Section 13.7 Checkboxes, Radio Buttons, and Borders
Suppose you are the software developer for your own software business specializing in computer games. You want to develop an applet-based order form that customers can use to order software over the Web. At the moment you have three software titles—a chess game, a checkers game, and a crossword puzzle game. The assumption is that the user will choose one or more of these titles from some kind of menu. The user must also indicate a payment option—either E-cash, credit card, or debit card. These options are mutually exclusive—the user can choose one and only one.
Let's design an applet interface for this program. Unlike the previous problem where the input was a numeric value, in this problem the input will be the user's selection from some kind of menu. The result will be the creation of an order. Let's suppose that this part of the task happens behind the scenes—that is, we don't have to worry about creating an actual order. The output the user sees will simply be an acknowledgment that the order was successfully submitted.
There are several kinds of controls needed for this interface. First, a conventional way to have users indicate their purchase decisions is to have them click a Submit button. They should also have the option to cancel the transaction at any time.
In addition to these button controls, a couple of menus must be presented, one for the software titles, and one for the payment choices. Swing and AWT libraries provide many options for building menus.
One key requirement for this interface is the mutually exclusive payment options. A conventional way to handle this kind of selection is with a JRadioButton
—a button that belongs to a group of mutually exclusive alternatives. Only one button from the group may be selected at one time. The selection of software titles could be handled by a collection of checkboxes. A JCheckbox
is a button that can be selected and deselected and that always displays its current state to the user. Using a checkbox will make it obvious to the user exactly what software has been selected.
To complete the design, let's use a JTextArea
again to serve as something of a printed order form. It will confirm the user's order and display other messages needed during the transaction.
Given these decisions, we arrive at the design shown in Figure 13.7.1. In this case, our design uses a JPanel
as the main container, instead of using the top window itself. The reason for this decision is that we want to use Swing Borders
around the various JPanel
s to enhance the overall visual appeal of the design. The borders will have titles that help explain the purpose of the various panels.
Note that the top-level window in this case is a JApplet
. By default it will have a border layout. For the main JPanel
we are using a \(3 \times 1\) GridLayout
. The components in the main panel are the JTextArea
and two other JPanel
s. The GridLayout
will take care of sizing these so they are all of equal size.
The center panel, which uses a flow layout, contains panels for the checkboxes and the radio buttons. These elements are grouped within their own panels. Again, we can put a border around them in the final implementation (Fig. 13.26). The button panels use a BoxLayout
, which we will discuss later. This design leads to the most complex containment hierarchy thus far.
Subsection 13.7.1 Checkbox and Radio Button Arrays
Because we will need three checkboxes, one for each title, and three radio buttons, one for each payment option, it will be useful again to use arrays to store both the buttons and their titles:
private ButtonGroup optGroup = new ButtonGroup();
private JCheckBox titles[] = new JCheckBox[NTITLES];
private JRadioButton options[] = new JRadioButton[NOPTIONS];
private String titleLabels[] =
{"Chess Master - \59.95", "Checkers Pro - \39.95",
"Crossword Maker - \$19.95"};
private String optionLabels[] = {"Credit Card",
"Debit Card", "E-cash"};
Again, the advantage of this design is that it simplifies the instantiation and initialization of the buttons: \marginfigvspace{-76pt}{chptr13/9f18.eps}{Borders around containers help make them stand out more.}{fig-acmescreen}
for(int k = 0; k < titles.length; k++) {
titles[k] = new JCheckBox(titleLabels[k]);
titles[k].addItemListener(this);
choicePanel.add(titles[k]);}
The only difference between this array of checkboxes and the keypad array of buttons that we used in the Converter
program is that checkboxes generate ItemEvent
s instead ActionEvent
s. Therefore, each checkbox must be registered with an ItemListener
(and, of course, the applet itself must implement the ItemListener
interface). We'll show how ItemEvent
s are handled later.
The code for instantiating and initializing the radio buttons is almost the same:
for(int k = 0; k < options.length; k++) {
options[k] = new JRadioButton(optionLabels[k]);
options[k].addItemListener(this);
optionPanel.add(options[k]);
optGroup.add(options[k] );}
options[0].setSelected(true); // Set first button 'on'
Radio buttons also generate ItemEvent
s, so they too must be registered with an ItemListener
. Note that the first button is set on, which represents a default payment option for the user.
The difference between checkboxes and radio buttons is that radio buttons must be added to a ButtonGroup
—here named optGroup
—in order to enforce mutual exclusion among them. A ButtonGroup
is an object whose sole task is to enforce mutual exclusion among its members. Whenever you click one radio button, the ButtonGroup
will automatically be notified of this event and will turn off whatever other button was turned on. As Figure 13.7.2 illustrates, radio buttons are monitored by two different objects, a ButtonGroup
, which manages the radio buttons' states, and an ItemListener
, which listens for clicks on the buttons and takes appropriate actions.
Note the effective division of labor in the design of the various objects to which a radio button belongs. The optionPanel
is a GUI component (a JPanel
) that contains the button within the visual interface. Its role is to help manage the graphical aspects of the button's behavior. The ButtonGroup
is just an Object
, not a GUI component. Its task is to monitor the button's relationship to the other buttons in the group. Each object has a clearly delineated task.
This division of labor is a key feature of object-oriented design. It is clearly preferable to giving one object broad responsibilities. For example, a less effective design might have given the task of managing a group of buttons to the JPanel
that contains them. However, this would lead to all kinds of problems, not least of which is the fact that not everything in the container belongs to the same button group. So a clear division of labor is a much preferable design.
Principle 13.7.3. EFFECTIVE DESIGN:Division of Labor.
In good object-oriented design, objects are specialists (experts) for very narrow, clearly defined tasks. If there's a new task that needs doing, design a new object to do it.
Subsection 13.7.2 Swing Borders
The Swing Border
and BorderFactory
classes can place borders around virtually any GUI element. Using borders is an effective way to make the grouping of components more apparent. Borders can have titles, which enhance the GUI's ability to guide and inform the user. They can also have a wide range of styles and colors, thereby helping to improve the GUI's overall appearance.
A border occupies some space around the edge of a JComponent
. For the Acme Software Titles interface, we place titled borders around four of the panels (Fig. 13.26). The border on the main panel serves to identify the company again. The one around the button panel serves to group the two control buttons. The borders around both the checkbox and the radio button menus help to set them apart from other elements of the display and help identify the purpose of the buttons.
Attaching a titled border to a component—in this case to a JPanel
—is very simple. It takes one statement:
choicePanel.setBorder(
BorderFactory.createTitledBorder("Titles"));
The setBorder()
method is defined in JComponent
, is inherited by all Swing components, and takes a Border
argument. In this case, we use the BorderFactory
class to create a border and assign it a title. There are several versions of the static createTitledBorder()
method. This version lets us specify the border's title. It uses default values for type of border (etched), the title's position (sitting on the top line), justification (left), and for font's type and color.
As you would expect, the Border
and BorderFactory
classes contain methods that let you exert significant control over the border's look and feel. You can even design and create your own custom borders.
Subsection 13.7.3 The BoxLayout
Manager
Another type of layout to use is the BoxLayout
. This can be associated with any container, and it comes as the default with the Swing Box
container. We use it in this example to arrange the checkboxes and radio buttons (Fig. Figure 13.7.1).
A BoxLayout
is like a one-dimensional grid layout. It allows multiple components to be arranged either vertically or horizontally in a row. The layout will not wrap around, as does the FlowLayout
. Unlike the GridLayout
, the BoxLayout
does not force all its components to be the same size. Instead, it tries to use each component's preferred width (or height) in arranging them horizontally (or vertically). (Every Swing component has a preferred size that is used by the various layout managers in determining the component's actual size in the interface.) The BoxLayout
manager also tries to align its components' heights (for horizontal layouts) or widths (for vertical layouts).
Once again, to set the layout manager for a container you use the setLayout()
method:
choicePanel.setLayout(new
BoxLayout(choicePanel,BoxLayout.Y_AXIS));
The BoxLayout()
constructor has two parameters. The first is a reference to the container that's being managed, and the second is a constant that determines whether horizontal (x-axis) or vertical (y-axis) alignment is used.
One nice feature of the BoxLayout
is that it can be used in combinations to imitate the look of the very complicated GridBoxLayout
. For example, Figure 12.2.4 shows an example with two panels (Panel1 and Panel2) arranged horizontally within an outer box (Panel0), each containing four components arranged
BoxLayout
.
Subsection 13.7.4 The ItemListener
Interface
In this section, we will describe how to handle menu selections. Whenever the user makes a menu selection, or clicks a check box or radio button, an ItemEvent
is generated. ItemEvent
s are associated with items that make up menus, including JPopupMenu
s, JCheckboxes
, JRadioButton
s, and other types of menus. Item events are handled by the ItemListener
interface, which consists of a single method, the itemStateChanged()
method:
public void itemStateChanged(ItemEvent e) {
display.setText("Your order so far (Payment by: ");
for (int k = 0; k < options.length; k++ )
if (options[k].isSelected())
display.append(options[k].getText() + ")\n");
for (int k = 0; k < titles.length; k++ )
if (titles[k].isSelected())
display.append("\t" + titles[k].getText() + "\n");} // itemStateChanged()
This version of the method handles item changes for both the checkbox menu and the radio buttons menu. The code uses two consecutive for loops. The first iterates through the options
menu (radio buttons) to determine what payment option the user has selected. Since only one option can be selected, only one title will be appended to the display. The second loop iterates through the titles menu (checkboxes) and appends each title the user selected to the display. This way the complete status of the user's order is displayed after every selection. The isSelected()
method is used to determine if a checkbox or radio button is selected or not.
In this example, we have no real need to identify the item that caused the event. No matter what item the user selected, we want to display the entire state of the order. However, like the ActionEvent class, the ItemEvent class contains methods that can retrieve the item that caused the event:
getItem(); // Returns a menu item within a menu
The getItem()
method is the ItemListener
's analogue to the ActionEvent
's getSource()
method. It enables you to obtain the object that generated the event but returns a representation of the item that was selected or deselected.
Subsection 13.7.5 The OrderApplet
The design of the OrderApplet
is summarized in Figure 13.29 and its complete implementation is given in Figure 13.7.4. There are several important points to make about this program. First, five JPanel
s are used to organize the components into logical and visual groupings. This conforms to the design shown in Figure 13.7.1.
Second, note the use of titled borders around the four internal panels. These help reinforce that the components within the border are related by function.
The applet init()
method is used to initialize the interface. This involves setting the layouts for the various containers and filling the containers with their components. Because their initializations are relatively long, the checkboxes and radio buttons are initialized in separate methods, the initChoices()
and initOptions()
methods, respectively.
Finally, note how the actionPerformed()
method creates a mock order form in the display area. This allows the user to review the order before it is submitted. Also note that the algorithm used for submittal requires the user to confirm an order before it is actually submitted. The first time the user clicks the Submit button, the button's label is changed to, “Confirm Order,” and the user is prompted in the display area to click the Confirm button to submit the order. This design allows the interface to catch inadvertent button clicks.
A user interface should anticipate errors by the user. When a program involves an action that can't be undone—such as placing an order—the program should make sure the user really wants to take the action before carrying it out.
Principle 13.7.6. EFFECTIVE DESIGN:Anticipate the User.
A well-designed interface should make it difficult for the user to make errors and should make it easy to recover from mistakes when they do happen.
Subsection 13.7.6 Self-Study Exercise
What's your favorite interface horror story? How would you have remedied the problem? The interface needn't be a computer interface.