|
|
|
|
Learning JavaBy Pat Niemeyer & Jonathan KnudsenMay 2000 1-56592-718-4, Order Number: 7184 720 pages, $34.95, Includes CD-ROM |
Chapter 14
Using Swing ComponentsIn the previous chapter, we discussed a number of concepts, including how Java's user interface facility is put together and how the larger pieces work. You should understand what components and containers are, how you use them to create a display, what events are, how components use them to communicate with the rest of your application, and what layout managers are.
Now that we're through with the general concepts and background, we'll get to the fun stuff: how to do things with Swing. We will cover most of the components that the Swing package supplies, how to use these components in applets and applications, and how to build your own components. We will have lots of code and lots of pretty examples to look at.
There's more material than fits in a single chapter. In this chapter, we'll cover all the basic user interface components. In the next chapter, we'll cover some of the more involved topics: text components, trees, tables, and creating your own components.
Buttons and Labels
We'll start with the simplest components: buttons and labels. Frankly, there isn't much to say about them. If you've seen one button, you've seen them all; and you've already seen buttons in the applications in Chapter 2, A First Application (
HelloJava3andHelloJava4). A button generates anActionEventwhen the user presses it. To receive these events, your program registers anActionListener, which must implement theactionPerformed( )method. The argument passed toactionPerformed( )is the event itself.There's one more thing worth saying about buttons, which applies to any component that generates an action event. Java lets us specify an "action command" string for buttons (and other components, like menu items, that can generate action events). The action command is less interesting than it sounds. It is just a
Stringthat serves to identify the component that sent the event. By default, the action command of aJButtonis the same as its label; it is included in action events, so you can use it to figure out which button an event came from.To get the action command from an action event, call the event's
getActionCommand( )method. The following code checks whether the user pressed the Yes button:public void actionPerformed(ActionEvent e){if (e.getActionCommand( ).equals("Yes") {//the user pressed "Yes"; do something...}}You can change the action command by calling the button's
setActionCommand( )method. The following code changes buttonmyButton's action command to "confirm":myButton.setActionCommand("confirm");It's a good idea to get used to setting action commands explicitly; this helps to prevent your code from breaking when you or some other developer "internationalizes" it, or otherwise changes the button's label. If you rely on the button's label, your code will stop working as soon as that label changes; a French user might see the label
Ouirather thanYes. By setting the action command, you eliminate one source of bugs; for example, the buttonmyButtonin the previous example will always generate the action commandconfirm, regardless of what its label says.Swing buttons can have an image in addition to a label. The
JButtonclass includes constructors that accept anIconobject, which knows how to draw itself. You can create buttons with captions, images, or both. A handy class calledImageIcontakes care of loading an image for you and can be used to easily add an image to a button. The following example shows how this works://file: PictureButton.javaimport java.awt.*;import java.awt.event.*;import javax.swing.*;public class PictureButton extends JFrame {public PictureButton( ) {super("PictureButton v1.0");setSize(200, 200);setLocation(200, 200);Icon icon = new ImageIcon("rhino.gif");JButton button = new JButton(icon);button.addActionListener(new ActionListener( ) {public void actionPerformed(ActionEvent ae) {System.out.println("Urp!");}});Container content = getContentPane( );content.setLayout(new FlowLayout( ));content.add(button);}public static void main(String[] args) {JFrame f = new PictureButton( );f.addWindowListener(new WindowAdapter( ) {public void windowClosing(WindowEvent we) { System.exit(0); }});f.setVisible(true);}}The example creates an
ImageIconfrom the rhino.gif file. Then aJButtonis created from theImageIcon. The whole thing is displayed in aJFrame. This example also shows the idiom of using an anonymous inner class as anActionListener.There's even less to be said about
JLabelcomponents. They're just text strings or images housed in a component. There aren't any special events associated with labels; about all you can do is specify the text's alignment, which controls the position of the text within the label's display area. As with buttons,JLabels can be created withIcons if you want to create a picture label. The following code creates some labels with different options:// default alignment (CENTER)JLabel label1 = new JLabel("Lions");// left alignedJLabel label2 = new JLabel("Tigers", SwingConstants.LEFT);//label with no text, default alignmentJLabel label3 = new JLabel( );// create image iconIcon icon = new ImageIcon("rhino.gif");// create image labelJLabel label4 = new JLabel(icon);// assigning text to label3label3.setText("and Bears");// set alignmentlabel3.setHorizontalAlignment(SwingConstants.RIGHT);The alignment constants are defined in the
SwingConstantsinterface.Now we've built several labels, using a variety of constructors and several of the class's methods. To display the labels, just add them to a container by calling the container's
add( )method.The other characteristics you might like to set on labels, such as changing their font or color, are accomplished using the methods of the
Componentclass,JLabel's distant ancestor. For example, you can callsetFont( )andsetColor( )on a label, as with any other component.Given that labels are so simple, why do we need them at all? Why not just draw a text string directly on the container object? Remember that a
JLabelis aJComponent. That's important; it means that labels have the normal complement of methods for setting fonts and colors that we mentioned earlier, as well as the ability to be managed sensibly by a layout manager. Therefore, they're much more flexible than a text string drawn at an absolute location within a container.Speaking of layouts--if you use the
setText( )method to change the text of your label, the label's preferred size may change. But the label's container will automatically lay out its components when this happens, so you don't have to worry about it.Swing can interpret HTML-formatted text in
JLabelandJButtonlabels. The following example shows how to create a button with HTML-formatted text:JButton button = new JButton("<html>"+ "S<font size=-1>MALL<font size=+0> "+ "C<font size=-1>APITALS");Checkboxes and Radio Buttons
A checkbox is a labeled toggle switch. Each time the user clicks it, its state toggles between checked and unchecked. Swing implements the checkbox as a special kind of button. Radio buttons are similar to checkboxes, but they are usually arranged in groups. Click on one radio button in the group, and the others automatically turn off. They are named for the preset buttons on old car radios.
Checkboxes and radio buttons are represented by instances of
JCheckBoxandJRadioButton, respectively. Radio buttons can be tethered together using an instance of another class calledButtonGroup. By now you're probably well into the swing of things (no pun intended) and could easily master these classes on your own. We'll use an example to illustrate a different way of dealing with the state of components and to show off a few more things about containers.A
JCheckBoxsendsItemEvents when it's pushed. Since a checkbox is a kind of button, it also firesActionEvents when it becomes checked. For something like a checkbox, we might want to be lazy and check on the state of the buttons only at some later time, such as when the user commits an action. It's like filling out a form; you can change your choices until you submit the form.The following application,
DriveThrough, lets us check off selections on a fast food menu, as shown in Figure 14-1.DriveThroughprints the results when we press the Place Order button. Therefore, we can ignore all the events generated by our checkboxes and radio buttons and listen only for the action events generated by the regular button.//file: DriveThrough.javaimport java.awt.*;import java.awt.event.*;import javax.swing.*;public class DriveThrough {public static void main(String[] args) {JFrame f = new JFrame("Lister v1.0");f.setSize(300, 150);f.setLocation(200, 200);f.addWindowListener(new WindowAdapter( ) {public void windowClosing(WindowEvent we) { System.exit(0); }});JPanel entreePanel = new JPanel( );final ButtonGroup entreeGroup = new ButtonGroup( );JRadioButton radioButton;entreePanel.add(radioButton = new JRadioButton("Beef"));radioButton.setActionCommand("Beef");entreeGroup.add(radioButton);entreePanel.add(radioButton = new JRadioButton("Chicken"));radioButton.setActionCommand("Chicken");entreeGroup.add(radioButton);entreePanel.add(radioButton = new JRadioButton("Veggie", true));radioButton.setActionCommand("Veggie");entreeGroup.add(radioButton);final JPanel condimentsPanel = new JPanel( );condimentsPanel.add(new JCheckBox("Ketchup"));condimentsPanel.add(new JCheckBox("Mustard"));condimentsPanel.add(new JCheckBox("Pickles"));JPanel orderPanel = new JPanel( );JButton orderButton = new JButton("Place Order");orderPanel.add(orderButton);Container content = f.getContentPane( );content.setLayout(new GridLayout(3, 1));content.add(entreePanel);content.add(condimentsPanel);content.add(orderPanel);orderButton.addActionListener(new ActionListener( ) {public void actionPerformed(ActionEvent ae) {String entree =entreeGroup.getSelection().getActionCommand( );System.out.println(entree + " sandwich");Component[] components = condimentsPanel.getComponents( );for (int i = 0; i < components.length; i++) {JCheckBox cb = (JCheckBox)components[i];if (cb.isSelected( ))System.out.println("With " + cb.getText( ));}}});f.setVisible(true);}}
Figure 14-1. The DriveThrough application
![]()
DriveThroughlays out three panels. The radio buttons in theentreePanelare tied together through aButtonGroupobject. Weadd( )the buttons to aButtonGroupto make them mutually exclusive. TheButtonGroupobject is an odd animal. One expects it to be a container or a component, but it isn't; it's simply a helper object that allows only oneRadioButtonto be selected at a time.In this example, the button group forces you to choose a beef, chicken, or veggie entree, but not more than one. The condiment choices, which are
JCheckBoxes, aren't in a button group, so you can request any combination of ketchup, mustard, and pickles on your sandwich.When the Place Order button is pushed, we receive an
ActionEventin theactionPerformed( )method of our innerActionListener. At this point, we gather the information in the radio buttons and checkboxes and print it.actionPerformed( )simply reads the state of the various buttons. We could have saved references to the buttons in a number of ways; this example demonstrates two. First, we find out which entree was selected. To do so, we call theButtonGroup'sgetSelection( )method. This returns aButtonModel, upon which we immediately callgetActionCommand( ). This returns the action command as we set it when we created the radio buttons. The action commands for the buttons are the entrée names, which is exactly what we need.To find out which condiments were selected, we use a more complicated procedure. The problem is that condiments aren't mutually exclusive, so we don't have the convenience of a
ButtonGroup. Instead, we ask the condimentsJPanelfor a list of its components. ThegetComponents( )method returns an array of references to the container's child components. We'll use this to loop over the components and print the results. We cast each element of the array back toJCheckBoxand call itsisSelected( )method to see if the checkbox is on or off. If we were dealing with different types of components in the array, we could determine each component's type with theinstanceofoperator.Lists and Combo Boxes
JLists andJComboBoxes are a step up on the evolutionary chain fromJButtons andJLabels. Lists let the user choose from a group of alternatives. They can be configured to force the user to choose a single selection or to allow multiple choices. Usually, only a small group of choices are displayed at a time; a scrollbar lets the user move to the choices that aren't visible. The user can select an item by clicking on it. He or she can expand the selection to a range of items by holding down Shift and clicking on another item. To make discontinuous selections, the user can hold down the Control key instead of the Shift key.A combo box is a cross-breed between a text field and a list. It displays a single line of text (possibly with an image) and a downward pointing arrow at one side. If you click on the arrow, the combo box opens up and displays a list of choices. You can select a single choice by clicking on it. After a selection is made, the combo box closes up; the list disappears and the new selection is shown in the text field.
Like every other component in Swing, lists and combo boxes have data models that are distinct from visual components. The list also has a selection model that controls how selections may be made on the list data.
Lists and combo boxes are similar because they have similar data models. Each is simply an array of acceptable choices. This similarity is reflected in Swing, of course: the type of a
JComboBox's data model is a subclass of the type used for aJList's data model. The next example demonstrates this relationship.The following example creates a window with a combo box, a list, and a button. The combo box and the list use the same data model. When you press the button, the program writes out the current set of selected items in the list. Figure 14-2 shows the example; the code itself follows.
Figure 14-2. A combo box and a list using the same data model
![]()
/file: Lister.javaimport java.awt.*;import java.awt.event.*;import javax.swing.*;public class Lister {public static void main(String[] args) {JFrame f = new JFrame("Lister v1.0");f.setSize(200, 200);f.setLocation(200, 200);f.addWindowListener(new WindowAdapter( ) {public void windowClosing(WindowEvent we) { System.exit(0); }});// create a combo boxString [] items = { "uno", "due", "tre", "quattro", "cinque","sei", "sette", "otto", "nove", "deici","undici", "dodici" };JComboBox comboBox = new JComboBox(items);comboBox.setEditable(true);// create a list with the same data modelfinal JList list = new JList(comboBox.getModel( ));// create a button; when it's pressed, print out// the selection in the listJButton button = new JButton("Per favore");button.addActionListener(new ActionListener( ) {public void actionPerformed(ActionEvent ae) {Object[] selection = list.getSelectedValues( );System.out.println("-----");for (int i = 0; i < selection.length; i++)System.out.println(selection[i]);}});// put the controls the content paneContainer c = f.getContentPane( );JPanel comboPanel = new JPanel( );comboPanel.add(comboBox);c.add(comboPanel, BorderLayout.NORTH);c.add(new JScrollPane(list), BorderLayout.CENTER);c.add(button, BorderLayout.SOUTH);f.setVisible(true);}}The combo box is created from an array of strings. This is a convenience--behind the scenes, the
JComboBoxconstructor creates a data model from the strings you supply and sets theJComboBoxto use that data model. The list is created using the data model of the combo box. This works becauseJListexpects to use aListModelfor its data model, and theComboBoxModelused by theJComboBoxis a subclass ofListModel.The button's action event handler simply prints out the selected items in the list, which are retrieved with a call to
getSelectedValues( ). This method actually returns an object array, not a string array. List and combo box items, like many other things in Swing, are not limited to text. You can use images, or drawings, or some combination of text and images.You might expect that selecting one item in the combo box would select the same item in the list. In Swing components, selection is controlled by a selection model. The combo box and the list have distinct selection models; after all, you can select only one item from the combo box, while it's possible to select multiple items from the list. Thus, while the two components share a data model, they have separate selection models.
We've made the combo box editable. By default, it would not be editable: the user could choose only one of the items in the drop-down list. With an editable combo box, the user can type in a selection, as if it were a text field. Non-editable combo boxes are useful if you just want to offer a limited set of choices; editable combo boxes are handy when you want to accept any input but offer some common choices.
There's a great class tucked away in the last example that deserves some recognition. It's
JScrollPane. InLister, you'll notice we created one when we added theListto the main window.
JScrollPanesimply wraps itself around anotherComponentand provides scrollbars as necessary. The scrollbars show up if the containedComponent's preferred size (as returned bygetPreferredSize( )) is greater than the size of theJScrollPaneitself. In the previous example, the scrollbars show up whenever the size of theListexceeds the available space.You can use
JScrollPaneto wrap anyComponent, including components with drawings or images or complex user interface panels. We'll discussJScrollPanein more detail later in this chapter, and we'll use it frequently with the text components in the next chapter.Borders
Any Swing component can have a decorative border.
JComponentincludes a method calledsetBorder( ); all you have to do is callsetBorder( ), passing it an appropriate implementation of theBorderinterface.Swing provides many useful
Borderimplementations in thejavax.swing.borderpackage. You could create an instance of one of these classes and pass it to a component'ssetBorder( )method, but there's an even simpler technique.The
BorderFactoryclass can create any kind of border for you using static "factory" methods. Creating and setting a component's border, then, is simple:JLabel labelTwo = new JLabel("I have an etched border.");labelTwo.setBorder(BorderFactory.createEtchedBorder( ));Every component has a
setBorder( )method, from simple labels and buttons right up to the fancy text and table components we'll cover in the next chapter.
BorderFactoryis convenient, but it does not offer every option of every border type. For example, if you want to create a raisedEtchedBorderinstead of the default lowered border, you'll need to useEtchedBorder's constructor rather than a method inBorderFactory, like this:JLabel labelTwo = new JLabel("I have a raised etched border.");labelTwo.setBorder( new EtchedBorder(EtchedBorder.RAISED) );The
Borderimplementation classes are listed and briefly described here:
BevelBorder- This border draws raised or lowered beveled edges, giving an illusion of depth.
SoftBevelBorder- This border is similar to
BevelBorder, but thinner.
EmptyBorder- Doesn't do any drawing, but does take up space. You can use it to give a component a little breathing room in a crowded user interface.
EtchedBorder- A lowered etched border gives the appearance of a rectangle that has been chiseled into a piece of stone. A raised etched border looks like it is standing out from the surface of the screen.
LineBorder- Draws a simple rectangle around a component. You can specify the color and width of the line in
LineBorder's constructor.
MatteBorder- A souped-up version of
LineBorder. You can create aMatteBorderwith a certain color and specify the size of the border on the left, top, right, and bottom of the component.MatteBorderalso allows you to pass in anIconthat will be used to draw the border. This could be an image (ImageIcon) or any other implementation of theIconinterface.
TitledBorder- A regular border with a title.
TitledBorderdoesn't actually draw a border; it just draws a title in conjunction with another border object. You can specify the locations of the title, its justification, and its font. This border type is particularly useful for grouping different sets of controls in a complicated interface.
CompoundBorder- A border that contains two other borders. This is especially handy if you want to enclose a component in an
EmptyBorderand then put something decorative around it, like anEtchedBorderor aMatteBorder.
The following example shows off some different border types. It's only a sampler, though; many more border types are available. Furthermore, the example only encloses labels with borders. You can put a border around any component in Swing. The example is shown in Figure 14-3; the source code follows.
Figure 14-3. A bevy of borders
![]()
//file: Borders.javaimport java.awt.*;import java.awt.event.*;import javax.swing.*;import javax.swing.border.*;public class Borders {public static void main(String[] args) {// create a JFrame to hold everythingJFrame f = new JFrame("Borders");f.addWindowListener(new WindowAdapter( ) {public void windowClosing(WindowEvent we) { System.exit(0); }});f.setSize(300, 300);f.setLocation(200, 200);// Create labels with borders.int center = SwingConstants.CENTER;JLabel labelOne = new JLabel("raised BevelBorder", center);labelOne.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));JLabel labelTwo = new JLabel("EtchedBorder", center);labelTwo.setBorder(BorderFactory.createEtchedBorder( ));JLabel labelThree = new JLabel("MatteBorder", center);labelThree.setBorder(BorderFactory.createMatteBorder(10, 10, 10, 10, Color.pink));JLabel labelFour = new JLabel("TitledBorder", center);Border etch = BorderFactory.createEtchedBorder( );labelFour.setBorder(BorderFactory.createTitledBorder(etch, "Title"));JLabel labelFive = new JLabel("TitledBorder", center);Border low = BorderFactory.createLoweredBevelBorder( );labelFive.setBorder(BorderFactory.createTitledBorder(low, "Title",TitledBorder.RIGHT, TitledBorder.BOTTOM));JLabel labelSix = new JLabel("CompoundBorder", center);Border one = BorderFactory.createEtchedBorder( );Border two =BorderFactory.createMatteBorder(4, 4, 4, 4, Color.blue);labelSix.setBorder(BorderFactory.createCompoundBorder(one, two));// add components to the content paneContainer c = f.getContentPane( );c.setLayout(new GridLayout(3, 2));c.add(labelOne);c.add(labelTwo);c.add(labelThree);c.add(labelFour);c.add(labelFive);c.add(labelSix);f.setVisible(true);}}Menus
A
JMenuis a standard pull-down menu with a fixed name. Menus can hold other menus as submenu items, enabling you to implement complex menu structures. In Swing, menus are first-class components, just like everything else. You can place them wherever a component would go. Another class,JMenuBar, holds menus in a horizontal bar. Menu bars are real components, too, so you can place them wherever you want in a container: top, bottom, or middle. But in the middle of a container, it usually makes more sense to use aJComboBoxrather than some kind of menu.Menu items may have associated images and shortcut keys; there are even menu items that look like checkboxes and radio buttons. Menu items are really a kind of button. Like buttons, menu items fire action events when they are selected. You can respond to menu items by registering action listeners with them.
There are two ways to use the keyboard with menus. The first is called mnemonics. A mnemonic is one character in the menu name. If you hold down the Alt key and type a menu's mnemonic, the menu will drop down, just as if you had clicked on it with the mouse. Menu items may also have mnemonics. Once a menu is dropped down, you can select individual items in the same way.
Menu items may also have accelerators. An accelerator is a key combination that selects the menu item, whether or not the menu that contains it is showing. A common example is the accelerator Ctrl-C, which is frequently used as a shortcut for the Copy item in the Edit menu.
The following example demonstrates several different features of menus. It creates a menu bar with three different menus. The first, Utensils, contains several menu items, a submenu, a separator, and a Quit item that includes both a mnemonic and an accelerator. The second menu, Spices, contains menu items that look and act like checkboxes. Finally, the Cheese menu demonstrates how radio button menu items can be used.
This application is shown in Figure 14-4 with one of its menus dropped down. Choosing Quit from the menu (or pressing Ctrl-Q) removes the window. Give it a try.
//file: DinnerMenu.javaimport java.awt.*;import java.awt.event.*;import javax.swing.*;public class DinnerMenu extends JFrame {public DinnerMenu( ) {super("DinnerMenu v1.0");setSize(200, 200);setLocation(200, 200);// create the Utensils menuJMenu utensils = new JMenu("Utensils");utensils.setMnemonic(KeyEvent.VK_U);utensils.add(new JMenuItem("Fork"));utensils.add(new JMenuItem("Knife"));utensils.add(new JMenuItem("Spoon"));JMenu hybrid = new JMenu("Hybrid");hybrid.add(new JMenuItem("Spork"));hybrid.add(new JMenuItem("Spife"));hybrid.add(new JMenuItem("Knork"));utensils.add(hybrid);utensils.addSeparator( );// do some fancy stuff with the Quit itemJMenuItem quitItem = new JMenuItem("Quit");quitItem.setMnemonic(KeyEvent.VK_Q);quitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, Event.CTRL_MASK));quitItem.addActionListener(new ActionListener( ) {public void actionPerformed(ActionEvent e) { System.exit(0); }});utensils.add(quitItem);// create the Spices menuJMenu spices = new JMenu("Spices");spices.setMnemonic(KeyEvent.VK_S);spices.add(new JCheckBoxMenuItem("Thyme"));spices.add(new JCheckBoxMenuItem("Rosemary"));spices.add(new JCheckBoxMenuItem("Oregano", true));spices.add(new JCheckBoxMenuItem("Fennel"));// create the Cheese menuJMenu cheese = new JMenu("Cheese");cheese.setMnemonic(KeyEvent.VK_C);ButtonGroup group = new ButtonGroup( );JRadioButtonMenuItem rbmi;rbmi = new JRadioButtonMenuItem("Regular", true);group.add(rbmi);cheese.add(rbmi);rbmi = new JRadioButtonMenuItem("Extra");group.add(rbmi);cheese.add(rbmi);rbmi = new JRadioButtonMenuItem("Blue");group.add(rbmi);cheese.add(rbmi);// create a menu bar and use it in this JFrameJMenuBar menuBar = new JMenuBar( );menuBar.add(utensils);menuBar.add(spices);menuBar.add(cheese);setJMenuBar(menuBar);}public static void main(String[] args) {JFrame f = new DinnerMenu( );f.addWindowListener(new WindowAdapter( ) {public void windowClosing(WindowEvent we) { System.exit(0); }});f.setVisible(true);}}Figure 14-4. The DinnerMenu application
![]()
Yes, we know. Quit doesn't belong in the Utensils menu. If it's driving you crazy, you can go back and add a File menu as an exercise when we're through.
Creating menus is pretty simple work. You create a
JMenuobject, specifying the menu's title.[1] Then you just addJMenuItems to theJMenu. You can also addJMenus to aJMenu; they show up as submenus. This is shown in the creation of the Utensils menu:JMenu utensils = new JMenu("Utensils");utensils.setMnemonic(KeyEvent.VK_U);utensils.add(new JMenuItem("Fork"));utensils.add(new JMenuItem("Knife"));utensils.add(new JMenuItem("Spoon"));JMenu hybrid = new JMenu("Hybrid");hybrid.add(new JMenuItem("Spork"));hybrid.add(new JMenuItem("Spife"));hybrid.add(new JMenuItem("Knork"));utensils.add(hybrid);In the second line, we set the mnemonic for this menu using a constant defined in the
KeyEventclass.You can add those pretty separator lines with a single call:
utensils.addSeparator( );The Quit menu item has some bells and whistles we should explain. First, we create the menu item and set its mnemonic, just as we did before for the Utensils menu:
JMenuItem quitItem = new JMenuItem("Quit");quitItem.setMnemonic(KeyEvent.VK_Q);Now we want to create an accelerator for the menu item. We do this with the help of a class called
KeyStroke:quitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, Event.CTRL_MASK));Finally, to actually do something in response to the menu item, we register an action listener:
quitItem.addActionListener(new ActionListener( ) {public void actionPerformed(ActionEvent e) { System.exit(0); }});Our action listener exits the application when the Quit item is selected.
Creating the Spices menu is just as easy, except that we use
JCheckBoxMenuItems instead of regularJMenuItems. The result is a menu full of items that behave like checkboxes.The next menu, Cheese, is a little more tricky. We want the items to be radio buttons, but we need to place them in a
ButtonGroupto ensure they are mutually exclusive. Each item, then, is created, added to the button group, and added to the menu itself.The final step is to place the menus we've just created in a
JMenuBar. This is simply a component that lays out menus in a horizontal bar. We have two options for adding it to ourJFrame. Since theJMenuBaris a real component, we could add it to the content pane of theJFrame. Instead, we use a convenience method calledsetJMenuBar( ), which automatically places theJMenuBarat the top of the frame's content pane. This saves us the trouble of altering the layout or size of the content pane; it is adjusted to coexist peacefully with the menu bar.The PopupMenu Class
One of Swing's nifty components is
JPopupMenu, a menu that automatically appears when you press the appropriate mouse button inside of a component. (On a Windows system, for example, clicking the right mouse button invokes a popup menu.) Which button you press depends on the platform you're using; fortunately, you don't have to care--Swing figures it out for you.The care and feeding of
JPopupMenuis basically the same as any other menu. You use a different constructor (JPopupMenu( )) to create it, but otherwise, you build a menu and add elements to it the same way. The big difference is you don't need to attach it to aJMenuBar. Instead, just pop up the menu whenever you need it.The following example,
PopupColorMenu,contains three buttons. You can use aJPopupMenuto set the color of each button or the frame itself, depending on where you press the mouse. Figure 14-5 shows the example in action; the user is preparing to change the color of the bottom button.
Figure 14-5. The PopupColorMenu application
![]()
//file: PopUpColorMenu.javaimport java.awt.*;import java.awt.event.*;import javax.swing.*;public class PopUpColorMenu extends JFrameimplements ActionListener {JPopupMenu colorMenu;Component selectedComponent;public PopUpColorMenu( ) {super("PopUpColorMenu v1.0");setSize(100, 200);setLocation(200, 200);addWindowListener(new WindowAdapter( ) {public void windowClosing(WindowEvent e) { System.exit(0); }});MouseListener mouseListener = new MouseAdapter( ) {public void mousePressed(MouseEvent e) { checkPopup(e); }public void mouseClicked(MouseEvent e) { checkPopup(e); }public void mouseReleased(MouseEvent e) { checkPopup(e); }private void checkPopup(MouseEvent e) {if (e.isPopupTrigger( )) {selectedComponent = e.getComponent( );colorMenu.show(e.getComponent(), e.getX(), e.getY( ));}}};final Container content = getContentPane( );content.setLayout(new FlowLayout( ));JButton button = new JButton("Uno");button.addMouseListener(mouseListener);content.add(button);button = new JButton("Due");button.addMouseListener(mouseListener);content.add(button);button = new JButton("Tre");button.addMouseListener(mouseListener);content.add(button);colorMenu = new JPopupMenu("Color");colorMenu.add(makeMenuItem("Red"));colorMenu.add(makeMenuItem("Green"));colorMenu.add(makeMenuItem("Blue"));getContentPane( ).addMouseListener(mouseListener);setVisible(true);}public void actionPerformed(ActionEvent e) {String color = e.getActionCommand( );if (color.equals("Red"))selectedComponent.setBackground(Color.red);else if (color.equals("Green"))selectedComponent.setBackground(Color.green);else if (color.equals("Blue"))selectedComponent.setBackground(Color.blue);}private JMenuItem makeMenuItem(String label) {JMenuItem item = new JMenuItem(label);item.addActionListener( this );return item;}public static void main(String[] args) {new PopUpColorMenu( );}}Because the popup menu is triggered by mouse events, we need to register a
MouseListenerfor any of the components to which it applies. In this example, all three buttons and the content pane of the frame are eligible for the color popup menu. Therefore, we add a mouse event listener for all of these components explicitly. The same instance of an anonymous innerMouseAdaptersubclass is used in each case. In this class, we override themousePressed( ),mouse-Released( ), andmouseClicked( )methods to display the popup menu when we get an appropriate event. How do we know what an "appropriate event" is? Fortunately, we don't need to worry about the specifics of our user's platform; we just need to call the event'sisPopupTrigger( )method. If this method returnstrue, we know the user has done whatever normally displays a popup menu on his or her system.Once we know that the user wants to raise a popup menu, we display the popup menu by calling its
show( )method with the mouse event coordinates as arguments.If we wanted to provide different menus for different types of components or the background, we'd create different mouse listeners for each different kind of component. The mouse listeners would invoke different kinds of popup menus as appropriate.
The only thing left is to handle the action events from the popup menu items. We use a helper method called
makeMenuItem( )to register thePopUpColorMenuwindow as an action listener for every item we add. The example implementsActionListenerand has the requiredactionPerformed( )method. This method reads the action command from the event, which is equal to the selected menu item's label by default. It then sets the backgroundcolor of the selected component appropriately.The JScrollPane Class
We used
JScrollPaneearlier in this chapter without explaining much about it. In this section we'll remedy the situation.A
JScrollPaneis a container that can hold one component. Said another way, aJScrollPanewraps another component. By default, if the wrapped component is larger than theJScrollPaneitself, theJScrollPanesupplies scrollbars.JScrollPanehandles the events from the scrollbars and displays the appropriate portion of the contained component.Technically,
JScrollPaneis aContainer, but it's a funny one. It has its own layout manager, which can't be changed. It can accommodate only one component at a time. This seems like a big limitation, but it isn't. If you want to put a lot of stuff in aJScrollPane, just put your components into aJPanel, with whatever layout manager you like, and put that panel into theJScrollPane.When you create a
JScrollPane, you can specify the conditions under which its scrollbars will be displayed. This is called the scrollbar display policy; a separate policy is used for the horizontal and vertical scrollbars. The following constants can be used to specify the policy for each of the scrollbars:
HORIZONTAL_SCROLLBAR_AS_NEEDED- Displays a scrollbar only if the wrapped component doesn't fit.
HORIZONTAL_SCROLLBAR_ALWAYS- Always shows a scrollbar, regardless of the contained component's size.
HORIZONTAL_SCROLLBAR_NEVER- Never shows a scrollbar, even if the contained component won't fit. If you use this policy, you should provide some other way to manipulate the
JScrollPane.
VERTICAL_SCROLLBAR_AS_NEEDED- Displays a scrollbar only if the wrapped component doesn't fit.
VERTICAL_SCROLLBAR_ALWAYS- Always shows a scrollbar, regardless of the contained component's size.
VERTICAL_SCROLLBAR_NEVER- Never shows a scrollbar, even if the contained component won't fit. If you use this policy, you should provide some other way to manipulate the
JScrollPane.
By default, the policies are
HORIZONTAL_SCROLLBAR_AS_NEEDEDandVERTICAL_SCROLLBAR_AS_NEEDED.Here's an example that uses a
JScrollPaneto display a large image. The application itself is very simple; all we do is place the image in anImageComponent, wrap aJScrollPanearound it, and put theJScrollPanein aJFrame's content pane. Here's the code://file: ScrollPaneFrame.javaimport java.awt.*;import java.awt.event.*;import javax.swing.*;public class ScrollPaneFrame {public static void main(String[] args) {String filename = "Piazza di Spagna.jpg";if (args.length > 0)filename = args[0];JFrame f = new JFrame("ScrollPaneFrame v1.0");f.setSize(300, 300);f.setLocation(200, 200);f.addWindowListener(new WindowAdapter( ) {public void windowClosing(WindowEvent e) { System.exit(0); }});Image image = Toolkit.getDefaultToolkit( ).getImage(filename);f.getContentPane( ).add(new JScrollPane(new ImageComponent(image)));f.setVisible(true);}}And here's the
ImageComponent. It waits for the image to load, using aMediaTracker, and sets its size to the size of the image. It also provides apaint( )method to draw the image. This takes a single call todrawImage( ). The first argument is the image itself; the next two are the coordinates of the image relative to theImageComponent; and the last is a reference to theImageComponentitself (this), which serves as an image observer. (We'll discuss image observers in Chapter 18, Working with Images and Other Media; for the time being, take this on faith.)//file: ImageComponent.javaimport java.awt.*;import javax.swing.*;public class ImageComponent extends JComponent {Image image;Dimension size;public ImageComponent(Image image) {this.image = image;MediaTracker mt = new MediaTracker(this);mt.addImage(image, 0);try {mt.waitForAll( );}catch (InterruptedException e) {// error ...};size = new Dimension (image.getWidth(null),image.getHeight(null));setSize(size);}public void paint(Graphics g) {g.drawImage(image, 0, 0, this);}public Dimension getPreferredSize( ) {return size;}}Finally,
ImageComponentprovides agetPreferredSize( )method, overriding the method it inherits fromComponent. This method simply returns the image's size, which is aDimensionobject. When you're usingJScrollPane, it's important for the object you're scrolling to provide a reliable indication of its size. Figure 14-6 shows theScrollPaneFramewith theImageComponent.
Figure 14-6. The ScrollPaneFrame application
![]()
The JSplitPane Class
A split pane is a special container that holds two components, each in its own sub-pane. A splitter bar adjusts the sizes of the two sub-panes. In a document viewer, you could use a split pane to show a table of contents next to a full document.
The following example capitalizes on the
ImageComponentclass from the previous example. It displays twoImageComponents, wrapped inJScrollPanes, in either side of aJSplitPane. You can drag the splitter bar back and forth to adjust the sizes of the two contained components.//file: SplitPaneFrame.javaimport java.awt.*;import java.awt.event.*;import javax.swing.*;import javax.swing.border.*;public class SplitPaneFrame {public static void main(String[] args) {String fileOne = "Piazza di Spagna.jpg";String fileTwo = "L1-Light.jpg";if (args.length > 0) fileOne = args[0];if (args.length > 1) fileTwo = args[1];// create a JFrame to hold everythingJFrame f = new JFrame("SplitPaneFrame");f.addWindowListener(new WindowAdapter( ) {public void windowClosing(WindowEvent we) { System.exit(0); }});f.setSize(300, 200);f.setLocation(200, 200);Image leftImage = Toolkit.getDefaultToolkit( ).getImage(fileOne);Component left =new JScrollPane(new ImageComponent(leftImage));Image rightImage = Toolkit.getDefaultToolkit( ).getImage(fileTwo);Component right =new JScrollPane(new ImageComponent(rightImage));JSplitPane split =new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, right);split.setDividerLocation(100);f.getContentPane( ).add(split);f.setVisible(true);}}This
example is shown in Figure 14-7.
Figure 14-7. Using a split pane
![]()
The JTabbedPane Class
If you've ever dealt with the System control panel in Windows, you already know what a
JTabbedPaneis. It's a container with labeled tabs. When you click on a tab, a new set of controls is shown in the body of theJTabbedPane. In Swing,JTabbedPaneis simply a specialized container.Each tab has a name. To add a tab to the
JTabbedPane, simply calladdTab( ). You'll need to specify the name of the tab as well as a component that supplies the tab's contents. Typically, it's a container holding other components.Even though the
JTabbedPaneonly shows one set of components at a time, be aware that all the components on all the pages are in memory at one time. If you have components that hog processor time or memory, try to put them into some "sleep" state when they are not showing.The following example shows how to create a
JTabbedPane. It adds standard Swing components to a first tab, named Controls. The second tab is filled with an instance ofImageComponent, which was presented earlier in this chapter.//file: TabbedPaneFrame.javaimport java.awt.*;import java.awt.event.*;import javax.swing.*;import javax.swing.border.*;public class TabbedPaneFrame {public static void main(String[] args) {// create a JFrame to hold everythingJFrame f = new JFrame("TabbedPaneFrame");f.addWindowListener(new WindowAdapter( ) {public void windowClosing(WindowEvent we) { System.exit(0); }});f.setSize(200, 200);f.setLocation(200, 200);JTabbedPane tabby = new JTabbedPane( );// create a controls paneJPanel controls = new JPanel( );controls.add(new JLabel("Service:"));JList list = new JList(new String[] { "Web server", "FTP server" });list.setBorder(BorderFactory.createEtchedBorder( ));controls.add(list);controls.add(new JButton("Start"));// create an image paneString filename = "Piazza di Spagna.jpg";Image image = Toolkit.getDefaultToolkit( ).getImage(filename);JComponent picture = new JScrollPane(new ImageComponent(image));tabby.addTab("Controls", controls);tabby.addTab("Picture", picture);f.getContentPane( ).add(tabby);f.setVisible(true);}}The code is not especially fancy, but the result is an impressive-looking user interface. The first tab is a
JPanelthat contains some other components, including aJListwith an etched border. The second tab simply contains anImageComponentwrapped in aJScrollPane. The running example is shown in Figure 14-8.
Figure 14-8. Using a tabbed pane
![]()
Scrollbars and Sliders
JScrollPaneis such a handy component that you may not ever need to use scrollbars by themselves. In fact, if you ever do find yourself using a scrollbar by itself, chances are you really want to use another component called a slider.There's not much point in describing the appearance and functionality of scrollbars and sliders. Instead, let's jump right in with an example that includes both components. Figure 14-9 shows a simple example with both a scrollbar and a slider.
Figure 14-9. Using a scrollbar and a slider
![]()
Here is the source code for this example:
//file: Slippery.javaimport java.awt.*;import java.awt.event.*;import javax.swing.*;import javax.swing.event.*;public class Slippery extends JFrame {public Slippery( ) {super("Slippery v1.0");setSize(220, 160);setLocation(200, 200);Container content = getContentPane( );JPanel main = new JPanel(new GridLayout(2, 1));JPanel scrollBarPanel = new JPanel( );final JScrollBar scrollBar =new JScrollBar(JScrollBar.HORIZONTAL, 0, 48, 0, 255);int height = scrollBar.getPreferredSize( ).height;scrollBar.setPreferredSize(new Dimension(175, height));scrollBarPanel.add(scrollBar);main.add(scrollBarPanel);JPanel sliderPanel = new JPanel( );final JSlider slider =new JSlider(JSlider.HORIZONTAL, 0, 255, 128);slider.setMajorTickSpacing(48);slider.setMinorTickSpacing(16);slider.setPaintTicks(true);sliderPanel.add(slider);main.add(sliderPanel);content.add(main, BorderLayout.CENTER);final JLabel statusLabel =new JLabel("Welcome to Slippery v1.0");content.add(statusLabel, BorderLayout.SOUTH);// wire up the event handlersscrollBar.addAdjustmentListener(new AdjustmentListener( ) {public void adjustmentValueChanged(AdjustmentEvent e) {statusLabel.setText("JScrollBar's current value = "+ scrollBar.getValue( ));}});slider.addChangeListener(new ChangeListener( ) {public void stateChanged(ChangeEvent e) {statusLabel.setText("JSlider's current value = "+ slider.getValue( ));}});}public static void main(String[] args) {JFrame f = new Slippery( );f.addWindowListener(new WindowAdapter( ) {public void windowClosing(WindowEvent e) { System.exit(0); }});f.setVisible(true);}}All we've really done here is added a
JScrollBarand aJSliderto our main window. If the user adjusts either of these components, the current value of the component is displayed in aJLabelat the bottom of the window.The
JScrollBarandJSliderare both created by specifying an orientation, eitherHORIZONTALorVERTICAL. You can also specify the minimum and maximum values for the components, as well as the initial value. TheJScrollBarsupports one additional parameter, the extent. The extent simply refers to what range of values is represented by the slider within the scroll bar. For example, in a scrollbar that runs from 0 to 255, an extent of 128 means that the slider will be half the width of the scrollable area of the scrollbar.
JSlidersupports the idea of tick marks, which are lines drawn at certain values along the slider's length. Major tick marks are slightly larger than minor tick marks. To draw tick marks, just specify an interval for major and minor tick marks, and then paint the tick marks:slider.setMajorTickSpacing(48);slider.setMinorTickSpacing(16);slider.setPaintTicks(true);
JSlideralso supports labeling the ticks with text strings, using thesetLabel-Table( )method.Responding to events from the two components is straightforward. The
JScrollBarsends outAdjustmentEvents every time something happens; theJSliderfires offChangeEvents when its value changes. In our simple example, we display the new value of the changed component in theJLabelat the bottom of the window.Dialogs
A dialog is another standard feature of user interfaces. Dialogs are frequently used to present information to the user ("Your fruit salad is ready.") or to ask a question ("Shall I bring the car around?"). Dialogs are used so commonly in GUI applications that Swing includes a handy set of pre-built dialogs. These are accessible from static methods in the
JOptionPaneclass. Many variations are possible;JOptionPanegroups them into four basic types:
- message dialog
- Displays a message to the user, usually accompanied by an OK button.
- confirmation dialog
- Ask a question and displays answer buttons, usually Yes, No, and Cancel.
- input dialog
- Asks the user to type in a string.
- option dialogs
- The most general type--you pass it your own components, which are displayed in the dialog.
A confirmation dialog is shown in Figure 14-10.
Figure 14-10. Using a confirmation dialog
![]()
Let's look at examples of each kind of dialog. The following code produces a message dialog:
JOptionPane.showMessageDialog(f, "You have mail.");The first parameter to
showMessageDialog( )is the parent component (in this casef, an existingJFrame). The dialog will be centered on the parent component. If you passnullfor the parent component, the dialog is centered in your screen. The dialogs thatJOptionPanedisplays are modal, which means they block other input to your application while they are showing.Here's a slightly fancier message dialog. We've specified a title for the dialog and a message type, which affects the icon that is displayed:
JOptionPane.showMessageDialog(f, "You are low on memory.","Apocalyptic message", JOptionPane.WARNING_MESSAGE);Here's how to display the confirmation dialog shown in Figure 14-10:
int result = JOptionPane.showConfirmDialog(null,"Do you want to remove Windows now?");In this case, we've passed
nullfor the parent component. Special values are returned fromshowConfirmDialog( )to indicate which button was pressed. There's a full example below that shows how to use this return value.Sometimes you need to ask the user to type some input. The following code puts up a dialog requesting the user's name:
String name = JOptionPane.showInputDialog(null,"Please enter your name.");Whatever the user types is returned as a
String, ornullif the user presses the Cancel button.The most general type of dialog is the option dialog. You supply an array of objects that you wish to be displayed;
JOptionPanetakes care of formatting them and displaying the dialog. The following example displays a text label, aJTextField, and aJPasswordField. (Text components are described in the next chapter.)JTextField userField = new JTextField( );JPasswordField passField = new JPasswordField( );String message = "Please enter your user name and password.";result = JOptionPane.showOptionDialog(f,new Object[] { message, userField, passField },"Login", JOptionPane.OK_CANCEL_OPTION,JOptionPane.QUESTION_MESSAGE,null, null, null);We've also specified a dialog title ("Login") in the call to
showOptionDialog( ). We want OK and Cancel buttons, so we passOK_CANCEL_OPTIONas the dialog type. TheQUESTION_MESSAGEargument indicates we'd like to see the question mark icon. The last three items are optional: anIcon, an array of different choices, and a current selection. Since the icon parameter isnull, a default is used. If the array of choices and the current selection parameters were notnull,JOptionPanemight try to display the choices in a list or combo box.The following application includes all the examples we've covered:
import javax.swing.*;public class ExerciseOptions {public static void main(String[] args) {JFrame f = new JFrame("ExerciseOptions v1.0");f.setSize(200, 200);f.setLocation(200, 200);f.setVisible(true);JOptionPane.showMessageDialog(f, "You have mail.");JOptionPane.showMessageDialog(f, "You are low on memory.","Apocalyptic message", JOptionPane.WARNING_MESSAGE);int result = JOptionPane.showConfirmDialog(null,"Do you want to remove Windows now?");switch (result) {case JOptionPane.YES_OPTION:System.out.println("Yes"); break;case JOptionPane.NO_OPTION:System.out.println("No"); break;case JOptionPane.CANCEL_OPTION:System.out.println("Cancel"); break;case JOptionPane.CLOSED_OPTION:System.out.println("Closed"); break;}String name = JOptionPane.showInputDialog(null,"Please enter your name.");System.out.println(name);JTextField userField = new JTextField( );JPasswordField passField = new JPasswordField( );String message = "Please enter your user name and password.";result = JOptionPane.showOptionDialog(f,new Object[] { message, userField, passField },"Login", JOptionPane.OK_CANCEL_OPTION,JOptionPane.QUESTION_MESSAGE,null, null, null);if (result == JOptionPane.OK_OPTION)System.out.println(userField.getText( ) +" " + new String(passField.getPassword( )));System.exit(0);}}File Selection Dialog
A
JFileChooseris a standard file-selection box. As with other Swing components,JFileChooseris implemented in pure Java, so it looks and acts the same on different platforms.Selecting files all day can be pretty boring without a greater purpose, so we'll exercise the
JFileChooserin a mini-editor application.Editorprovides a text area in which we can load and work with files. (TheJFileChoosercreated byEditoris shown in Figure 14-11.) We'll stop just shy of the capability to save and let you fill in the blanks (with a few caveats):
Figure 14-11. Using a JFileChooser
![]()
import java.awt.*;import java.awt.event.*;import java.io.*;import javax.swing.*;public class Editorextends JFrameimplements ActionListener {public static void main(String[] s) { new Editor( ); }private JEditorPane textPane = new JEditorPane( );public Editor( ) {super("Editor v1.0");addWindowListener(new WindowAdapter( ) {public void windowClosing(WindowEvent e) { System.exit(0); }});Container content = getContentPane( );content.add(new JScrollPane(textPane), BorderLayout.CENTER);JMenu menu = new JMenu("File");menu.add(makeMenuItem("Open"));menu.add(makeMenuItem("Save"));menu.add(makeMenuItem("Quit"));JMenuBar menuBar = new JMenuBar( );menuBar.add(menu);setJMenuBar(menuBar);setSize(300, 300);setLocation(200, 200);setVisible(true);}public void actionPerformed(ActionEvent e) {String command = e.getActionCommand( );if (command.equals("Quit")) System.exit(0);else if (command.equals("Open")) loadFile( );else if (command.equals("Save")) saveFile( );}private void loadFile ( ) {JFileChooser chooser = new JFileChooser( );int result = chooser.showOpenDialog(this);if (result == JFileChooser.CANCEL_OPTION) return;try {File file = chooser.getSelectedFile( );java.net.URL url = file.toURL( );textPane.setPage(url);}catch (Exception e) {textPane.setText("Could not load file: " + e);}}private void saveFile( ) {JFileChooser chooser = new JFileChooser( );chooser.showSaveDialog(this);// Save file data...}private JMenuItem makeMenuItem( String name ) {JMenuItem m = new JMenuItem( name );m.addActionListener( this );return m;}}
Editoris aJFramethat lays itself out with aJEditorPane(which will be covered in the next chapter) and a pull-down menu. From the pull-down File menu, we can Open, Save, or Quit. TheactionPerformed( )method catches the events associated with these menu selections and takes the appropriate action.The interesting parts of
Editorare theprivatemethodsloadFile( )andsaveFile( ).loadFile( )creates a newJFileChooserand calls itsshowOpen-Dialog( )method.A
JFileChooserdoes its work when theshowOpenDialog( )method is called. This method blocks the caller until the dialog completes its job, at which time the file chooser disappears. After that, we can retrieve the designated file with thegetFile( )method. InloadFile( ), we convert the selectedFileto aURLand pass it to theJEditorPane, which displays the selected file. As you'll learn in the next chapter,JEditorPanecan display HTML and RTF files.You can fill out the unfinished
saveFile( )method if you wish, but it would be prudent to add the standard safety precautions. For example, you could use one of the confirmation dialogs we just looked at to prompt the user before overwriting an existing file.The Color Chooser
Swing is chock full of goodies.
JColorChooseris yet another ready-made dialog supplied with Swing; it allows your users to choose colors. The following very brief example shows how easy it is to useJColorChooser:import java.awt.*;import java.awt.event.*;import javax.swing.*;public class LocalColor {public static void main(String[] args) {final JFrame f = new JFrame("LocalColor v1.0");f.addWindowListener(new WindowAdapter( ) {public void windowClosing(WindowEvent e) { System.exit(0); }});f.setSize(200, 200);f.setLocation(200, 200);final Container content = f.getContentPane( );content.setLayout(new GridBagLayout( ));JButton button = new JButton("Change color...");content.add(button);button.addActionListener(new ActionListener( ) {public void actionPerformed(ActionEvent e) {Color c = JColorChooser.showDialog(f,"Choose a color", content.getBackground( ));if (c != null) content.setBackground(c);}});f.setVisible(true);}}This examples shows a frame window with a single button. When you click on the button, a color chooser pops up. After you select a color, it becomes the background color of the frame window.
Basically all we have to do is call
JColorChooser's static methodshowDialog( ). In this example, we've specified a parent component, a dialog title, and an initial color value. But you can get away with just specifying a parent component. Whatever color the user chooses is returned; if the user presses the Cancel button,nullis returned.
1. Like the text of
JButtons andJLabels, menu labels can contain simple HTML.
Back to: Learning Java
© 2001, O'Reilly & Associates, Inc.
webmaster@oreilly.com