Chapter 18. Graphical User Interfaces

CHAPTER GOALS

  • G To become familiar with common user-interface components, such as text components, radio buttons, check boxes, and menus

  • G To understand the use of layout managers to arrange user-interface components in a container

  • G To build programs that handle events from user-interface components

  • To learn how to browse the Java documentation

In this chapter, we will delve more deeply into graphical user interface programming. The graphical applications with which you are familiar have many visual gadgets for information entry: text components, buttons, scroll bars, menus, and so on. In this chapter, you will learn how to use the most common user-interface components in the Java Swing user-interface toolkit. Swing has many more components than can be mastered in a first course, and even the basic components have advanced options that can't be covered here. In fact, few programmers try to learn everything about a particular user-interface component. It is more important to understand the concepts and to search the Java documentation for the details. This chapter walks you through one example to show you how the Java documentation is organized and how you can rely on it for your programming.

Processing Text Input

We start our discussion of graphical user interfaces with text input. Of course, a graphical application can receive text input by calling the showInputDialog method of the JOptionPane class, but popping up a separate dialog box for each input is not a natural user interface. Most graphical programs collect text input through text fields (see Figure 1). In this section, you will learn how to add text fields to a graphical application, and how to read what the user types into them.

The JTextField class provides a text field. When you construct a text field, you need to supply the width—the approximate number of characters that you expect the user to type.

final int FIELD_WIDTH = 10;
final JTextField rateField = new JTextField(FIELD_WIDTH);

Note

Use JTextField components to provide space for user input. Place a JLabel next to each text field.

Users can type additional characters, but then a part of the contents of the field becomes invisible.

You will want to label each text field so that the user knows what to type into it. Construct a JLabel object for each label:

JLabel rateLabel = new JLabel("Interest Rate: ");

You want to give the user an opportunity to enter all information into the text fields before processing it. Therefore, you should supply a button that the user can press to indicate that the input is ready for processing.

When that button is clicked, its actionPerformed method reads the user input from the text field, using the getText method of the JTextField class. The getText method returns a String object. In our sample program, we turn the string into a number, using the Double.parseDouble method. After updating the account, we show the balance in another label.

class AddInterestListener implements ActionListener
{
   public void actionPerformed(ActionEvent event)
   {
      double rate = Double.parseDouble(rateField.getText());
      double interest = account.getBalance() * rate / 100;
      account.deposit(interest);
      resultLabel.setText("balance: " + account.getBalance());
   }
}

The following application is a useful prototype for a graphical user-interface front end for arbitrary calculations. You can easily modify it for your own needs. Place other input components into the frame. Change the contents of the actionPerformed method to carry out other calculations. Display the result in a label.

An Application with a Text Field

Figure 18.1. An Application with a Text Field

ch18/textfield/InvestmentViewer3.java

1  import javax.swing.JFrame;
2
3  /**
4     This program displays the growth of an investment.
5  */
6  public class InvestmentViewer3
7  {
8     public static void main(String[] args)
9     {
10       JFrame frame = new InvestmentFrame();
11       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
12       frame.setVisible(true);
13  }
14 }

ch18/textfield/InvestmentFrame.java

1  import java.awt.event.ActionEvent;
2  import java.awt.event.ActionListener;
3  import javax.swing.JButton;
4  import javax.swing.JFrame;
5  import javax.swing.JLabel;
6  import javax.swing.JPanel;
7  import javax.swing.JTextField;
8
9  /**
10    A frame that shows the growth of an investment with variable interest.
11  */
12  public class InvestmentFrame extends JFrame
13  {
14    private static final int FRAME_WIDTH = 450;
15    private static final int FRAME_HEIGHT = 100;
16
17    private static final double DEFAULT_RATE = 5;
18    private static final double INITIAL_BALANCE = 1000;
19
20    private JLabel rateLabel;
21    private JTextField rateField;
22    private JButton button;
23    private JLabel resultLabel;
24    private JPanel panel;
25    private BankAccount account;
26
27    public InvestmentFrame()
28    {
29      account = new BankAccount(INITIAL_BALANCE);
30
31      // Use instance variables for components
32      resultLabel = new JLabel("balance: " + account.getBalance());
33
34      // Use helper methods
35      createTextField();
36      createButton();
37      createPanel();
38
39      setSize(FRAME_WIDTH, FRAME_HEIGHT);
40   }
41
42      private void createTextField()
43      {
44          rateLabel = new JLabel("Interest Rate: ");
45
46          final int FIELD_WIDTH = 10;
47          rateField = new JTextField(FIELD_WIDTH);
48          rateField.setText("" + DEFAULT_RATE);
49      }
50
51      private void createButton()
52      {
53          button = new JButton("Add Interest");
54
55          class AddInterestListener implements ActionListener
56          {
57             public void actionPerformed(ActionEvent event)
58             {
59                double rate = Double.parseDouble(rateField.getText());
60                double interest = account.getBalance() * rate / 100;
61                account.deposit(interest);
62                resultLabel.setText("balance: " + account.getBalance());
63             }
64          }
65
66           ActionListener listener = new AddInterestListener();
67           button.addActionListener(listener);
68      }
69
70      private void createPanel()
71      {
72            panel = new JPanel();
73            panel.add(rateLabel);
74            panel.add(rateField);
75            panel.add(button);
76          panel.add(resultLabel);
77          add(panel);
78      }
79   }
An Application with a Text Field

2. If a text field holds an integer, what expression do you use to read its contents?

Text Areas

In the preceding section, you saw how to construct text fields. A text field holds a single line of text. To display multiple lines of text, use the JTextArea class.

When constructing a text area, you can specify the number of rows and columns:

final int ROWS = 10;
final int COLUMNS = 30;
JTextArea textArea = new JTextArea(ROWS, COLUMNS);

Note

Use a JTextArea to show multiple lines of text.

Use the setText method to set the text of a text field or text area. The append method adds text to the end of a text area. Use newline characters to separate lines, like this:

textArea.append(account.getBalance() + "
");

If you want to use a text field or text area for display purposes only, call the setEditable method like this

textArea.setEditable(false);

Now the user can no longer edit the contents of the field, but your program can still call setText and append to change it.

As shown in Figure 2, the JTextField and JTextArea classes are subclasses of the class JTextComponent. The methods setText and setEditable are declared in the JText-Component class and inherited by JTextField and JTextArea. However, the append method is declared in the JTextArea class.

A Part of the Hierarchy of Swing User-Interface Components

Figure 18.2. A Part of the Hierarchy of Swing User-Interface Components

The Investment Application with a Text Area

Figure 18.3. The Investment Application with a Text Area

To add scroll bars to a text area, use a JScrollPane, like this:

JTextArea textArea = new JTextArea(ROWS, COLUMNS);
JScrollPane scrollPane = new JScrollPane(textArea);

Note

You can add scroll bars to any component with a JScrollPane.

Then add the scroll pane to the panel. Figure 3 shows the result.

The following sample program puts these concepts together. A user can enter numbers into the interest rate text field and then click on the "Add Interest" button). The interest rate is applied, and the updated balance is appended to the text area. The text area has scroll bars and is not editable.

This program is similar to the previous investment viewer program, but it keeps track of all the bank balances, not just the last one.

ch18/textarea/InvestmentFrame.java

1  import java.awt.event.ActionEvent;
2  import java.awt.event.ActionListener;
3  import javax.swing.JButton;
4  import javax.swing.JFrame;
5  import javax.swing.JLabel;
6  import javax.swing.JPanel;
7  import javax.swing.JScrollPane;
8  import javax.swing.JTextArea;
9  import javax.swing.JTextField;
10
11  /**
12     A frame that shows the growth of an investment with variable interest.
13  */
14  public class InvestmentFrame extends JFrame
15  {
16     private static final int FRAME_WIDTH = 400;
17     private static final int FRAME_HEIGHT = 250;
18
19     private static final int AREA_ROWS = 10;
20     private static final int AREA_COLUMNS = 30;
21     private static final double DEFAULT_RATE = 5;
22     private static final double INITIAL_BALANCE = 1000;
23
24     private JLabel rateLabel;
25     private JTextField rateField;
26     private JButton button;
27     private JTextArea resultArea;
28     private JPanel panel;
29     private BankAccount account;
30
31     public InvestmentFrame()
32     {
33        account = new BankAccount(INITIAL_BALANCE);
34        resultArea = new JTextArea(AREA_ROWS, AREA_COLUMNS);
35        resultArea.setEditable(false);
36
37        // Use helper methods
38        createTextField();
39        createButton();
40        createPanel();
41
42        setSize(FRAME_WIDTH, FRAME_HEIGHT);
43     }
44
45     private void createTextField()
46     {
47        rateLabel = new JLabel("Interest Rate: ");
48
49        final int FIELD_WIDTH = 10;
50        rateField = new JTextField(FIELD_WIDTH);
51        rateField.setText("" + DEFAULT_RATE);
52     }
53
54     private void createButton()
55     {
56        button = new JButton("Add Interest");
57
58     class AddInterestListener implements ActionListener
59        {
60             public void actionPerformed(ActionEvent event)
61             {
62                double rate = Double.parseDouble(rateField.getText());
63                double interest = account.getBalance() * rate / 100;
64                account.deposit(interest);
65                resultArea.append(account.getBalance() + "
");
66             }
67        }
68
69             ActionListener listener = new AddInterestListener();
70             button.addActionListener(listener);
71       }
72
73       private void createPanel()
74       {
75            panel = new JPanel();
76            panel.add(rateLabel);
77            panel.add(rateField);
78            panel.add(button);
79            JScrollPane scrollPane = new JScrollPane(resultArea);
80            panel.add(scrollPane);
81            add(panel);
82       }
83   }

3. What is the difference between a text field and a text area?

4. Why did the InvestmentFrame program call resultArea.setEditable(false)?

5. How would you modify the InvestmentFrame program if you didn't want to use scroll bars?

Layout Management

Up to now, you have had limited control over the layout of user-interface components. You learned how to add components to a panel. The panel arranged the components from the left to the right. However, in many applications, you need more sophisticated arrangements.

Note

User-interface components are arranged by placing them inside containers.

In Java, you build up user interfaces by adding components into containers such as panels. Each container has its own layout manager, which determines how the components are laid out.

By default, a JPanel uses a flow layout. A flow layout simply arranges its components from left to right and starts a new row when there is no more room in the current row.

Note

Each container has a layout manager that directs the arrangement of its components.

Another commonly used layout manager is the border layout. The border layout groups components into five areas: center, north, west, south, and east (see Figure 4). Not all of the areas need to be occupied.

The border layout is the default layout manager for a frame (or, more technically, the frame's content pane). But you can also use the border layout in a panel:

panel.setLayout(new BorderLayout());

Note

Three useful layout managers are the border layout, flow layout, and grid layout.

Now the panel is controlled by a border layout, not the flow layout. When adding a component, you specify the position, like this:

panel.add(component, BorderLayout.NORTH);

Note

When adding a component to a container with the border layout, specify the NORTH, EAST, SOUTH, WEST, or CENTER position.

The grid layout is a third layout that is sometimes useful. The grid layout arranges components in a grid with a fixed number of rows and columns, resizing each of the components so that they all have the same size. Like the border layout, it also expands each component to fill the entire allotted area. (If that is not desirable, you need to place each component inside a panel.) Figure 5 shows a number pad panel that uses a grid layout. To create a grid layout, you supply the number of rows and columns in the constructor, then add the components, row by row, left to right:

Note

The content pane of a frame has a border layout by default. A panel has a flow layout by default.

Components Expand to Fill Space in the Border Layout

Figure 18.4. Components Expand to Fill Space in the Border Layout

The Grid Layout

Figure 18.5. The Grid Layout

JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(4, 3));
buttonPanel.add(button7);
buttonPanel.add(button8);
buttonPanel.add(button9);
buttonPanel.add(button4);
...

Sometimes you want to have a tabular arrangement of the components where columns have different sizes or one component spans multiple columns. A more complex layout manager called the grid bag layout can handle these situations. The grid bag layout is quite complex to use, however, and we do not cover it in this book; see, for example, Cay S. Horstmann and Gary Cornell, Core Java 2 Volume 1: Fundamentals,8th edition (Prentice Hall, 2008), for more information. Java 6 introduces a group layout that is designed for use by interactive tools—see Productivity Hint 18.1 on page 757.

Fortunately, you can create acceptable-looking layouts in nearly all situations by nesting panels. You give each panel an appropriate layout manager. Panels don't have visible borders, so you can use as many panels as you need to organize your components. Figure 6 shows an example. The keypad buttons are contained in a panel with grid layout. That panel is itself contained in a larger panel with border layout. The text field is in the northern position of the larger panel. The following code produces this arrangement:

JPanel keypadPanel = new JPanel();
keypadPanel.setLayout(new BorderLayout());
buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(4, 3));
buttonPanel.add(button7);
buttonPanel.add(button8);
// ...
keypadPanel.add(buttonPanel, BorderLayout.CENTER);
JTextField display = new JTextField();
keypadPanel.add(display, BorderLayout.NORTH);
Nesting Panels

Figure 18.6. Nesting Panels

Nesting Panels

7. How can you stack three buttons on top of each other?

Choices

In the following sections, you will see how to present a finite set of choices to the user. Which Swing component you use depends on whether the choices are mutually exclusive or not, and on the amount of space you have for displaying the choices.

Radio Buttons

If the choices are mutually exclusive, use a set of radio buttons. In a radio button set, only one button can be selected at a time. When the user selects another button in the same set, the previously selected button is automatically turned off. (These buttons are called radio buttons because they work like the station selector buttons on a car radio: If you select a new station, the old station is automatically deselected.) For example, in Figure 7, the font sizes are mutually exclusive. You can select small, medium, or large, but not a combination of them.

Note

For a small set of mutually exclusive choices, use a group of radio buttons or a combo box.

To create a set of radio buttons, first create each button individually, and then add all buttons of the set to a ButtonGroup object:

JRadioButton smallButton = new JRadioButton("Small");
JRadioButton mediumButton = new JRadioButton("Medium");
JRadioButton largeButton = new JRadioButton("Large");

ButtonGroup group = new ButtonGroup();
group.add(smallButton);
group.add(mediumButton);
group.add(largeButton);

Note

Add radio buttons into a ButtonGroup so that only one button in the group is on at any time.

Note that the button group does not place the buttons close to each other on the container. The purpose of the button group is simply to find out which buttons to turn off when one of them is turned on. It is still your job to arrange the buttons on the screen.

The isSelected method is called to find out whether a button is currently selected or not. For example,

if (largeButton.isSelected()) { size = LARGE_SIZE; }

Because users will expect one radio button in a radio button group to be selected, call setSelected(true) on the default radio button before making the enclosing frame visible.

If you have multiple button groups, it is a good idea to group them together visually. It is a good idea to use a panel for each set of radio buttons, but the panels themselves are invisible. You can add a border to a panel to make it visible. In Figure 7, for example, the panels containing the Size radio buttons and Style check boxes have borders.

Note

You can place a border around a panel to group its contents visually.

There are a large number of border types. We will show only a couple of variations and leave it to the border enthusiasts to look up the others in the Swing documentation. The EtchedBorder class yields a border with a three-dimensional, etched effect. You can add a border to any component, but most commonly you apply it to a panel:

JPanel panel = new JPanel();
panel.setBorder(new EtchedBorder());
A Combo Box, Check Boxes, and Radio Buttons

Figure 18.7. A Combo Box, Check Boxes, and Radio Buttons

If you want to add a title to the border (as in Figure 7), you need to construct a TitledBorder. You make a titled border by supplying a basic border and then the title you want. Here is a typical example:

panel.setBorder(new TitledBorder(new EtchedBorder(), "Size"));

Check Boxes

A check box is a user-interface component with two states: checked and unchecked. You use a group of check boxes when one selection does not exclude another. For example, the choices for "Bold" and "Italic" in Figure 7 are not exclusive. You can choose either, both, or neither. Therefore, they are implemented as a set of separate check boxes. Radio buttons and check boxes have different visual appearances. Radio buttons are round and have a black dot when selected. Check boxes are square and have a check mark when selected.

You construct a check box by giving the name in the constructor:

JCheckBox italicCheckBox = new JCheckBox("Italic");

Note

For a binary choice, use a check box.

Because check box settings do not exclude each other, you do not place a set of check boxes inside a button group.

As with radio buttons, you use the isSelected method to find out whether a check box is currently checked or not.

Combo Boxes

If you have a large number of choices, you don't want to make a set of radio buttons, because that would take up a lot of space. Instead, you can use a combo box. This component is called a combo box because it is a combination of a list and a text field. The text field displays the name of the current selection. When you click on the arrow to the right of the text field of a combo box, a list of selections drops down, and you can choose one of the items in the list (see Figure 8).

Note

For a large set of choices, use a combo box.

An Open Combo Box

Figure 18.8. An Open Combo Box

If the combo box is editable, you can also type in your own selection. To make a combo box editable, call the setEditable method.

You add strings to a combo box with the addItem method.

JComboBox facenameCombo = new JComboBox();
facenameCombo.addItem("Serif");
facenameCombo.addItem("SansSerif");
...

You get the item that the user has selected by calling the getSelectedItem method. However, because combo boxes can store other objects in addition to strings, the getSelectedItem method has return type Object. Hence you must cast the returned value back to String.

String selectedString = (String) facenameCombo.getSelectedItem();
The Components of the FontViewerFrame

Figure 18.9. The Components of the FontViewerFrame

Classes of the Font Viewer Program

Figure 18.10. Classes of the Font Viewer Program

You can select an item for the user with the setSelectedItem method.

Radio buttons, check boxes, and combo boxes generate an ActionEvent whenever the user selects an item. In the following program, we don't care which component was clicked—all components notify the same listener object. Whenever the user clicks on any one of them, we simply ask each component for its current content, using the isSelected and getSelectedItem methods. We then redraw the text sample with the new font.

Figure 9 shows how the components are arranged in the frame. Figure 10 shows the relationships between the classes used in the font viewer program.

Note

Radio buttons, check boxes, and combo boxes generate action events, just as buttons do.

ch18/choice/FontViewer.java

1  import javax.swing.JFrame;
2
3  /**
4     This program allows the user to view font effects.
5  */
6  public class FontViewer
7  {
8     public static void main(String[] args)
9     {
10       JFrame frame = new FontViewerFrame();
11       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
12       frame.setTitle("FontViewer");
13       frame.setVisible(true);
14     }
15  }

ch18/choice/FontViewerFrame.java

1   import java.awt.BorderLayout;
2   import java.awt.Font;
3   import java.awt.GridLayout;
4   import java.awt.event.ActionEvent;
5   import java.awt.event.ActionListener;
6   import javax.swing.ButtonGroup;
7   import javax.swing.JButton;
8   import javax.swing.JCheckBox;
9   import javax.swing.JComboBox;
10   import javax.swing.JFrame;
11   import javax.swing.JLabel;
12   import javax.swing.JPanel;
13   import javax.swing.JRadioButton;
14   import javax.swing.border.EtchedBorder;
15   import javax.swing.border.TitledBorder;
16
17   /**
18      This frame contains a text field and a control panel
19      to change the font of the text.
20   */
21   public class FontViewerFrame extends JFrame
22   {
23      private static final int FRAME_WIDTH = 300;
24      private static final int FRAME_HEIGHT = 400;
25
26      private JLabel sampleField;
27      private JCheckBox italicCheckBox;
28      private JCheckBox boldCheckBox;
29      private JRadioButton smallButton;
30      private JRadioButton mediumButton;
31      private JRadioButton largeButton;
32      private JComboBox facenameCombo;
33      private ActionListener listener;
34
35      /**
36         Constructs the frame.
37      */
38      public FontViewerFrame()
39      {
40         // Construct text sample
41         sampleField = new JLabel("Big Java");
42         add(sampleField, BorderLayout.CENTER);
43
44         // This listener is shared among all components
45         class ChoiceListener implements ActionListener
46         {
47            public void actionPerformed(ActionEvent event)
48            {
49               setSampleFont();
50            }
51         }
52
53         listener = new ChoiceListener();
54
55         createControlPanel();
56         setSampleFont();
57         setSize(FRAME_WIDTH, FRAME_HEIGHT);
58      }
59
60     /**
61        Creates the control panel to change the font.
62     */
63     public void createControlPanel()
64     {
65       JPanel facenamePanel = createComboBox();
66       JPanel sizeGroupPanel = createCheckBoxes();
67       JPanel styleGroupPanel = createRadioButtons();
68
69       //   Line up component panels
70
71       JPanel controlPanel = new JPanel();
72       controlPanel.setLayout(new GridLayout(3, 1));
73       controlPanel.add(facenamePanel);
74       controlPanel.add(sizeGroupPanel);
75       controlPanel.add(styleGroupPanel);
76
77       //  Add panels to content pane
78
79        add(controlPanel, BorderLayout.SOUTH);
80     }
81
82     /**
83        Creates the combo box with the font style choices.
84        @return  the panel containing the combo box
85     */
86     public JPanel createComboBox()
87      {
88        facenameCombo = new JComboBox();
89        facenameCombo.addItem("Serif");
90        facenameCombo.addItem("SansSerif");
91        facenameCombo.addItem("Monospaced");
92        facenameCombo.setEditable(true);
93        facenameCombo.addActionListener(listener);
94
95        JPanel panel = new JPanel();
96        panel.add(facenameCombo);
97        return panel;
98     }
99
100     /**
101        Creates the check boxes for selecting bold and italic styles.
102        @return  the panel containing the check boxes
103     */
104     public JPanel createCheckBoxes()
105     {
106         italicCheckBox = new JCheckBox("Italic");
107         italicCheckBox.addActionListener(listener);
108
109        boldCheckBox = new JCheckBox("Bold");
110         boldCheckBox.addActionListener(listener);
111
112        JPanel panel = new JPanel();
113        panel.add(italicCheckBox);
114        panel.add(boldCheckBox);
115        panel.setBorder(new TitledBorder(new EtchedBorder(), "Style"));
116
117        return panel;
118     }
119
120     /**
121        Creates the radio buttons to select the font size.
122        @return the panel containing the radio buttons
123     */
124     public JPanel createRadioButtons()
125     {
126        smallButton = new JRadioButton("Small");
127        smallButton.addActionListener(listener);
128
129        mediumButton = new JRadioButton("Medium");
130        mediumButton.addActionListener(listener);
131
132        largeButton = new JRadioButton("Large");
133        largeButton.addActionListener(listener);
134        largeButton.setSelected(true);
135
136        // Add radio buttons to button group
137
138        ButtonGroup group = new ButtonGroup();
139        group.add(smallButton);
140        group.add(mediumButton);
141        group.add(largeButton);
142
143        JPanel panel = new JPanel();
144        panel.add(smallButton);
145        panel.add(mediumButton);
146        panel.add(largeButton);
147        panel.setBorder(new TitledBorder(new EtchedBorder(), "Size"));
148
149        return panel;
150     }
151
152     /**
153        Gets user choice for font name, style, and size
154        and sets the font of the text sample.
155     */
156     public void setSampleFont()
157
158        // Get font name
159        String facename
160               = (String) facenameCombo.getSelectedItem();
161
162        // Get font style
163
164        int style = 0;
165        if (italicCheckBox.isSelected())
166        {
167           style = style + Font.ITALIC;
168        }
169        if (boldCheckBox.isSelected())
170        {
171           style = style + Font.BOLD;
172        }
173
174        // Get font size
175
176        int size = 0;
177
178        final int SMALL_SIZE = 24;
179        final int MEDIUM_SIZE = 36;
180        final int LARGE_SIZE = 48;
181
182        if (smallButton.isSelected()) { size = SMALL_SIZE; }
183        else if (mediumButton.isSelected()) { size = MEDIUM_SIZE; }
184        else if (largeButton.isSelected()) { size = LARGE_SIZE; }
185
186        //  Set font of text field
187
188        sampleField.setFont(new Font(facename, style, size));
189        sampleField.repaint();
190        }
191    }
Classes of the Font Viewer Program

9. Why do all user-interface components in the FontViewerFrame class share the same listener?

10. Why was the combo box placed inside a panel? What would have happened if it had been added directly to the control panel?

Menus

Anyone who has ever used a graphical user interface is familiar with pull-down menus (see Figure 12). In Java it is easy to create these menus.

The container for the top-level menu items is called a menu bar. A menu is a collection of menu items and more menus (submenus). You add menu items and sub-menus with the add method:

JMenuItem fileExitItem = new JMenuItem("Exit");
fileMenu.add(fileExitItem);

Note

A frame contains a menu bar. The menu bar contains menus. A menu contains submenus and menu items.

A menu item has no further submenus. When the user selects a menu item, the menu item sends an action event. Therefore, you want to add a listener to each menu item:

fileExitItem.addActionListener(listener);

You add action listeners only to menu items, not to menus or the menu bar. When the user clicks on a menu name and a submenu opens, no action event is sent.

Note

Menu items generate action events.

Pull-Down Menus Menu bar

Figure 18.12. Pull-Down Menus Menu bar

The following program builds up a small but typical menu and traps the action events from the menu items. To keep the program readable, it is a good idea to use a separate method for each menu or set of related menus. Have a look at the create-FaceItem method, which creates a menu item to change the font face. The same listener class takes care of three cases, with the name parameters varying for each menu item. The same strategy is used for the createSizeItem and createStyleItem methods.

ch18/menu/FontViewer2.java

1  import javax.swing.JFrame;
2
3  /**
4     This program uses a menu to display font effects.
5  */
6  public class FontViewer2
7  {
8     public static void main(String[] args)
9     {
10       JFrame frame = new FontViewer2Frame();
11       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
12       frame.setVisible(true);
13     }
14  }

ch18/menu/FontViewer2Frame.java

1  import java.awt.BorderLayout;
2  import java.awt.Font;
3  import java.awt.GridLayout;
4  import java.awt.event.ActionEvent;
5  import java.awt.event.ActionListener;
6  import javax.swing.ButtonGroup;
7  import javax.swing.JButton;
8  import javax.swing.JCheckBox;
9  import javax.swing.JComboBox;
10  import javax.swing.JFrame;
11  import javax.swing.JLabel;
12  import javax.swing.JMenu;
13  import javax.swing.JMenuBar;
14  import javax.swing.JMenuItem;
15  import javax.swing.JPanel;
16  import javax.swing.JRadioButton;
17  import javax.swing.border.EtchedBorder;
18  import javax.swing.border.TitledBorder;
19
20  /**
21     This frame has a menu with commands to change the font
22      of a text sample.
23  */
24  public class FontViewer2Frame extends JFrame
25  {
26     private static final int FRAME_WIDTH = 300;
27     private static final int FRAME_HEIGHT = 400;
28
29     private JLabel sampleField;
30     private String facename;
31     private int fontstyle;
32     private int fontsize;
33
34     /**
35        Constructs the frame.
36     */
37     public FontViewer2Frame()
38     {
39        // Construct text sample
40        sampleField = new JLabel("Big Java");
41        add(sampleField, BorderLayout.CENTER);
42
43        // Construct menu
44        JMenuBar menuBar = new JMenuBar();
45        setJMenuBar(menuBar);
46        menuBar.add(createFileMenu());
47        menuBar.add(createFontMenu());
48
49        facename = "Serif";
50        fontsize = 24;
51        fontstyle = Font.PLAIN;
52
53        setSampleFont();
54        setSize(FRAME_WIDTH, FRAME_HEIGHT);
55     }
56
57     /**
58        Creates the File menu.
59        @return the menu
60     */
61     public JMenu createFileMenu()
62
63     {
64          JMenu menu = new JMenu("File");
65          menu.add(createFileExitItem());
66          return menu;
67     }
68
69    /**
70      Creates the File->Exit menu item and sets its action listener.
71      @return  the menu item
72    */
73    public JMenuItem createFileExitItem()
74    {
75      JMenuItem item = new JMenuItem("Exit");
76      class MenuItemListener implements ActionListener
77       {
78          public void actionPerformed(ActionEvent event)
79          {
80             System.exit(0);
81          }
82      }
83      ActionListener listener = new MenuItemListener();
84      item.addActionListener(listener);
85      return item;
86    }
87
88    /**
89      Creates the Font submenu.
90      @return  the menu
91    */
92    public JMenu createFontMenu()
93    {
94      JMenu menu = new JMenu("Font");
95      menu.add(createFaceMenu());
96      menu.add(createSizeMenu());
97      menu.add(createStyleMenu());
98      return menu;
99    }
100
101    /**
102      Creates the Face submenu.
103      @return  the menu
104    */
105    public JMenu createFaceMenu()
106    {
107      JMenu menu = new JMenu("Face");
108      menu.add(createFaceItem("Serif"));
109      menu.add(createFaceItem("SansSerif"));
110      menu.add(createFaceItem("Monospaced"));
111      return menu;
112    }
113
114     /**
115      Creates the Size submenu.
116      @return  the menu
117    */
118    public JMenu createSizeMenu()
119    {
120      JMenu menu = new JMenu("Size");
121      menu.add(createSizeItem("Smaller", −1));
122      menu.add(createSizeItem("Larger", 1));
123      return menu;
124    }
125
126    /**
127       Creates the Style submenu.
128       @return the menu
129    */
130    public JMenu createStyleMenu()
131    {
132       JMenu menu = new JMenu("Style");
133               menu.add(createStyleItem("Plain", Font.PLAIN));
134       menu.add(createStyleItem("Bold", Font.BOLD));
135               menu.add(createStyleItem("Italic", Font.ITALIC));
136       menu.add(createStyleItem("Bold Italic", Font.BOLD
137                  + Font.ITALIC));
138       return menu;
139    }
140
141    /**
142       Creates a menu item to change the font face and set its action listener.
143       @param name the name of the font face
144       @return the menu item
145    */
146    public JMenuItem createFaceItem(final String name)
147    {
148       JMenuItem item = new JMenuItem(name);
149       class MenuItemListener implements ActionListener
150       {
151           public void actionPerformed(ActionEvent event)
152           {
153               facename = name;
154               setSampleFont();
155           }
156       }
157       ActionListener listener = new MenuItemListener();
158       item.addActionListener(listener);
159       return item;
160    }
161
162    /**
163       Creates a menu item to change the font size
164       and set its action listener.
165       @param name the name of the menu item
166       @param ds  the amount by which to change the size
167       @return  the menu item
168    */
169    public JMenuItem createSizeItem(String name, final int ds)
170    {
171          JMenuItem item = new JMenuItem(name);
172          class MenuItemListener implements ActionListener
173          {
174              public void actionPerformed(ActionEvent event)
175              {
176               fontsize = fontsize + ds;
177               setSampleFont();
178              }
179          }
180             ActionListener listener = new MenuItemListener();
181             item.addActionListener(listener);
182             return item;
183    }
184
185    /**
186       Creates a menu item to change the font style
187        and set its action listener.
188       @param name the name of the menu item
189       @param style the new font style
190       @return the menu item
191    */
192    public JMenuItem createStyleItem(String name, final int style)
193    {
194       JMenuItem item = new JMenuItem(name);
195       class MenuItemListener implements ActionListener
196       {
197           public void actionPerformed(ActionEvent event)
198           {
199              fontstyle = style;
200              setSampleFont();
201           }
202       }
203           ActionListener listener = new MenuItemListener();
204           item.addActionListener(listener);
205           return item;
206    }
207
208    /**
209       Sets the font of the text sample.
210    */
211    public void setSampleFont()
212    {
213           Font f = new Font(facename, fontstyle, fontsize);
214           sampleField.setFont(f);
215           sampleField.repaint();
216       }
217    }
Pull-Down Menus Menu bar

12. Why is the name parameter in the createFaceItem method declared as final?

Exploring the Swing Documentation

In the preceding sections, you saw the basic properties of the most common user-interface components. We purposefully omitted many options and variations to simplify the discussion. You can go a long way by using only the simplest properties of these components. If you want to implement a more sophisticated effect, you can look inside the Swing documentation. You will probably find the documentation quite intimidating at first glance, though. The purpose of this section is to show you how you can use the documentation to your advantage without becoming overwhelmed.

As an example, consider a program for mixing colors by specifying the red, green, and blue values. How can you specify the colors? Of course, you could supply three text fields, but sliders would be more convenient for users of your program (see Figure 13).

The Swing user-interface toolkit has a large set of user-interface components. How do you know if there is a slider? You can buy a book that illustrates all Swing components. Or you can run the sample application included in the Java Development Kit that shows off all Swing components (see Figure 14). Or you can look at the names of all of the classes that start with J and decide that JSlider may be a good candidate.

Note

You should learn to navigate the API documentation to find out more about user-interface components.

A Color Viewer

Figure 18.13. A Color Viewer

The SwingSet Demo

Figure 18.14. The SwingSet Demo

Next, you need to ask yourself a few questions:

  • How do I construct a JSlider?

  • How can I get notified when the user has moved it?

  • How can I tell to which value the user has set it?

When you look at the documentation of the JSlider class, you will probably not be happy. There are over 50 methods in the JSlider class and over 250 inherited methods, and some of the method descriptions look downright scary, such as the one in Figure 15. Apparently some folks out there are concerned about the valueIs-Adjusting property, whatever that may be, and the designers of this class felt it necessary to supply a method to tweak that property. Until you too feel that need, your best bet is to ignore this method. As the author of an introductory book, it pains me to tell you to ignore certain facts. But the truth of the matter is that the Java library is so large and complex that nobody understands it in its entirety, not even the designers of Java themselves. You need to develop the ability to separate fundamental concepts from ephemeral minutiae. For example, it is important that you understand the concept of event handling. Once you understand the concept, you can ask the question, "What event does the slider send when the user moves it?" But it is not important that you memorize how to set tick marks or that you know how to implement a slider with a custom look and feel.

Let us go back to our fundamental questions. In Java 6, there are six constructors for the JSlider class. You want to learn about one or two of them. You must strike a balance somewhere between the trivial and the bizarre. Consider

public JSlider()
     Creates a horizontal slider with the range 0 to 100 and an initial value of 50.

Maybe that is good enough for now, but what if you want another range or initial value? It seems too limited.

On the other side of the spectrum, there is

public JSlider(BoundedRangeModel brm)
     Creates a horizontal slider using the specified BoundedRangeModel.
A Mysterious Method Description from the API Documentation

Figure 18.15. A Mysterious Method Description from the API Documentation

Whoa! What is that? You can click on the BoundedRangeModel link to get a long explanation of this class. This appears to be some internal mechanism for the Swing implementors. Let's try to avoid this constructor if we can. Looking further, we find

public JSlider(int min, int max, int value)
     Creates a horizontal slider using the specified min, max, and value.

This sounds general enough to be useful and simple enough to be usable. You might want to stash away the fact that you can have vertical sliders as well.

Next, you want to know what events a slider generates. There is no addAction-Listener method. That makes sense. Adjusting a slider seems different from clicking a button, and Swing uses a different event type for these events. There is a method

public void addChangeListener(ChangeListener l)

Click on the ChangeListener link to find out more about this interface. It has a single method

void stateChanged(ChangeEvent e)

Apparently, that method is called whenever the user moves the slider. What is a ChangeEvent? Once again, click on the link, to find out that this event class has nomethods of its own, but it inherits the getSource method from its superclass Event-Object. The getSource method tells us which component generated this event, but we don't need that information—we know that the event came from the slider.

Now let's make a plan: Add a change event listener to each slider. When the slider is changed, the stateChanged method is called. Find out the new value of the slider. Recompute the color value and repaint the color panel. That way, the color panel is continually repainted as the user moves one of the sliders.

To compute the color value, you will still need to get the current value of the slider. Look at all the methods that start with get. Sure enough, you find

public int getValue()
     Returns the slider's value.
The Components of the ColorViewerFrame

Figure 18.16. The Components of the ColorViewerFrame

Classes of the Color Viewer Program

Figure 18.17. Classes of the Color Viewer Program

Now you know everything you need to write the program. The program uses one new Swing component and one event listener of a new type. After having mastered the basics, you may want to explore the capabilities of the component further, for example by adding tick marks—see Exercise P18.17.

Figure 16 shows how the components are arranged in the frame. Figure 17 shows the UML diagram.

ch18/slider/ColorViewer.java

1    import javax.swing.JFrame;
2
3    public class ColorViewer
4    {
5       public static void main(String[] args)
6       {
7          ColorViewerFrame frame = new ColorViewerFrame();
8          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
9          frame.setVisible(true);
10       }
11    }

ch18/slider/ColorViewerFrame.java

1  import java.awt.BorderLayout;
2  import java.awt.Color;
3  import java.awt.GridLayout;
4  import javax.swing.JFrame;
5  import javax.swing.JLabel;
6  import javax.swing.JPanel;
7  import javax.swing.JSlider;
8  import javax.swing.event.ChangeListener;
9  import javax.swing.event.ChangeEvent;
10
11 public class ColorViewerFrame extends JFrame
12 {
13    private static final int FRAME_WIDTH = 300;
14    private static final int FRAME_HEIGHT = 400;
15
16    private JPanel colorPanel;
17    private JSlider redSlider;
18    private JSlider greenSlider;
19    private JSlider blueSlider;
20
21    public ColorViewerFrame()
22    {
23       colorPanel = new JPanel();
24
25       add(colorPanel, BorderLayout.CENTER);
26       createControlPanel();
27       setSampleColor();
28       setSize(FRAME_WIDTH, FRAME_HEIGHT);
29    }
30
31    public void createControlPanel()
32    {
33       class ColorListener implements ChangeListener
34       {
35          public void stateChanged(ChangeEvent event)
36          {
37             setSampleColor();
38          }
39       }
40
41       ChangeListener listener = new ColorListener();
42
43       redSlider = new JSlider(0, 255, 255);
44       redSlider.addChangeListener(listener);
45
46       greenSlider = new JSlider(0, 255, 175);
47       greenSlider.addChangeListener(listener);
48
49       blueSlider = new JSlider(0, 255, 175);
50       blueSlider.addChangeListener(listener);
51
52       JPanel controlPanel = new JPanel();
53       controlPanel.setLayout(new GridLayout(3, 2));
54
55       controlPanel.add(new JLabel("Red"));
56       controlPanel.add(redSlider);
57
58       controlPanel.add(new JLabel("Green"));
59       controlPanel.add(greenSlider);
60
61       controlPanel.add(new JLabel("Blue"));
62       controlPanel.add(blueSlider);
63
64       add(controlPanel, BorderLayout.SOUTH);
65    }
66
67    /**
68       Reads the slider values and sets the panel to
69       the selected color.
70    */
71    public void setSampleColor()
72    {
73       // Read slider values
74
75       int red = redSlider.getValue();
76       int green = greenSlider.getValue();
77       int blue = blueSlider.getValue();
78
79       // Set panel background to selected color
80
81       colorPanel.setBackground(new Color(red, green, blue));
82        colorPanel.repaint();
83    }
84  }
Classes of the Color Viewer Program

14. Why does a slider emit change events and not action events?

Summary of Learning Objectives

Use text fields for reading text input.

  • Use JTextField components to provide space for user input. Place a JLabel next to each text field.

Use text areas for reading and displaying multi-line text.

  • Use a JTextArea to show multiple lines of text.

  • You can add scroll bars to any component with a JScrollPane.

Learn how to arrange multiple components in a container.

  • User-interface components are arranged by placing them inside containers. Containers can be placed inside larger containers.

  • Each container has a layout manager that directs the arrangement of its components.

  • When adding a component to a container with the border layout, specify the NORTH, EAST, SOUTH, WEST, or CENTER position.

  • The content pane of a frame has a border layout by default. A panel has a flow layout by default.

Select among the Swing components for presenting choices to the user.

  • For a small set of mutually exclusive choices, use a group of radio buttons or a combo box.

  • Add radio buttons into a ButtonGroup so that only one button in the group is on at any time.

  • You can place a border around a panel to group its contents visually.

  • For a binary choice, use a check box.

  • For a large set of choices, use a combo box.

  • Radio buttons, check boxes, and combo boxes generate action events, just as buttons do.

Implement menus in a Swing program.

  • A frame contains a menu bar. The menu bar contains menus. A menu contains submenus and menu items.

  • Menu items generate action events.

  • You should learn to navigate the API documentation to find out more about user-interface components.

Classes, Objects, and Methods Introduced in this Chapter

java.awt.BorderLayout                     javax.swing.JMenuBar
   CENTER                                    add
   EAST                                   javax.swing.JMenuItem
   NORTH                                  javax.swing.JRadioButton
   SOUTH                                  javax.swing.JScrollPane
   WEST                                   javax.swing.JSlider
java.awt.Container                           addChangeListener
   setLayout                                 getValue
java.awt.FlowLayout                       javax.swing.JTextArea
java.awt.Font                                 append
java.awt.GridLayout                       javax.swing.JTextField
javax.swing.AbstractButton                javax.swing.border.EtchedBorder
   isSelected                             javax.swing.border.TitledBorder
   setSelected                            javax.swing.event.ChangeEvent
javax.swing.ButtonGroup                   javax.swing.event.ChangeListener
   add                                       stateChanged
javax.swing.ImageIcon                     javax.swing.text.JTextComponent
javax.swing.JCheckBox                        getText
javax.swing.JComboBox                        isEditable
   addItem                                   setEditable
   getSelectedItem                           setText
   isEditable
   setEditable
javax.swing.JComponent
   setBorder
   setFont
javax.swing.JFrame
   setJMenuBar
javax.swing.JMenu
   add

Media Resources

  • • Lab Exercises

  • Media Resources
  • Media Resources

Review Exercises

R18.1 What is the difference between a label, a text field, and a text area?

R18.2 Name a method that is declared in JTextArea, a method that JTextArea inherits from JTextComponent, and a method that JTextArea inherits from JComponent.

R18.3 Can you use a flow layout for the components in a frame? If yes, how?

R18.4 What is the advantage of a layout manager over telling the container "place this component at position (x, y)"?

R18.5 What happens when you place a single button into the CENTER area of a container that uses a border layout? Try it out, by writing a small sample program, if you aren't sure of the answer.

R18.6 What happens if you place multiple buttons directly into the SOUTH area, without using a panel? Try it out, by writing a small sample program, if you aren't sure of the answer.

R18.7 What happens when you add a button to a container that uses a border layout and omit the position? Try it out and explain.

R18.8 What happens when you try to add a button to another button? Try it out and explain.

R18.9 The ColorViewerFrame uses a grid layout manager. Explain a drawback of the grid that is apparent from Figure 16 on page 767. What could you do to overcome this drawback?

R18.10 What is the difference between the grid layout and the grid bag layout?

R18.11 Can you add icons to check boxes, radio buttons, and combo boxes? Browse the Java documentation to find out. Then write a small test program to verify your findings.

R18.12 What is the difference between radio buttons and check boxes?

R18.13 Why do you need a button group for radio buttons but not for check boxes?

R18.14 What is the difference between a menu bar, a menu, and a menu item?

R18.15 When browsing through the Java documentation for more information about sliders, we ignored the JSlider constructor with no parameters. Why? Would it have worked in our sample program?

R18.16 How do you construct a vertical slider? Consult the Swing documentation for an answer.

R18.17 Why doesn't a JComboBox send out change events?

R18.18 What component would you use to show a set of choices, just as in a combo box, but so that several items are visible at the same time? Run the Swing demo application or look at a book with Swing example programs to find the answer.

R18.19 How many Swing user-interface components are there? Look at the Java documentation to get an approximate answer.

R18.20 How many methods does the JProgressBar component have? Be sure to count inherited methods. Look at the Java documentation.

Programming Exercises

P18.1 Write a graphical application front end for a bank account class. Supply text fields and buttons for depositing and withdrawing money, and for displaying the current balance in a label.

P18.2 Write a graphical application front end for an Earthquake class. Supply a text field and button for entering the strength of the earthquake. Display the earthquake description in a label.

P18.3 Write a graphical application front end for a DataSet class. Supply text fields and buttons for adding floating-point values, and display the current minimum, maximum, and average in a label.

P18.4 Write an application with three labeled text fields, one each for the initial amount of a savings account, the annual interest rate, and the number of years. Add a button "Calculate" and a read-only text area to display the result, namely, the balance of the savings account after the end of each year.

P18.5 In the application from Exercise P18.4, replace the text area with a bar chart that shows the balance after the end of each year.

P18.6 Write a program that contains a text field, a button "Add Value", and a component that draws a bar chart of the numbers that a user typed into the text field.

P18.7 Write a program that draws a clock face with a time that the user enters in two text fields (one for the hours, one for the minutes).

Hint: You need to determine the angles of the hour hand and the minute hand. The angle of the minute hand is easy: The minute hand travels 360 degrees in 60 minutes. The angle of the hour hand is harder; it travels 360 degrees in 12 × 60 minutes.

P18.8 Write an application with three buttons labeled "Red", "Green", and "Blue" that changes the background color of a panel in the center of the frame to red, green, or blue.

P18.9 Add icons to the buttons of Exercise P18.8.

P18.10 Write a calculator application. Use a grid layout to arrange buttons for the digits and for the + – × ÷ operations. Add a text field to display the result.

P18.11 Write an application with three radio buttons labeled "Red", "Green", and "Blue" that changes the background color of a panel in the center of the frame to red, green, or blue.

P18.12 Write an application with three check boxes labeled "Red", "Green", and "Blue" that adds a red, green, or blue component to the background color of a panel in the center of the frame. This application can display a total of eight color combinations.

P18.13 Write an application with a combo box containing three items labeled "Red", "Green", and "Blue" that changes the background color of a panel in the center of the frame to red, green, or blue.

P18.14 Write an application with a Color menu and menu items labeled "Red", "Green", and "Blue" that changes the background color of a panel in the center of the frame to red, green, or blue.

P18.15 Write a program that displays a number of rectangles at random positions. Supply buttons "Fewer" and "More" that generate fewer or more random rectangles. Each time the user clicks on "Fewer", the count should be halved. Each time the user clicks on "More", the count should be doubled.

P18.16 Modify the program of Exercise P18.15 to replace the buttons with a slider to generate fewer or more random rectangles.

P18.17 In the slider test program, add a set of tick marks to each slider that show the exact slider position.

P18.18 Enhance the font viewer program to allow the user to select different fonts. Research the API documentation to find out how to find the available fonts on the user's system.

Programming Projects

  • Project 18.1 Write a program that lets users design charts such as the following:

    Programming Projects

    Use appropriate components to ask for the length, label, and color, then apply them when the user clicks an "Add Item" button. Allow the user to switch between bar charts and pie charts.

  • Project 18.2 Write a program that displays a scrolling message in a panel. Use a timer for the scrolling effect. In the timer's action listener, move the starting position of the message and repaint. When the message has left the window, reset the starting position to the other corner. Provide a user interface to customize the message text, font, foreground and background colors, and the scrolling speed and direction.

Answers to Self-Check Questions

  1. Then the text field is not labeled, and the user will not know its purpose.

  2. Integer.parseInt(textField.getText())

  3. A text field holds a single line of text; a text area holds multiple lines.

  4. The text area is intended to display the program output. It does not collect user input.

  5. Don't construct a JScrollPane but add the resultArea object directly to the frame.

  6. First add them to a panel, then add the panel to the north end of a frame.

  7. Place them inside a panel with a GridLayout that has three rows and one column.

  8. If you have many options, a set of radio buttons takes up a large area. A combo box can show many options without using up much space. But the user cannot see the options as easily.

  9. When any of the component settings is changed, the program simply queries all of them and updates the label.

  10. To keep it from growing too large. It would have grown to the same width and height as the two panels below it.

  11. When you open a menu, you have not yet made a selection. Only JMenuItem objects correspond to selections.

  12. The parameter variable is accessed in a method of an inner class.

  13. JColorChooser.

  14. Action events describe one-time changes, such as button clicks. Change events describe continuous changes.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.152.139