Lesson 12.6 Menus
Java Menus
So far, all of your applets and applications have used Buttons or TextFields to get input from your users. Is there any advantage to using pulldown menus instead?
Yes, there are two main reasons to use menus instead of Buttons.
First, a pull-down menu allows your users to explore the different options offered by your program. Because users can explore the menu system to see what different options are available, a menu system facilitates learning a program. Toolbar buttons make a program easier to use, once learned, but menus make a program easier to learn.
A second advantage of using a menu system is that it conserves your valuable screen real-estate. Consider, for instance, how much of the common IDE is taken up with toolbar buttons. (Idea shamelessly stolen from Mr. Bunny's Big Cup O' Java.)
Menu Classes
Just as the AWT Component class is the superclass for Buttons, TextFields, and other UI objects, the AWT MenuComponent class is the superclass for each of the different pieces that make up a Java menu system.
Let's take a quick look at those classes.
A menu bar is the horizontal strip that appears either at the top of a Frame, [below the Frame title], or, on some platforms, at the top of the screen. In Java, a menu bar is represented by an instance of the MenuBar class.
A MenuBar acts as a container that normally holds Menu objects. Menu objects represent the drop-down [or pull-down] menus that we're all familiar with.
The individual items on a Menu are composed of MenuItem objects in Java. As you can see from the figure, a Menu object is really a special kind of MenuItem--a MenuItem that can contain other MenuItems.
A second kind of MenuItem is the CheckboxMenuItem. A CheckboxMenuItem stores an on-off value, represented by a check-mark drawn next to the menu item when it is displayed.
A sixth menu class is the PopupMenu added in Java 1.1. The PopupMenu class is a subclass of the Menu class.
Creating a Simple Menu
Creating a menu system in Java is a simple four-step process:
- Create the menu bar.
- Create a menu.
- Add items to the menu.
- Hook the pieces together.
Let's take a quick look at each of these steps, and then we'll turn our attention to responding to menu events.
1. Creating the MenuBar
This is the easiest step, because the MenuBar class has only a single, no-argument constructor. Create a MenuBar object in your program like this:
| MenuBar mb = new MenuBar(); |
If your program has several different menu systems, each active during a different portion of your program, you could create a separate MenuBar object for each one.
2. Creating the Menu
For each of the drop-down menus on your MenuBar, create a Menu object. There are three different Menu constructors, but you'll usually just use this one:
| Menu fm = new Menu(“File”); |
There is also a default [no-argument] Menu constructor, and a Menu constructor that allows you to specify whether the Menu is a "tear-off" menu. Tear-off menus are only available under Motif [the Unix window manager], so most people don't use that constructor.
3. Creating the MenuItems
Once you have your Menu object, you need to add MenuItems. You can create MenuItems in two ways: either explicitly or implicitly.
Here's how you create some MenuItems explicitly and add them to your "File" Menu object [fm]:
MenuItem fmNew = new MenuItem("New");
MenuItem fmExit = new MenuItem("Exit");
fm.add(fmNew);
fm.add(fmExit); |
Here's how you create MenuItems implicitly:
fm.add("New");
fm.add("Exit"); |
4. Hooking It Up
There are two steps you need to follow before your menu actually works. You must:
- Attach each of your Menu objects to your MenuBar object.
- Attach your MenuBar object to your application.
To attach your Menu object, you use the MenuBar add() method, the same way you added MenuItems to your Menu. To add the "File" Menu, fm, to the MenuBar, mb, just write:
To attach your MenuBar to the Frame which holds it, you use the setMenuBar() method. To hook up the MenuBar mb, write:
An Example
Here's a short application that puts these four steps to work. You can download Menu1.java, or copy it from here. Menu1 extends the GuiApp class so you must place it in the same directory as MainFrame.class.
|
Menu1.java
|
| import java.awt.*;
public class Menu1 extends GuiApp
{
public Menu1(String title)
{
super(title);
// 1. Create the menu bar
MenuBar mb = new MenuBar();
// 2. Create the menus
Menu fm = new Menu("File");
// 3. Add items to the menus
fm.add("New");
fm.add("Exit");
// 4. Hook everything together
mb.add(fm);
setMenuBar(mb);
}
public static void main(String[] args)
{
Menu1 app = new Menu1("A Simple Menu");
app.show();
}
} |
Since Menu1 is an application, you'll need to run it using the Java interpreter like this:
On the Mac, you'll have to run it using JBindery.
Here's what the program looks like as it runs:
Menu Events
Selecting a MenuItem generates an ActionEvent just like pressing a Button does. Under Java 1.0, the action() method will be called, but under Java 1.1 you have to subscribe to events, just as you do for Buttons.
Java 1.1 menu events work just like button-clicks: you have to “subscribe” to the events that each component generates. As with Buttons, this is a three-step process:
- Add “implements ActionListener” to class header.
- Add an actionPerformed() method to your application.
- Use addActionListener() on each MenuItem as it’s created.
Here's the short example application, Menu2.java, that carries out these steps. The major changes from Menu1.java are marked.
|
Menu2.java
|
import java.awt.*;
import java.awt.event.*;
public class Menu2 extends MainFrame
implements ActionListener
{
MenuItem miNew = new MenuItem("New");
MenuItem miExit = new MenuItem("Exit");
public Menu2(String title)
{
super(title);
MenuBar mb = new MenuBar();
Menu fm = new Menu("File");
miNew.addActionListener(this);
fm.add(miNew);
miExit.addActionListener(this);
fm.add(miExit);
mb.add(fm);
setMenuBar(mb);
}
public void actionPerformed(ActionEvent ae)
{
Object chosen = ae.getSource();
if (chosen == miExit)
{
quitApp();
}
else if (chosen == miNew)
{
System.out.println("New pressed");
}
}
public static void main(String[] args)
{
Menu2 app =
new Menu2("Java 1.1 Menu Events");
app.show();
}
} |
Here's the Menu2 application running on my machine. Note the output on the console when the miNew MenuItem is selected:
Who, What, Where?
Menu2 uses fields and explicitly creates each MenuItem it uses. Inside the actionPerformed() method, getSource() is used to retrieve the Object that fired the event and that Object is then compared to each of the MenuItem fields.
This creates a small problem; you have to create explicit MenuItems and store them as fields. Furthermore, the requirement that you send the addActionListener() message to each MenuItem seems to eliminate the easier, implicit style of MenuItems as well.
Fortunately, Java has two non-obvious features that can make 1.1 style event handling quite easy:
- If you send the addActionListener() message to a Menu, then all of that Menu's MenuItems are automatically registered as well.
- In the actionPerformed() method, you can use the ActionEvent getActionCommand() method to retrieve the text of the MenuItem.
Here is an application, Menu2b.java, that uses these techniques to reduce the size of Menu2:
|
Menu2b.java
|
import java.awt.*;
import java.awt.event.*;
public class Menu2b extends MainFrame
implements ActionListener
{
public Menu2b(String title)
{
super(title);
MenuBar mb = new MenuBar();
Menu fm = new Menu("File");
fm.add("New");
fm.add("Exit");
fm.addActionListener(this);
mb.add(fm);
setMenuBar(mb);
}
public void actionPerformed(ActionEvent ae)
{
String cmd = ae.getActionCommand();
if (cmd.equals("Exit"))
{
quitApp();
}
else if (cmd.equals("New"))
{
System.out.println("New pressed");
}
}
public static void main(String[] args)
{
Menu2b app =
new Menu2b("Java 1.1 Implicit Events");
app.show();
}
} |
Advanced Menus
The menu classes are pretty straightforward; there are not a lot of advanced features. Before you leave, though, let's look at three things you might want to do:
- Add a menu "separator" between menu items.
- Create a set of "cascading" menus.
- Attach a "hot-key" or keyboard shortcut to a menu.
Keyboard shortcuts are only available in Java 1.1 and up, but since you can only use Java's Menu classes inside GUI applications, there's never any reason to use Java 1.0 events.
Separators, Hot-keys & Cascading Menus
Each of these three items is simple to implement. Let's start with menu separators.
Separators
Assume that you have the the following Menu already created:
Menu fm = new Menu("File");
fm.add("New"); |
You can add a separator after "New" using any one of these methods:
fm.addSeparator(); // At end
fm.add("-"); // Hyphen
fm.insertSeparator(1); // New = 0 |
The first method shown here, addSeparator(), adds the separator at the end of the menu, after the last item. The second method does the same thing, but uses the "magic" label, "-" [the hyphen].
The last method, insertSeparator(), treats the MenuItems on the menu as a zero-based array. If the menu already contains several additional items, then the separator is placed on the second line, and the remaining items are moved down.
Cascading Menus
A cascading menu is simply a regular Menu object that has been added to another Menu object as a MenuItem.
Assume you have these two menus already created:
Menu fm = new Menu("File");
fm.add("New");
fm.add("-");
Menu cm = new Menu("Color");
cm.add("Red");
cm.add("Blue"); |
You can then add the "Color" menu to the "File" menu like this:
fm.add(cm);
fm.addActionListener(this);
cm.addActionListener(this); |
Menus added in this manner will automatically cascade, however. You will need to register each Menu object as an ActionListener separately, as shown in the code fragment.
Hot-Keys
In early versions of Java you could automatically create a hot-key by adding an ampersand [&] in font of the appropriate letter in the MenuItem caption, like you can with Visual Basic. Unfortunately, this "feature" only worked on Windows machines, and was quickly removed.
To replace it the MenuShortcut class was added. Adding a hotkey to a MenuItem is a two-step process:
- Create a MenuShortcut object using the constants defined in the java.awt.event.KeyEvent class.
- Bind the MenuShortcut to a MenuItem.
The MenuShortcut class has two constructors. The simplest just requires that you supply a virtual keycode from the KeyEvent class like this:
MenuShortcut skR =
new MenuShortcut(KeyEvent.VK_R); |
The MenuShortcut skR now refers to the key combination Ctrl+R.
The second MenuShortcut constructor allows you to specify whether the Shift key should be used along with the Control key when selecting the item, like this:
MenuShortcut skG =
new MenuShortcut(KeyEvent.VK_G, true); |
To activate the MenuShortcut skG the user would have to press Ctrl+Shift+G. [Hold down Control and Shift, then press G].
To bind the MenuShortcut to the MenuItem, you can use the two argument MenuItem constructor like this:
| MenuItem mi = new MenuItem("Red", skR); |
You can also send the setShortcut() message to an existing MenuItem like this:
mi = new MenuItem("Green");
mi.setShortcut(skG); |
There is one further complicating factor.
Once you bind a MenuShortcut to a MenuItem, that item returns null when sent the getActionCommand() message. To fix this, you also have to explicitly set the menu item's action command using the setActionCommand() method, like this:
mi = new MenuItem("Green");
mi.setActionCommand("Green");
mi.setShortcut(skG); |
An Example
Here's a short example application that puts all three of these features to work. Download Menu3.java, and browse through it, or try it out at home. On my machine, it looks like this as it runs.
Something to Talk About
You want to try working with menus?
- Download and run Menu3.java.
- Add a third item to the Color menu and give it an appropriate hot key. You can choose a color of your choice.
You might want to talk about the following in the discussion area:
- The lines you added to create the new MenuItem and its hotkey.
- The lines changed in actionPerformed() to carry out the action.
This is the last section of this lesson.
|