Chapter 21

Checkboxes, Choices, and Lists


CONTENTS


This chapter covers the details of using the Checkbox, Choice, and List classes. It shows you how to create and use objects of these classes and how to create custom components that simplify the use of these GUI controls. When you finish this chapter, you will be able to effectively use checkboxes, radio buttons, choices, and lists in your Java window programs.

Using Checkboxes

Checkboxes are a common type of GUI control. They are typically used in form-like windows to simplify yes/no or true/false selections. The user checks a checkbox to indicate yes or true, and leaves it unchecked to indicate no or false.

The name of the Checkbox class is somewhat misleading in that Checkbox objects are used to implement traditional checkboxes as well as radio buttons.

Radio buttons are similar to traditional checkboxes in that they are in either an on or off state. They are different from traditional checkboxes in that only one radio button in a group may be on at a given time. They are aptly named after the buttons on a car radio, which can only be used to select a single radio channel at a given time.

When Checkbox objects are used as radio buttons, they are associated with a CheckboxGroup object that enforces mutual exclusion among the Checkbox objects in its group.

The CheckboxPanel Class

Checkboxes, like menus, are easy to use but tedious to construct and organize. The CheckboxPanel class provides a more convenient approach to creating and organizing checkboxes. (See Listing 21.1.) Typically, checkboxes are created in groups and organized in a panel that is given a title. The CheckboxPanel class provides a constructor for quickly creating objects of this type. It also provides access methods for getting and setting the value of an individual checkbox within the panel, based on the checkbox's label.


Listing 21.1. The CheckboxPanel class source code.

package jdg.ch21;

import java.awt.*;

public class CheckboxPanel extends Panel {
 public static int HORIZONTAL = 0;
 public static int VERTICAL = 1;
 public CheckboxPanel(String title,String labels[],int orientation) {
  super();
  int length = labels.length;
  if(orientation == HORIZONTAL) setLayout(new GridLayout(1,length+1));
  else setLayout(new GridLayout(length+1,1));
  add(new Label(title));
  for(int i=0;i<length;++i) add(new Checkbox(labels[i]));
 }
 public CheckboxPanel(String title,String labels[],boolean state[],
  int orientation) {
  super();
  int length = labels.length;
  if(orientation == HORIZONTAL) setLayout(new GridLayout(1,length+1));
  else setLayout(new GridLayout(length+1,1));
  add(new Label(title));
  for(int i=0;i<length;++i){
   Checkbox checkBox = new Checkbox(labels[i]);
   checkBox.setState(state[i]);
   add(checkBox);
 }
}
public boolean getState(String label) {
  Checkbox boxes[] = (Checkbox[])getComponents();
  for(int i=0;i<boxes.length;++i)
   if(label.equals(boxes[i].getLabel())) return boxes[i].getState();
  return false;
 }
 public void setState(String label,boolean state) {
  Checkbox boxes[] = (Checkbox[])getComponents();
  for(int i=0;i<boxes.length;++i)
   if(label.equals(boxes[i].getLabel())) boxes[i].setState(state);
  }
 }


Two CheckboxPanel constructors are provided. The first constructor uses a title string for the panel, an array of labels[] to be associated with checkboxes, and an orientation parameter that specifies whether the panel is to be organized in a vertical or horizontal fashion.

A GridLayout object is used to organize the Label and Checkbox objects placed within the panel. The title Label is added at the top of vertical panels and on the left side of horizontal panels. Then the checkboxes are created, one at a time, and fill in the rest of the panel.

The second constructor is similar to the first constructor, except that it uses an additional state[] array to set the initial state of the checkboxes that are added to the panel. The state of each checkbox is set using the setState() method of the Checkbox class.

The getState() method takes the label of a checkbox as its parameter and searches the checkboxes contained in the panel for one whose label matches the specified label. It then returns the state of this checkbox. If no matching checkbox is found, it returns false.

The setState() method is similar to the getState() method. It is used to update a checkbox with a given label.

Working with Radio Buttons

Radio buttons are created using the Checkbox class and are transformed from checkboxes into radio buttons when they are associated with a CheckboxGroup object. A CheckboxGroup can be assigned with the Checkbox constructor or using the setCheckboxGroup() method. Only one object in the checkbox group is allowed to be set at any given time.

The CheckboxGroupPanel Class

The CheckboxGroupPanel class extends the CheckboxPanel class to work with radio buttons. Its source code is shown in Listing 21.2.


Listing 21.2. The source code for the CheckboxGroupPanel class.

package jdg.ch21;

import java.awt.*;

public class CheckboxGroupPanel extends CheckboxPanel {
 public CheckboxGroupPanel(String title,String labels[],int orientation) {
  super(title,labels,orientation);
  putInGroup();
 }
 public CheckboxGroupPanel(String title,String labels[],boolean state[],
  int orientation) {
  super(title,labels,state,orientation);
  putInGroup();
 }
 void putInGroup() {
  Component components[] = getComponents();
  int length = components.length;
  CheckboxGroup group = new CheckboxGroup();
  for(int i=1;i<length;++i){
   Checkbox checkBox = (Checkbox) components[i];
   checkBox.setCheckboxGroup(group);
  }
 }
}


The Checkbox panel constructors are overridden to place the checkboxes in the panel in a single group. If the second constructor is used, only one checkbox should be specified as being in the on state.

The putInGroup() method uses the getComponents() method inherited from the Container class to create an array of the components contained in the panel. It creates a CheckboxGroup object and then indexes through the array, putting all checkboxes into this group using the setCheckboxGroup() method. The first component is skipped because it is the title of the panel.

The CheckboxApp Program

The CheckboxApp program illustrates the use of the CheckboxPanel and CheckboxGroupPanel classes. Its source code is shown in Listing 21.3.


Listing 21.3. The source code for the CheckboxApp program.

import java.awt.*;
import jdg.ch20.MyMenu;
import jdg.ch20.MyMenuBar;
import jdg.ch20.MessageDialog;
import jdg.ch21.CheckboxPanel;
import jdg.ch21.CheckboxGroupPanel;

public class CheckboxApp extends Frame {
 MyMenuBar menuBar;
 MessageDialog dialog;
 CheckboxPanel checkboxPanel;
 CheckboxGroupPanel checkboxGroupPanel;
 public static void main(String args[]){
  CheckboxApp app = new CheckboxApp();
 }
 public CheckboxApp() {
  super("CheckboxApp");
  setup();
  pack();
  resize(minimumSize());
  show();
 }
 void setup() {
  setBackground(Color.white);
  setupMenuBar();
  setupCheckboxes();
 }
 void setupMenuBar(){
  Object menuItems[][] = {
   {"File","Exit"},
  };
  menuBar = new MyMenuBar(menuItems);
  setMenuBar(menuBar);
 }
 void setupCheckboxes(){
  setLayout(new GridLayout(1,2));
  String sports[] = {"Baseball","Basketball","Football","Hockey","Soccer"};
  checkboxPanel = new CheckboxPanel("What team sports do you like? ",
   sports,CheckboxPanel.VERTICAL);
  add(checkboxPanel);
  String ages[] = {"under 20","20 - 39","40 - 59","60 - 79","80 and over"};
  checkboxGroupPanel = new CheckboxGroupPanel("What is your age? ",
   ages,CheckboxPanel.VERTICAL);
  add(checkboxGroupPanel);
 }
 public boolean handleEvent(Event event) {
  if(event.id==Event.WINDOW_DESTROY){
   if(event.target instanceof MessageDialog) return true;
   System.exit(0);
   return true;
  }else if(event.id==Event.ACTION_EVENT){
   if(event.target instanceof MenuItem){
    if("Exit".equals(event.arg)){
     System.exit(0);
     return true;
    }
   }else if(event.target instanceof Checkbox){
    String status;
    Checkbox checkbox = (Checkbox)event.target;
    if(checkbox.getState()) status = "You checked: ";
    else status = "You unchecked: ";
    String text[] = {status+checkbox.getLabel()+" "};
    String buttons[] = {"OK"};
    dialog = new MessageDialog(this,"Guess what?",true,text,buttons);
    dialog.show();
    return true;
   }
  }
  return false;
 }
}


When you execute the program, it displays the window shown in Figure 21.1. The left side of the window displays a CheckboxPanel object, and the right side displays a CheckboxPanelGroup object. Notice that traditional checkboxes are displayed on the left, and radio buttons are displayed on the right.

Figure 21.1 : The CheckboxApp opening window.

Click on the Basketball checkbox, as shown in Figure 21.2. The checkbox is checked and the dialog box shown in Figure 21.3 is displayed as the result of handling this event. Click on the Basketball checkbox again, and the dialog box shown in Figure 21.4 is displayed. The purpose of this type of dialog box is to illustrate checkbox event handling. It is not normally part of any application that uses checkboxes because the checkbox display indicates the state of a checkbox.

Figure 21.2 : Checking Basketball.

Figure 21.3 : You checked Basketball

Figure 21.4 : You unchecked Basketball.

Go ahead and check your favorite sports, and then turn your attention to the radio buttons. Select your age group, as shown in Figure 21.5. The program notifies you of your selection. Go ahead and select another age group, as shown in Figure 21.6. Notice that you can't select more than one age group at a given time. That's the idea behind radio buttons.

Figure 21.5 : Select your age group

Figure 21.6 : Now select a different age group

The CheckboxApp program makes use of several custom components that you've built so far, including the MyMenu, MyMenuBar, MessageDialog, CheckboxPanel, and CheckboxGroupPanel classes. Try rewriting this program without using these classes and you'll find out how time- consuming and tedious it can be to write programs without custom classes. You should now be getting used to using this class-building approach to simplify your Java programming.

The setupCheckboxes() method sets up the checkbox and radio button panels displayed in the previous figures. A GridLayout object is used to organize the main application window. The checkboxPanel variable is assigned the CheckboxPanel object that is created using the sports[] array, and the checkboxGroupPanel variable is assigned the CheckboxGroupPanel object that is created using the ages[] array. Both panels are then added to the Frame object being constructed. That's all the code required to create the GUI controls shown in Figure 21.1.

The handleEvent() method looks to see if the target of an ACTION_EVENT is an instance of the Checkbox class. This will be true for both checkboxes and radio buttons. It then retrieves the checkbox from the event.target variable and uses the getState() method to obtain the state of the Checkbox object. Then it retrieves the label of the Checkbox object using the getLabel() method and passes this information to the user via a MessageDialog object.

Making Choices

The Choice class allows Motif-style choice lists to be used in Java window programs. These GUI controls are also supported in Windows 95 and NT programs. A choice list is a pull-down menu that allows a user to select a single item from a list. When a choice selection is made, an ACTION_EVENT is generated, and the program is then able to respond to that selection. Choices are like menus that are placed in the middle of a window.

The MyChoice Class

MyChoice is a short and sweet class that simplifies the construction of a Choice object. Its source code is shown in Listing 21.4.


Listing 21.4. The source code for the MyChoice class.

package jdg.ch21;

import java.awt.*;

public class MyChoice extends Choice {
 public MyChoice(String labels[]) {
  super();
  int length = labels.length;
  for(int i=0;i<length;++i) {
   try {
    addItem(labels[i]);
   }catch (NullPointerException ex) {
    addItem("");
   }
  }
 }
}


Rather than constructing a Choice object and adding all of the items in the choice list, the MyChoice constructor takes an array of labels and adds them to the Choice object as it is constructed. The addItem() method of the Choice class throws the NullPointerException and is handled by adding a blank item to the choice list when a null pointer is encountered.

Selecting from Lists

The List class is a tad more sophisticated that the Choice class. It is similar in that it allows a user to select from a list of items that are displayed in a window component. It is different because it provides the capability to support multiple menu selections, to specify the size of the list window, and to dynamically update the list during program execution.

The List class provides two constructors. The default constructor takes no parameters. The second constructor specifies the number of visible rows to be identified and whether multiple selections are allowed.

The access methods supported by the List class are also more extensive than the Choice class. In particular, the delItem(), clear(), and replaceItem() methods allow List objects to be dynamically updated.

The MyList Class

The MyList class is very similar to the MyChoice class in that it enables a list to be constructed using an array of list items. (See Listing 21.5.) The MyList constructor also allows the number of rows displayed in the list and the multiple-selection parameter to be specified. The MyList constructor sets the foreground and background colors of the list box that is displayed. This is used to override any colors that might be set for the foreground and background of the list's container.


Listing 21.5. The source code for the MyList class.

package jdg.ch21;

import java.awt.*;

public class MyList extends List {
 public MyList(int rows,boolean multiple,String labels[]) {
  super(rows,multiple);
  int length = labels.length;
  for(int i=0;i<length;++i) {
   try {
    addItem(labels[i]);
   }catch (NullPointerException ex) {
    addItem("");
   }
  }
  setBackground(Color.white);
  setForeground(Color.black);
 }
}


The ChoiceListApp Program

The ChoiceListApp program illustrates the use of the MyChoice and MyList classes. (See Listing 21.6.) It provides the capability to decide what you want to eat for your next meal and can be very handy when a moment of indecision arrives. The following source code lists only a basic set of menu items, but additional menu items can be added easily.


Listing 21.6. The source code for the ChoiceListApp program.

import java.awt.*;
import jdg.ch20.MyMenu;
import jdg.ch20.MyMenuBar;
import jdg.ch21.MyChoice;
import jdg.ch21.MyList;

public class ChoiceListApp extends Frame {
 MyMenuBar menuBar;
 MyChoice mealChoice;
 MyList currentList;
 MyList mealList[];
 String meals[] = {"Breakfast","Lunch","Dinner"};
 String mealChoices[][] = {
  {"pancakes","eggs","bacon","ham","sausage","cereal",
   "toast","coffee","juice"},
  {"pizza","hamburger","hot dog","burrito","salad","fries",
   "chips","soda","milk"},
  {"spaghetti","carne asada","barbequed chicken","soup","salad",
   "bread","wine","beer","soda","milk"}
 };
 TextField text;
 public static void main(String args[]){
  ChoiceListApp app = new ChoiceListApp();
 }
 public ChoiceListApp() {
  super("ChoiceListApp");
  setup();
  pack();
  resize(275,175);
  show();
 }
 void setup() {
  setBackground(Color.white);
  setupMenuBar();
  setupChoice();
  setupLists();
  text = new TextField(40);
  add("North",new Label("Place your order:"));
  add("South",text);
  add("West",mealChoice);
  currentList = mealList[0];
  add("East",currentList);
 }
 void setupMenuBar(){
  Object menuItems[][] = {
   {"File","Exit"},
  };
  menuBar = new MyMenuBar(menuItems);
  setMenuBar(menuBar);
 }
 void setupChoice(){
  mealChoice = new MyChoice(meals);
 }
 void setupLists(){
  mealList = new MyList[meals.length];
  for(int i=0;i<meals.length;++i)
   mealList[i] = new MyList(5,true,mealChoices[i]);
 }
 public boolean handleEvent(Event event) {
  if(event.id==Event.WINDOW_DESTROY){
   System.exit(0);
   return true;
  }else if(event.id==Event.ACTION_EVENT){
   if(event.target instanceof MenuItem){
    if("Exit".equals(event.arg)){
     System.exit(0);
     return true;
    }
   }else if(event.target instanceof Choice){
    for(int i=0;i<meals.length;++i)
     if(meals[i].equals(event.arg)){
      remove(currentList);
      currentList = mealList[i];
      add("East",currentList);
      text.setText(meals[i]);
    }
   show();
   return true;
  }else if(event.target instanceof List){
   updateTextField();
   return true;
  }
 }else if(event.id==Event.LIST_SELECT || event.id==Event.LIST_DESELECT){
  updateTextField();
  return true;
 }
return false;
}
void updateTextField() {
 String order = mealChoice.getSelectedItem()+": ";
 String items[] = currentList.getSelectedItems();
 for(int i=0;i<items.length;++i) order += items[i]+" ";
 text.setText(order);
 }
}


Make sure that you have food on hand when you run the ChoiceListApp program. Its opening window is shown in Figure 21.7.

Figure 21.7 : The ChoiceListApp opening window.

The choice list shown on the left side of the window is used to select a meal. This selection determines what menu items are displayed in the list shown on the right side of the window. More than one item can be selected from the entrée list. The text field on the bottom of the screen identifies the selections that you have made. Go ahead and select Lunch from the choice list, as shown in Figure 21.8. Notice that the entrée list is updated with some typical lunch items. The text field tells you that you are now ordering lunch.

Figure 21.8 : Selecting lunch.

Go ahead and select some menu items from the entrée list. They are displayed in the text field, as shown in Figure 21.9.

Figure 21.9 : Ordering lunch.

Now select Dinner from the choice list and select some dinner entrées, as shown in Figure 21.10. The text field is updated to list your new selections.

Figure 21.10 : Selecting another meal.

The ChoiceListApp program declares several field variables. The menuBar variable is the now-standard variable used to identify the program's menu bar. The mealChoice variable is used to refer to the MyChoice object that displays the meals identified in the meals[] array. Two MyList variables are declared. The mealList[] array holds the three MyList objects used for breakfast, lunch, and dinner. These items are stored in the mealChoices[] array. The currentList variable points to the current menu entrée list being displayed. The text variable refers to the TextField object displayed at the bottom of the window.

The main() method and ChoiceListApp() constructor follow the pattern that's been developed so far. The window is resized to make its display more visually appealing. A GridBagLayout layout would have been more appropriate for this type of application, but the overhead of implementing it would obscure the important points associated with the MyChoice and MyList classes.

The setup() method sets up the background and menu bar and then invokes the setupChoice() and setupLists() methods to set up the MyChoice and MyList objects. The text field is initialized to be 40 characters wide, and then the user-interface objects are placed in the appropriate places in the ChoiceListApp window.

The setupChoice() method constructs the mealChoice object using a simple, one-line constructor. The setupLists() method sets up the mealList object by indexing through the mealChoices[] array and setting up the individual MyList objects.

The handleEvent() method looks for events of type ACTION_EVENT that are instances of the Choice class. It handles them by finding the element of the meals[] array that was selected and using it to select the next mealList[] element to be displayed as the currentList. The old list is removed, and a new currentList object is added. The text field is then set to the meals[] element that was selected. Note that this approach of removing the list and replacing it with a new one is less elegant than updating the list as it is displayed.

The handleEvent() method processes ACTION_EVENT, which occurs when the user double-clicks on a list item. It invokes the updateTextField() method to update the text that is displayed in the text field at the bottom of the window's display. It also handles the LIST_SELECT and LIST_DESELECT events in the same fashion.

The updateTextField() method retrieves the currently selected item in the MyChoice object referred to by mealChoice using the getSelectedItem() method of the Choice class. This String object is used to build the text display by identifying the currently selected meal. The getSelectedItems() method of the List class is used to return a list of all items selected in the currently displayed MyChoice object referred to by the currentList variable. These items are then added to the text field display.

Summary

This chapter shows you how to use the Checkbox, Choice, and List classes. It describes their available constructors and access methods and shows you how to use them as the basis for creating custom GUI components. Chapter 22, "Text and Fonts," shows you how to work with these features.