O'Reilly Book Excerpts: Java Swing, 2nd Edition
Java Swing: Menus and Toolbars, Part 3by Robert Eckstein, Marc Loy, Dave Wood, James Elliott, Brian Cole
In part three in this book excerpt series on Swing menus and toolbars from Java Swing, 2nd Edition, learn about the
The JMenuItem Class
Before discussing menus, we should introduce the
JMenuItem class. Figure 14-6 shows the class diagram for the
JMenuItem class diagram
JMenuItem serves as a wrapper for strings and images to be used as elements in a menu. The
JMenuItem class is essentially a specialized button and extends the
AbstractButton class. Its behavior, however, is somewhat different from standalone buttons. When the mouse pointer is dragged over a menu item, Swing considers the menu item to be selected. If the user releases the mouse button while over the menu item, it is considered to be chosen and should perform its action.
There is an unfortunate conflict in terminology here. Swing considers a menu item selected when the mouse moves over it, as updated by the
MenuSelectionManager and classes that implement the
MenuElement interface. On the other hand, Swing considers a button selected when it remains in one of two persistent states, such as a checkbox button remaining in the checked state until clicked again. So when a menu item is selected, its button model is really armed. Conversely, when a menu item is deselected, its button model is disarmed. Finally, when the user releases the mouse button over the menu item, the button is considered to be clicked, and the
doClick( ) method is invoked.
Menu Item Shortcuts
Menu items can take both keyboard accelerators and (on some platforms) mnemonics. Mnemonics are an artifact of buttons; they appear as a single underline below the character that represents the shortcut. Keyboard accelerators, on the other hand, are inherited from
JComponent. With menu items, they have the unique side effect of appearing in the menu item. (Their exact appearance and location is up to the L&F.) Figure 14-7 shows both mnemonics and keyboard accelerators.
Figure 14-7. Mnemonics and keyboard accelerators
Keyboard accelerators and mnemonics perform the same function: users can abbreviate common GUI actions with keystrokes. However, a mnemonic can be activated only when the button (or menu item) it represents is visible on the screen. Menu item keyboard accelerators can be invoked any time the application has the focus-- whether the menu item is visible or not. Also, as noted, accelerators work on all platforms and all L&Fs while mnemonics are less universal. Menus may be assigned both at once.
Let's look at programming both cases. Keyboard accelerators typically use a variety of keystrokes: function keys, command keys, or an alphanumeric key in combination with one or more modifiers (e.g., Shift, Ctrl, or Alt). All of these key combinations can be represented by the
javax.swing.KeyStroke class, but only some of them are appropriate for the platform and L&F in use. Hence, you can assign a keyboard accelerator to a menu item by setting its
accelerator property with a
KeyStroke object configured using the default toolkit's
menuShortcutKeyMask property, as follows:
JMenuItem m = new JMenuItem("Copy"); m.setAccelerator(KeyStroke.getKeyStroke('C', Toolkit.getDefaultToolkit( ).getMenuShortcutKeyMask( ), false));
Under Metal, this sets the accelerator to Ctrl-C, which is the letter C typed in combination with the Ctrl key. The accelerator appears at the right side of the menu item (though again, the position is up to the L&F). The
KeyStroke class is covered in more detail in Chapter 27.
The second, less universal, way to set a shortcut is through the
mnemonic property of the
JMenuItem mi = new JMenuItem("Copy"); mi.setMnemonic('C');
mnemonic property underlines the character you pass into the
setMnemonic( ) method. Note that mnemonic characters cannot take modifiers; they are simple letters. Be sure to use a letter that exists in the menu item's label. Otherwise, nothing is underlined, and the user will not know how to activate the keyboard shortcut. Also be sure to set up mnemonics only if you're running on a platform and L&F that support them.
As of SDK 1.4, you can use the
displayedMnemonicIndex property to cope with menu items containing multiple copies of the character you're using as a mnemonic, if it makes more sense for a later instance to be underlined (for example, the common Save As menu item in which the uppercase "A" should get the underline). To achieve this, once you set up the mnemonic, call
setDisplayedMnemonicIndex with a value of
In Swing, menu items can contain (or consist entirely of) icons. This can be a visual aid if the icon can convey the intended meaning more clearly. You can pass an
Icon object to the constructor of the
JMenuItem class as follows:
JMenu menu = new JMenu("Justify"); // The first two menu items contain text and an image. The third // uses only the image. menu.add(new JMenuItem("Center", new ImageIcon("center.gif"))); menu.add(new JMenuItem("Right", new ImageIcon("right.gif"))); menu.add(new JMenuItem(new ImageIcon("left.gif")));
By default, the text is placed to the left of the image. This is shown on the left in Figure 14-8. As you can see, this often misaligns the images to the right of the text, especially if there is a menu item consisting only of an image. If the menu item images are all the same width, you can improve the appearance of your menus by altering the text's position using the
setHorizontalTextAlignment( ) method:
JMenu menu = new JMenu("Justify"); // The first two menu items contain text and an image. The third // uses only the image. The text is now set to the right. JMenuItem item1= new JMenuItem("Center", new ImageIcon("center.gif"))); item1.setHorizontalTextAlignment(SwingConstants.RIGHT); JMenuItem item2= new JMenuItem("Right", new ImageIcon("right.gif"))); item2.setHorizontalTextAlignment(SwingConstants.RIGHT); // Now add the menu items to the menu. menu.add(item1); menu.add(item2); menu.add(new JMenuItem(new ImageIcon("left.gif")));
Figure 14-8. Image and text placement in menu items
This positions the text on the other side of the images, as shown on the right of Figure 14-8. You can trace the
setHorizontalTextAlignment( ) method up the class hierarchy to the
AbstractButton class. As we mentioned before, the
JMenuItem class is a button object with respect to its text and image.
AbstractButton contains a
setVerticalTextAlignment( ) method as well, so if the accompanying image is taller than the menu item text, you can use this method to set the text's vertical position as well. (See the
AbstractButton class in Chapter 5 and the
OverlayLayout class in Chapter 11 for more information about alignment with menu items and buttons.) The image is placed to the left of the text if you construct a menu item from an
Action object (more on this later in the chapter).
Java supports image transparency, so if you require some parts of an image to be transparent, you can specify a "transparent" color in the GIF file (many paint programs allow you to do this), or you can create a specialized color filter that seeks out specific pixel colors and changes their opacity before passing the resulting
Image onto the menus. The former is much easier.
There are a number of ways to process events from menu items. Because menu items inherit
ActionEvent functionality from
AbstractButton, one approach is to assign an action command to each menu item (this is often done automatically with named components) and attach all of the menu items to the same
ActionListener. Then, in the
actionPerformed( ) method of the listener, use the event's
getActionCommand( ) method to obtain the action command of the menu item generating the event. This tells the listener which menu item has been clicked, allowing it to react accordingly. This is the approach used in IntroExample.java earlier in this chapter and PopupMenuExample.java, which is discussed later.
Alternatively, you can register a separate
ActionListener class with each menu item, which takes the guesswork out of determining the menu item selected. However, Swing allows you to go a step further. The most object-oriented approach is to create a specialized
Action class that corresponds to each of the tasks a user might request of your application. This lets you bundle the code for each program action together with the action's name, icon, keystrokes, and other attributes in one place. You can then use this
Action to create the menu item, which automatically sets the item's text, image, accelerator, and so on.
This technique is particularly powerful if you want to be able to invoke the same action in multiple ways (such as from a toolbar as well as a menu). You can use the same
Action instance to create the menu item and toolbar button, and they'll both have appropriate labels and appearances. If the application needs to disable the action because it's not currently appropriate, calling
setEnabled on the
Action instance automatically updates all user interface elements associated with the action (thus dimming both your menu item and toolbar button). Similarly, changing other attributes of the action, such as its name or icon, automatically updates any associated user-interface components.
Although prior to SDK 1.3 it wasn't possible to construct a
JMenuItem from an
Action directly, adding the
Action to a
JPopupMenu had the same effect: the menu would create and configure an appropriate
JMenuItem for you.