'; zhtm += ''; zhtm += '

' + pPage + ''; zhtm += '
'; window.popUpWin.document.write(zhtm); window.popUpWin.document.close(); // Johnny Jackson 4/28/98 } //--> Java 1.2 Unleashed -- Ch 6 -- GUI Building


Java 1.2 Unleashed

Contents


- 6 -

GUI Building


One of the reasons for Java's massive appeal is the Abstract Windowing Toolkit (AWT). This API allows powerful graphical user interfaces to be developed quickly and easily. If you have ever programmed in Microsoft Windows, you will greatly appreciate the simplicity and efficiency of the GUI-programming capabilities of the AWT. Also, most of its classes and interfaces can be used for both applets and applications.

In this chapter, you'll learn how to build a GUI using the classes of the AWT. You'll work with labels, buttons, text fields, checkboxes, choices, lists, and scrollbars. You'll learn how to use components and containers, and how to handle events. When you finish this chapter, you'll be able to build a basic GUI using the AWT classes.


NOTE: Part 4 of this book introduces Swing programming. Swing extends the AWT to provide numerous components for GUI building and to support pluggable look and feel. Many of the AWT components that you'll learn about in this chapter have Swing counterparts.

Labels

The most basic GUI component is the label, which is simply text that is displayed at a particular location of a GUI container. The Label class of the java.awt package provides the capability to work with labels. This class provides three Label() constructors: a default parameterless constructor for creating blank Label objects, a constructor that takes a String object that specifies the label's text, and a constructor that takes a String object (the label's text) and an alignment constant. The alignment constant may be one of the following:

The alignment constants determine the justification of text within the label. The Label class provides six methods for working with the label's text and alignment, and for performing other operations. The setText() and getText() methods are used to access the label's text. You'll see an example of using labels in the section "Handling Events," later in this chapter.

Buttons

Buttons are another fundamental GUI component. Unlike labels, which are used solely to provide information to the user, buttons allow users to interact with your applets and applications. The clicking of a button triggers an event that can be handled by your GUI. You'll learn about event handling in the next section.

The Button class of java.awt provides the capability to use labeled buttons in your GUI. The Button class has two constructors: a default parameterless constructor that creates unlabeled buttons, and a constructor that takes a String object (the button's label) as a parameter. The Button class provides 10 methods for getting and setting the buttons label and handling button-related events. These methods are as follows:

Most of these methods are used for handling button-related events. The following section provides an introduction to Java event handling.

Handling Events

The user communicates with window programs by performing actions such as clicking a mouse button or pressing a key on the keyboard. These actions result in the generation of events. The process of responding to an event is known as event handling. Window programs are said to be event-driven because they operate by performing actions in response to events.

The JDK 1.02 supported an approach to event handling referred to as an inheritance model. In this approach, events are handled by subclassing window components and overriding their action() and handleEvent() methods. These methods return true or false to indicate whether they have successfully handled an event. If a true value is returned, the event processing is complete. Otherwise, the event is sent to the object's container for further processing.

The JDK 1.02 approach to event handling was replaced by an event delegation model in JDK 1.1; however, the old inheritance event model was still supported. The event delegation model provides the capability to deliver events to specific objects--a capability that was lacking in JDK 1.02. The event delegation approach is less complex and more efficient. It uses special classes, called adapter classes, whose objects listen for the occurrence of events on behalf of objects of other classes.


NOTE: The JDK 1.02 event model is being phased out, and you should no longer use it. JDK 1.2 relies heavily on the JDK 1.1 event delegation model. When you study Swing in Part 4, you'll find that it relies entirely on the event delegation model, which wasn't frozen in JDK 1.1. In fact, there are new event classes that have been added in JDK 1.2--especially in Swing. However, the term "JDK 1.1 event model" is often used to refer to the event delegation model, since it was introduced with JDK 1.1.

In the event delegation model, event handling is delegated to specific event handling adapter classes. In this model, a source object generates events that are listened for by a listener object. The source object is usually a window GUI component, such as a button. The listener object is an adapter class that implements an event listener interface. The source object provides methods that allow listener objects to register themselves to listen for its events. For example, an object that handles the clicking of a button implements the ActionListener interface. This object is registered with a particular button via the button's addActionListener()method.


NOTE: Many of the adapter classes are provided in the java.awt.event package. These classes can be extended to support custom event handling.

The JDK 1.1 event model is a significant improvement over that of Java 1.02, which required GUI components to be subclassed in order to handle events. Although it is still possible to do so in JDK 1.1, it is no longer necessary. Events can be handled by adapter classes that are separate from the component from which the event is generated.

The following subsections describe the classes and interfaces used for JDK 1.02 event handling, followed by those used for JDK 1.1 event handling.

JDK 1.02 Event Handling

In the JDK 1.02 inheritance model, the Event class encapsulates all Windows event processing. The Event class defines the entire list of events handled by window programs using class constants. These constants are used to identify the events that are passed to event-handling methods. You can review the Java API description of the Event class to familiarize yourself with these constants.

The Event class provides three constructors for creating events, but you probably won't need to use these constructors because events are internally generated by the Java runtime system in response to user interface actions. The Event class also provides methods for determining whether the Ctrl, Shift, or Meta (Alt) keys were pressed during the generation of an event.

JDK 1.1 Event Handling

In the JDK 1.1 event delegation model, the java.util.EventObject class is the top-level class of an event hierarchy. This class provides a source field variable to identify the object that is the source of an event, a getSource() method to retrieve this object, and a toString() method to convert an event into a String representation. It provides a single constructor that takes the object that is the source of the event as an argument.

The java.awt.AWTEvent class extends the java.util.EventObject class to support AWT events. It provides several variables, constants, and methods that are used to identify events and determine whether they are consumed. The AWTEvent class is extended by the following classes of java.awt.event:

The ComponentEvent class is further extended by the following classes:

FocusEvent--Generated by a change in the status of a component's input focus.

The AWTEvent class and its subclasses allow window-related events to be directed to specific objects that listen for those events. These objects implement EventListener interfaces. The java.util.EventListener interface is the top-level interface of the event listener hierarchy. It is an interface in name only because it does not define any constants or methods. It is extended by the following interfaces of java.awt.event:

As a convenience, the java.awt.event package provides adapter classes that implement the event listener interfaces. These classes may be subclassed to override specific event handling methods of interest. The adapter classes of java.awt.event are as follows:

These adapter classes are convenience classes, in that they provide stubs for the methods of the interfaces that you don't want to implement yourself.

The java.awt.EventQueue class supports the queuing of events. It allows event listeners to monitor the queue and retrieve specific events for processing.

The java.awt.AWTEventMulticaster class provides the capability to listen for multiple events and then forward them to multiple event listeners. It provides a thread-safe mechanism by which event listeners can be added and removed from its event listening destination list.

The Buttons Applet

The Buttons applet, shown in Listing 6.1, ties together what you've learned about the Label and Button classes and AWT event handling. Listing 6.2 shows an HTML file that can be used to run the Buttons applet with the appletviewer tool.

The Buttons applet declares and constructs a Label object called "Default Label" and assigns the object to the label variable. It then creates three Button objects, labeled "One," "Two," and "Three," and assigns them to the button1, button2, and button3 variables. It then creates two Panel objects and assigns them to the panel1 and panel2 variables. Panel objects are objects that are used to contain and organize other GUI objects. You'll learn about them in the section "Components and Containers," later in this chapter.

The init() method sets the layout for the applet to an object of the BorderLayout class. Layouts are used to determine how GUI objects are displayed within a container. You'll learn about them later in this chapter in the section "Using Layouts." The Applet class is a subclass of the Panel class. This allows applets to act as containers.

The add() method of the Panel object referenced by panel1 is invoked to add the label to the panel. The addActionLister() method is used to set up objects of the ButtonHandler class to handle the events associated with the three buttons. The buttons are then added to the second panel. The first panel is then added to the top (north) part of the applet display area, and the second panel is added to the center of the applet display area.

The ButtonHandler class is an inner class of the Buttons class. It is used to handle events associated with the clicking of the applet's buttons. It implements the actionPerformed() method of the ActionListener interface. The actionPerformed() method is invoked to handle the clicking of the buttons for which the ButtonHandler objects are registered. It uses the getActionCommand() method to get the label of the button that was clicked. It then invokes the setText() method to set the label of the Label object to the button's label.

Figure 6.1 shows how the Buttons applet is displayed. When you click any of the three buttons, the label is updated to identify the button that was last clicked, as shown in Figure 6.2.

FIGURE 6.1. The initial display of the Buttons applet.

FIGURE 6.2. The label is updated to identify the button that was clicked.

LISTING 6.1. THE Buttons APPLET.

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class Buttons extends Applet {
 Label label = new Label("Default Label");
 Button button1 = new Button("One");
 Button button2 = new Button("Two");
 Button button3 = new Button("Three");
 Panel panel1 = new Panel();
 Panel panel2 = new Panel();
 public void init() {
  setLayout(new BorderLayout());
  panel1.add(label);
  button1.addActionListener(new ButtonHandler());
  button2.addActionListener(new ButtonHandler());
  button3.addActionListener(new ButtonHandler());
  panel2.add(button1);
  panel2.add(button2);
  panel2.add(button3);
  add("North",panel1);
  add("Center",panel2);

}

 class ButtonHandler implements ActionListener {
  public void actionPerformed(ActionEvent e){
   String s = e.getActionCommand();
   label.setText(s);
  }
 }

}

LISTING 6.2. AN HTML FILE FOR RUNNING THE Buttons APPLET.

<HTML>
<HEAD>
<TITLE>Labels, Buttons, and Events</TITLE>
</HEAD>
<BODY>
<APPLET CODE="Buttons.class" WIDTH=400 HEIGHT=250>
</APPLET>
</BODY>

</HTML>

Components and Containers

The Component class is the superclass of the set of AWT classes that implement graphical user interface controls. These components include windows, dialog boxes, buttons, labels, text fields, and other common GUI components. The Component class provides a common set of methods that are used by all these subclasses, including methods for working with event handlers, images, fonts, and colors. More than 100 methods are implemented by this class. It is a good idea to browse the API pages of the Component class to get a feel for the kinds of methods that are available.

Although Component contains many GUI-related subclasses, its Container subclass is used to define components that can contain other components. It provides methods for adding, retrieving, displaying, counting, and removing the components that it contains. The Container class also provides methods for working with layouts. The layout classes control the layout of components within a container.

The Container class has three major subclasses: Window, Panel, and ScrollPane. Window provides a common superclass for application main windows (Frame objects) and Dialog windows. You'll learn about these classes in Chapter 9, "Creating Window Applications." The Panel class is a generic container that can be displayed within an applet or window. It is subclassed by the java.applet.Applet class as the base class for all Java applets. The ScrollPane class is a scrollable container that can have vertical and horizontal scrollbars.

In the Buttons applet, we used two Panel objects to act as containers for the Label and Button objects that were displayed as part of the applet's GUI.

Using Layouts

The method by which the components of a Container object are organized is determined by an object that implements the LayoutManager interface. The layout of a Container is specified using the setLayout() method of the Container class. It passes an object that implements the LayoutManager interface as a parameter. For example, in the Buttons applet, the applet's layout was set as an object of the BorderLayout class.

The LayoutManager and LayoutManager2 Interfaces

The LayoutManager interface provides a set of methods that are implemented by classes that control the layout of a container. These methods include those that add or remove components from a layout, specify the size of the container, and lay out the components of the container. The LayoutManager2 interface extends the LayoutManager interface to deal with constraint-based layouts.

The BorderLayout Class

The BorderLayout class is used to lay out the GUI components contained in a Container object. It lays out components along the north, south, east, and west borders of the container and in the center of the container. The center component gets any space left over from the north, south, east, and west border components. It is the default layout for Window, Frame, and Dialog objects and provides the capability to specify the horizontal and vertical gap between the laid-out components and the container.

The CardLayout Class

The CardLayout class is used to lay out the components of a Container object in the form of a deck of cards in which only one card is visible at a time. The class provides methods that are used to specify the first, last, next, and previous components in the container.

The FlowLayout Class

The FlowLayout class is used to lay out the components of a Container object in a left-to-right, top-to-bottom fashion. It is the default layout used with Panel objects. It allows the alignment of the components it lays out to be specified by the LEFT, CENTER, and RIGHT constants.

The GridLayout Class

The GridLayout class is used to lay out the components of a Container object in a grid in which all components are the same size. The GridLayout constructor is used to specify the number of rows and columns of the grid.

The GridBagLayout Class

The GridBagLayout class lays out the components of a Container object in a grid-like fashion in which some components may occupy more than one row or column. The GridBagConstraints class is used to identify the positioning parameters of a component that is contained within an object laid out using a GridBagLayout. The Insets class is used to specify the margins associated with an object that is laid out using a GridBagLayout object. Refer to the API description of the GridBagLayout class for more information on how to use this layout.

The Layouts Applet

The Layouts applet, shown in Listing 6.3, provides an example of using containers and layouts. This applet is run using the HTML file provided in Listing 6.4. When you run the Layouts applet using appletviewer, it displays the opening window shown in Figure 6.3. You can click the Border, Flow, Card, Grid, and GridBag buttons in the top panel to change the layout displayed in the panel below it. Figures 6.4 through 6.7 show the other layouts that are displayed. When you select the Card layout, a bottom panel is displayed (see Figure 6.4) that contains the First, Last, Next, and Previous buttons. These buttons are used to move through the card deck of buttons displayed in the middle panel. Play with the applet to familiarize yourself with its operation before going on to the next section, which describes how the Layouts applet works.

FIGURE 6.3. An example of a BorderLayout.

FIGURE 6.4. An example of a CardLayout.

FIGURE 6.5. An example of a FlowLayout.

FIGURE 6.6. An example of a GridLayout.

FIGURE 6.7. An example of a GridBagLayout.

LISTING 6.3. THE Layouts APPLET.

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class Layouts extends Applet {
 Panel[] panels;
 Panel currentPanel;
 static int border=0;
 static int card=1;
 static int flow=2;
 static int grid=3;
 static int gridBag=4;
 String[] layouts = {"Border","Card","Flow","Grid","GridBag"};
 String[] cards = {"First","Last","Next","Previous"};
 Button[] layoutButtons = new Button[layouts.length];
 Button[] navigateButtons = new Button[cards.length];
 Panel layoutButtonPanel = new Panel();
 Panel navigateButtonPanel = new Panel();
 public void init(){
  setLayout(new BorderLayout());
  setupButtons();
  add("North",layoutButtonPanel);
  setupDisplayPanels();
 }
 void setupButtons() {
  for(int i=0;i<layouts.length;++i) {
   layoutButtons[i] = new Button(layouts[i]);
   layoutButtons[i].addActionListener(new ButtonHandler());
   layoutButtonPanel.add(layoutButtons[i]);
  }
  for(int i=0;i<cards.length;++i) {
   navigateButtons[i] = new Button(cards[i]);
   navigateButtons[i].addActionListener(new ButtonHandler());
   navigateButtonPanel.add(navigateButtons[i]);
  }
 }
 void setupDisplayPanels() {
  panels = new Panel[5];
  for(int i=0;i<5;++i) panels[i]=new Panel();
  panels[border].setLayout(new BorderLayout());
  panels[card].setLayout(new CardLayout());
  panels[flow].setLayout(new FlowLayout());
  panels[grid].setLayout(new GridLayout(2,3));
  GridBagLayout gridBagLayout = new GridBagLayout();
  panels[gridBag].setLayout(gridBagLayout);
  panels[border].add("North",new Button("North"));
  panels[border].add("South",new Button("South"));
  panels[border].add("East",new Button("East"));
  panels[border].add("West",new Button("West"));
  panels[border].add("Center",new Button("Center"));
  String cardButtons[] = {"First","Second","Third","Fourth","Last"};
  String flowButtons[] = {"One","Two","Three","Four","Five"};
  String gridButtons[] = {"(0,0)","(1,0)","(2,0)","(0,1)","(1,1)","(2,1)"};
  for(int i=0;i<cardButtons.length;++i)
   panels[card].add("next card",new Button(cardButtons[i]));
  for(int i=0;i<flowButtons.length;++i)
   panels[flow].add(new Button(flowButtons[i]));
  for(int i=0;i<gridButtons.length;++i)
   panels[grid].add(new Button(gridButtons[i]));
  Button gridBagButtons[] = new Button[9];
  for(int i=0;i<9;++i) gridBagButtons[i] = new Button("Button"+i);
  int gridx[] = {0,1,2,0,2,0,1,1,0};
  int gridy[] = {0,0,0,1,1,2,2,3,4};
  int gridwidth[] = {1,1,1,2,1,1,1,2,3};
  int gridheight[] = {1,1,1,1,2,2,1,1,1};
  GridBagConstraints gridBagConstraints[] = new GridBagConstraints[9];
  for(int i=0;i<9;++i) {
   gridBagConstraints[i] = new GridBagConstraints();
   gridBagConstraints[i].fill=GridBagConstraints.BOTH;
   gridBagConstraints[i].gridx=gridx[i];
   gridBagConstraints[i].gridy=gridy[i];
   gridBagConstraints[i].gridwidth=gridwidth[i];
   gridBagConstraints[i].gridheight=gridheight[i];
   gridBagLayout.setConstraints(gridBagButtons[i],gridBagConstraints[i]);
   panels[gridBag].add(gridBagButtons[i]);
  }
  add("Center",panels[border]);
  currentPanel=panels[border];

}

 void switchPanels(Panel newPanel,boolean setNavigateButtons) {
  remove(currentPanel);
  currentPanel=newPanel;
  add("Center",currentPanel);
  remove(navigateButtonPanel);
  if(setNavigateButtons) add("South",navigateButtonPanel);
  validate();
 }
 class ButtonHandler implements ActionListener {
  public void actionPerformed(ActionEvent ev){
   String s=ev.getActionCommand();
   if(s.equals("Border")) switchPanels(panels[border],false);
   else if(s.equals("Card")) switchPanels(panels[card],true);
   else if(s.equals("Flow")) switchPanels(panels[flow],false);
   else if(s.equals("Grid")) switchPanels(panels[grid],false);
   else if(s.equals("GridBag")) switchPanels(panels[gridBag],false);
   else if(s.equals("First")){
    CardLayout currentLayout=(CardLayout)currentPanel.getLayout();
    currentLayout.first(currentPanel);
   }else if(s.equals("Last")){
    CardLayout currentLayout=(CardLayout)currentPanel.getLayout();
    currentLayout.last(currentPanel);
   }else if(s.equals("Next")){
    CardLayout currentLayout=(CardLayout)currentPanel.getLayout();
    currentLayout.next(currentPanel);
   }else if(s.equals("Previous")){
    CardLayout currentLayout=(CardLayout)currentPanel.getLayout();
    currentLayout.previous(currentPanel);
   }
  }
 }

}

LISTING 6.4. AN HTML FILE FOR DISPLAYING THE Layouts APPLET.

<HTML>
<HEAD>
<TITLE>Layouts</TITLE>
</HEAD>
<BODY>
<APPLET CODE="Layouts.class" WIDTH=400 HEIGHT=350>
</APPLET>
</BODY>

</HTML>

How the Layouts Applet Works

The Layouts applet begins by declaring a number of variables for use in the applet. These variables are used as follows:

The init() method sets the applet's layout to a BorderLayout. Note that this layout does not change. Only the layout of the middle panel is changed by clicking the layout buttons. The init() method invokes the setupButtons() method to initialize the layout and navigation buttons. It adds the panel used to display the layout buttons at the top (north) of the applet's display. It then invokes the setupDisplayPanels() method to set up the panels that are used to display the various layouts.

The setupButtons() method initializes the elements of the layoutButtons and navigateButtons arrays, sets up their event handlers, and adds the buttons to the layoutButtonPanel and navigateButtonPanel.

The setupDisplayPanels() method creates each of the five layout panels, lays them out, and adds buttons to them. The buttons are used for display purposes and do not handle any events. The panels are indexed by the border, flow, card, grid, and gridBag constants. The gridx, gridy, gridwidth, and gridheight arrays are used to set up the GridBagConstraints object for the GridBagLayout. These constraints determine the position and dimension of the objects being laid out. The panel with the BorderLayout is the first panel displayed, and is added to the center of the applet's display area.

The switchPanels() method is used to remove the current layout panel being displayed and to add the new panel identified by the newPanel parameter. The setNavigateButtons parameter determines whether the card navigation buttons panel is displayed at the bottom of the applet's display area. The validate() method causes the applet to be laid out and its components to be redisplayed.

The ButtonHandler class provides the event handling for the layout and card navigation buttons. This event handling is performed by the actionPerformed() method. The layout buttons are handled by invoking the switchPanels() method to display a new layout panel. The navigation buttons are handled by invoking the first(), last(), next(), and previous() methods of the CardLayout class to display other buttons in the card deck.

Using a null Layout for Absolute Positioning

In the preceding sections, you learned how to use the standard AWT layouts to organize the way GUI components are displayed within a container. But what if you want to organize your GUI in a way that is not easily supported by the standard layouts? The answer is to use a null layout and to position and size your components using absolute values. The setBounds() method of the Component class is used to specify both the position and dimensions of how a component is displayed. Because setBounds() is defined in the Component class, it can be used with all of the Component subclasses.


NOTE: The upper-left corner of a container is position (0,0). The x-coordinate increases as you move to the right. The y-coordinate increases as you move down.

The Positions Applet

The Positions applet, shown in Listing 6.5, illustrates the use of the null layout. The HTML file contained in Listing 6.6 is used to display this applet. The applet's display is shown in Figure 6.8. Note that this GUI organization is not easily supported by any of the standard layouts.

The Positions applet is short and simple, but it shows how the null layout can be used to produce custom layouts. Two labels and two buttons are declared and initialized. These buttons and labels identify the (x,y) coordinates at which they are located. The init() method sets the applet's layout to null, invokes the setBounds() method for each of the labels and buttons, and then adds the labels and buttons to the applet container. The setBounds() method used in this example takes the following four parameters:

The horizontal position of the component.

Other variations of the setBounds() method are also available. Consult the API description of the Component class for more information.

FIGURE 6.8. The Positions applet displays GUI components using a null layout.

LISTING 6.5. THE Positions APPLET.

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class Positions extends Applet {
 Label label1 = new Label("Label at (10,10)");
 Label label2 = new Label("Label at (100,100)");
 Button button1 = new Button("Button at (150,150)");
 Button button2 = new Button("Button at (200,200)");
 public void init() {
  setLayout(null);
  label1.setBounds(10,10,200,30);
  label2.setBounds(100,100,200,30);
  button1.setBounds(150,150,150,30);
  button2.setBounds(200,200,250,60);
  add(label1);
  add(label2);
  add(button1);
  add(button2);
 }

}

LISTING 6.6. AN HTML FILE DISPLAYING THE Positions APPLET.

<HTML>
<HEAD>
<TITLE>Positions</TITLE>
</HEAD>
<BODY>
<APPLET CODE="Positions.class" WIDTH=500 HEIGHT=350>
</APPLET>
</BODY>

</HTML>

Text Components

The TextComponent class is the superclass of all text-based classes. It provides a common set of methods used by its TextField and TextArea subclasses. It does not provide any constructors and cannot be instantiated. It provides methods for getting and setting the text that is displayed in a text object, setting the text object to an editable or read-only state, handling text-editing events, and selecting text that is contained within an object.

TextField

The TextField class implements a one-line text entry field. It provides four constructors that are used to specify the width of the text field in character columns and the default text to be displayed within the field. It provides several methods for accessing the field's size and for specifying whether the characters typed by the user should be displayed. The setEchoCharacter() method is used to specify a character that is to be displayed in lieu of text typed by the user. This method is used to implement password-like fields.

TextArea

The TextArea class implements scrollable text entry objects that span multiple lines and columns. It provides five constructors that allow the number of rows and columns and the default text display to be specified. It provides several methods that return the dimensions of the text area and then insert, append, and replace the text that is contained in that text area. It also provides the capability to set the text to read-only or edit mode.

The Text Applet

The Text applet, shown in Listing 6.7, illustrates the use of the TextField and TextArea classes. Listing 6.8 provides an HTML file for displaying this applet. When you run the applet with appletviewer, it displays the GUI shown in Figure 6.9. Enter text in the text field and then click the Append button. The text is appended to the text displayed in the text area, as shown in Figure 6.10. The Clear button is used to clear the text displayed in the text area.

The Text applet begins by declaring and initializing the TextField, TextArea, and Button objects. The init() method sets the applet's layout to null and uses setBounds() to position and size the GUI components. The event handlers for the Append and Clear buttons are registered, and all of the components are added to the applet container.

The actionPerformed() method of the ButtonHandler class handles the clicking of the Append button by using the getText() method to get the text contained in the TextArea and TextField objects. It appends the text from the TextField object to the text of the TextArea object, and then invokes the setText() method to put the appended text back into the TextArea object. The Clear button is handled by using setText() to set the text of the TextArea object to a blank string.

FIGURE 6.9. The Text applet's initial display.

FIGURE 6.10. The text area is updated with the text field's contents.

LISTING 6.7. THE Text APPLET.

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class Text extends Applet {
 TextField textField = new TextField();
 TextArea textArea = new TextArea();
 Button append = new Button("Append");
 Button clear = new Button("Clear");
 public void init() {
  setLayout(null);
  textField.setBounds(10,10,280,25);
  textArea.setBounds(10,50,280,150);
  append.setBounds(10,210,75,25);
  clear.setBounds(100,210,75,25);
  append.addActionListener(new ButtonHandler());
  clear.addActionListener(new ButtonHandler());
  add(textField);
  add(textArea);
  add(append);
  add(clear);
 }
 class ButtonHandler implements ActionListener {
  public void actionPerformed(ActionEvent ev){
   String s=ev.getActionCommand();
   if(s.equals("Append")) {
    String text = textArea.getText()+textField.getText();
    textArea.setText(text);
   }else if(s.equals("Clear")) textArea.setText("");
  }
 }

}

LISTING 6.8. AN HTML FILE FOR DISPLAYING THE Text APPLET.

<HTML>
<HEAD>
<TITLE>TextFields and TextAreas</TITLE>
</HEAD>
<BODY>
<APPLET CODE="Text.class" WIDTH=300 HEIGHT=300>
</APPLET>
</BODY>

</HTML>

Checkboxes

The Checkbox class is used to implement labeled checkbox and radio button GUI controls. If a Checkbox object is not associated with a CheckboxGroup object, it is implemented as a traditional checkbox. If a Checkbox object is associated with a CheckboxGroup object, it is implemented as a radio button.

The Checkbox class provides five constructors that allow the checkbox label, initial state, and CheckboxGroup object to be specified. The Checkbox class provides methods for getting and setting the label and state of the checkbox and its CheckboxGroup object, if any. The state of the checkbox is boolean. The Checkbox class also provides methods for identifying event handling code.

The CheckboxGroup class is used with the Checkbox class to implement radio buttons. All Checkbox objects that are associated with a CheckboxGroup object are treated as a single set of radio buttons. Only one button in the group may be set to "on" at a given point in time. The CheckboxGroup provides a single, parameterless constructor. It also provides methods for getting and setting the Checkbox object.

The CheckboxTest Applet

The CheckboxTest applet, shown in Listing 6.9, illustrates the use of checkboxes and radio buttons. It uses the CheckboxPanel and CheckboxGroupPanel classes that are defined in Listings 6.10 and 6.11. Listing 6.12 provides an HTML file for displaying the CheckboxTest applet.

The initial display of the CheckboxTest applet is shown in Figure 6.11. A list of checkboxes is displayed on the left, and a list of radio buttons is displayed on the right. When you click on a checkbox or radio button, the event is handled by displaying the action performed in the text area at the bottom of the applet display area.

The CheckboxTest applet begins by declaring objects of the CheckboxPanel, CheckboxGroupPanel, and TextArea classes. The init() method sets the applets layout to a BorderLayout object, creates a new CheckboxHandler object, and declares the sports array for setting up the checkboxes. A new CheckboxPanel object is created by passing a prompt, the sports array, a orientation constant, and the CheckboxHandler object as parameters to the object's constructor. The CheckboxPanel object is added to the left (west) side of the applet display area. A CheckboxGroupPanel object is created in a similar fashion and added to the right (east) of the applet's display. Finally, the TextArea object is added to the bottom (south) of the applet's display.

The itemStateChanged() method of the CheckboxHandler class handles the ItemEvent that is generated by clicking a checkbox or radio button. It invokes the getItemSelectable() method to obtain a reference to the object that was selected (or deselected). It uses the getState() method to determine the object's selection status and the getLabel() method to retrieve the object's label. The selection results are then displayed to the TextArea object at the bottom of the screen.

Checkboxes are easy to use but tedious to construct and organize. The CheckboxPanel class provides a more convenient approach to creating and organizing checkboxes. 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.

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

A GridLayout object is used to organize the Label and Checkbox objects placed within the panel. The title 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 they 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.

The CheckboxGroupPanel class extends the CheckboxPanel class to work with radio buttons. The Checkbox panel constructors are overridden to place the checkboxes in the panel of 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.

FIGURE 6.11. The CheckboxTest applet.

LISTING 6.9. THE CheckboxTest APPLET.

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class CheckboxTest extends Applet {
 CheckboxPanel checkboxPanel;
 CheckboxGroupPanel checkboxGroupPanel;

TextArea textArea = new TextArea(5,20);

 public void init() {
  setLayout(new BorderLayout());
  CheckboxHandler ch = new CheckboxHandler();
  String sports[] = {"Baseball","Basketball","Football","Hockey","Soccer"};
  checkboxPanel = new CheckboxPanel("What team sports do you like?  ",
   sports,CheckboxPanel.VERTICAL,ch);
  add(checkboxPanel,"West");
  String ages[] = {"under 20","20 - 39","40 - 59","60 - 79","80 and over"};
  checkboxGroupPanel = new CheckboxGroupPanel("What is your age?  ",
   ages,CheckboxPanel.VERTICAL,ch);
  add(checkboxGroupPanel,"East");
  add(textArea,"South");
 }
 class CheckboxHandler implements ItemListener {
  public void itemStateChanged(ItemEvent e){
   String status;
   Checkbox checkbox = (Checkbox) e.getItemSelectable();
   if(checkbox.getState()) status = "You checked: ";
   else status = "You unchecked: ";
   status+=checkbox.getLabel();
   textArea.setText(status);
  }
 }

}

LISTING 6.10. THE CheckboxPanel CLASS.

import java.awt.*;
import java.awt.event.*;
public class CheckboxPanel extends Panel {
 public static int HORIZONTAL = 0;
 public static int VERTICAL = 1;
 public CheckboxPanel(String title,String labels[],int orientation,
   ItemListener ih) {
  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 ch = new Checkbox(labels[i]);
   ch.addItemListener(ih);
   add(ch);
  }
 }
 public CheckboxPanel(String title,String labels[],boolean state[],
  int orientation,ItemListener ih) {
  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 ch = new Checkbox(labels[i]);
   ch.setState(state[i]);
   ch.addItemListener(ih);
   add(ch);
  }
 }
 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);
 }

}

LISTING 6.11. THE CheckboxGroupPanel CLASS.

import java.awt.*;
import java.awt.event.*;
public class CheckboxGroupPanel extends CheckboxPanel {
 public CheckboxGroupPanel(String title,String labels[],int orientation,
   ItemListener ih) {
  super(title,labels,orientation,ih);
  putInGroup();
 }
 public CheckboxGroupPanel(String title,String labels[],boolean state[],
   int orientation, ItemListener ih) {
  super(title,labels,state,orientation,ih);
  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);
  }
 }

}

LISTING 6.12. AN HTML FILE FOR DISPLAYING THE CheckboxTest APPLET.

<HTML>
<HEAD>
<TITLE>Checkboxes and Checkbox Groups</TITLE>
</HEAD>
<BODY>
<APPLET CODE="CheckboxTest.class" WIDTH=400 HEIGHT=350>
</APPLET>
</BODY>

</HTML>

Choices and Lists

The Choice class is used to implement pull-down lists that can be placed in the main area of a window. These lists are known as option menus or a pop-up menu of choices and allow the user to select a single menu value. The Choice class provides a single, parameterless constructor. It also provides access methods that are used to add items to the list, count the number of items contained in the list, select a list item, handle events, and determine which list item is selected.

The List class implements single- and multiple-selection list GUI controls. The lists provided by the List class are more sophisticated than those provided by the Choice class. The List class lets you specify the size of the scrollable window in which the list items are displayed and select multiple items from the list. The List class has three constructors. The first one takes no parameters and constructs a generic List object. The second one allows the number of rows of the visible window to be specified. The third one allows the number of rows to be specified, as well as whether or not multiple selections are allowed.

The List class provides several access methods that are used to add, delete, and replace list items, count the number of items in the list, determine which items are selected, handle events, and select items within the list.

The Chooser Applet

The Chooser applet, shown in Listing 6.13, illustrates the use of choices and lists. It uses the MyChoice and MyList classes, shown in Listings 6.14 and 6.15. Listing 6.16 provides an HTML file for running the Chooser applet.

The Chooser applet lets you decide what you want to eat for your next meal. Make sure that you have food on hand when you run the ChoiceListApp program. Its opening window is shown in Figure 6.12. The choice list, shown on the left side of the window, is used to select a meal. This selection determines which menu items are displayed in the list shown on the right side of the window. More than one item can be selected from the entree list. The text field on the bottom of the screen identifies the selections that you have made. Select Lunch from the choice list. Notice that the entree list is updated with some typical lunch items. The text field tells you that you are now ordering lunch. When you select Dinner from the choice list you get some dinner entrees, as shown in Figure 6.13. The text field is updated to list your new selections.

The Chooser applet declares several field variables. 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 entree list being displayed. The text variable refers to the TextField object displayed on the bottom of the window.

The init() method sets the applet's layout to BorderLayout and 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 applet display area.

The setupChoice() method constructs the mealChoice object and sets up its event handler. The setupLists() method sets up the mealList object by indexing through the mealChoices[] array and setting up the individual MyList objects.

The ChoiceHandler inner class handles the events associated with the MyChoice object assigned to mealChoice. It does so by updating the MyList object displayed on the right side of the applet display. It uses the remove() method of the Container class to remove the currently displayed MyList object, and then adds the MyList object corresponding to the selected meal choice. The validate() method is invoked to update the applet's display.

The ListHandler inner class handles the events associated with the meal lists that are implemented as MyList objects. It invokes the getSelectedItem() method of the Choice class to determine which meal was selected, and the getSelectedItems() method of the List class to obtain an array containing the selected list items associated with the meal. It combines the meal choice and associated entrees into a string that is displayed in the TextArea object.

The MyChoice class simplifies the construction of a Choice object. 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.

The MyList class is similar to the MyChoice class in that it allows a list to be constructed using an array of list items. The MyList constructor also allows the number of rows displayed in the list and the multiple-selection parameter to be specified.

FIGURE 6.12. The Chooser applet's initial display.

FIGURE 6.13. The Chooser applet's display is updated based on your selections.

LISTING 6.13. THE Chooser APPLET.

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class Chooser extends Applet {
 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 void init() {
  setLayout(new BorderLayout());
  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 setupChoice(){
  mealChoice = new MyChoice(meals);
  mealChoice.addItemListener(new ChoiceHandler());
 }
 void setupLists(){
  mealList = new MyList[meals.length];
  ListHandler lh = new ListHandler();
  for(int i=0;i<meals.length;++i){
   mealList[i] = new MyList(5,true,mealChoices[i]);
   mealList[i].addItemListener(lh);
  }
 }
 class ChoiceHandler implements ItemListener {
  public void itemStateChanged(ItemEvent e){
   for(int i=0;i<meals.length;++i)
    if(meals[i].equals(mealChoice.getSelectedItem())){
     Chooser.this.remove(currentList);
     currentList = mealList[i];
     Chooser.this.add("East",currentList);
     text.setText(meals[i]);
    }
   Chooser.this.validate();
  }
 }
 class ListHandler implements ItemListener {
  public void itemStateChanged(ItemEvent e){
   String order = mealChoice.getSelectedItem()+": ";
   String items[] = currentList.getSelectedItems();
   for(int i=0;i<items.length;++i) order += items[i]+" ";
   text.setText(order);
  }
 }

}

LISTING 6.14. THE MyChoice CLASS.

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 {
    add(labels[i]);
   }catch (NullPointerException ex) {
    add("");
   }
  }
 }

}

LISTING 6.15. THE MyList CLASS.

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 {
    add(labels[i]);
   }catch (NullPointerException ex) {
    add("");
   }
  }
 }

}

LISTING 6.16. AN HTML FILE FOR RUNNING THE Chooser APPLET.

<HTML>
<HEAD>
<TITLE>Choices and Lists</TITLE>
</HEAD>
<BODY>
<APPLET CODE="Chooser.class" WIDTH=300 HEIGHT=200>
</APPLET>
</BODY>

</HTML>

Scrollbars

The Scrollbar class is used to implement vertical and horizontal scrollbars. It provides three constructors that allow the orientation of the scrollbar to be specified, as well as parameters that control the scrollbar's operation. It provides several methods that allow the scrollbar's parameters and current value to be read and set.

When you use scrollbars in your Java programs, you will most likely use them to scroll through a Graphics object that is associated with a Canvas object or the main application window. (These objects are covered in the following chapter.) You create and place scrollbars in your window in the same manner as any other window component. Their position and size within the window are determined by the layout associated with the window.

Scrollbars are created using the Scrollbar()constructor. Three forms of this constructor are provided. The default constructor takes no parameters and is not particularly useful, unless you want to create a Scrollbar object and then specify its orientation and use later in your program. The second constructor allows the orientation of a Scrollbar object to be specified. The third Scrollbar() constructor uses the five parameters that are needed to create a working scrollbar: orientation, value, visible, minimum, and maximum.

The orientation of a scrollbar is specified by the VERTICAL and HORIZONTAL constants defined by the Scrollbar class. The minimum and maximum parameters specify the minimum and maximum values associated with the scrollbar's position. These values should map to the object being scrolled. For example, if you are scrolling a 1,000-line text object, appropriate minimum and maximum values for a vertical scrollbar would be 0 and 999. Horizontal values could be determined using the maximum width of the text to be scrolled (in pixels).

The value parameter identifies the starting value associated with the scrollbar. The value parameter is usually set to the minimum value of the scrollbar. However, suppose you wanted to initiate the display of an object with its center displayed on the screen. You would then set the scrollbar's value parameter to the average of its minimum and maximum values.

The visible parameter is used to specify the size of the viewable area of the object being scrolled. For example, if you are scrolling a 1,000-line text object and the viewable area of the window is 25 lines long, you would set the visible variable to 25.

The Scrollbar class provides several methods for getting and setting the parameters of a Scrollbar object. The getOrientation(), getValue(), getVisibleAmount(), getMinimum(), and getMaximum() methods retrieve the parameter values discussed so far. The getValue() method is used to determine the position to which the user has scrolled.

The setUnitIncrement() and setBlockIncrement() methods are used to specify the size of a scrollable unit and page relative to the minimum and maximum values associated with a scrollbar. For example, when scrolling text, you can set the line increment of a vertical scrollbar to one so that only one line of text is vertically scrolled. You can set the page increment to 10 to allow 10 lines of text to be scrolled when the user clicks between the tab and arrows of a scrollbar. The getUnitIncrement() and getBlockIncrement() methods provide access to the current line- and page-increment values.

The setValue() method allows you to directly set the current position of a scrollbar. The setValues() method allows you to specify a scrollbar's value, visible, minimum, and maximum parameters.

Scrollbars implement the Adjustable interface. In order to respond to user scrollbar operations and implement scrolling of the object associated with a scrollbar, you must handle the AdjustmentEvent generated by user manipulation of the scrollbar. This event is handled by implementing the AdjustmentListener interface. The adjustmentValueChanged() method of AdjustmentListener is invoked to handle scrollbar events. It is passed an object of class AdjustmentEvent.

The AdjustmentEvent class provides methods that can be used to retrieve the scrollbar for which the event was generated, the new value of the scrollbar, and the type of scrolling action that took place.

The Scroller Applet

The Scroller applet, shown in Listing 6.17, introduces the use of the Scrollbar class. Listing 6.18 provides an HTML file for running the Scroller applet.

Figure 6.14 shows the initial display of the Scroller applet. It consists of horizontal and vertical scrollbars and a label that displays the result of using either of the two scrollbars. Play with the scrollbars and watch how the label is updated.

The Scroller applet uses the MyScrollbar class to facilitate the creation and use of the horizontal and vertical scrollbars. The horizontal scrollbar is constructed with a range of 0 to 100, an initial setting of 50, and a visible window of 10 units. The vertical scrollbar is constructed with a range of 0 to 1000, an initial setting of 500, and a visible window of 100 units. The applet is laid out using a BorderLayout, with the horizontal scrollbar on top, the vertical scrollbar at left, and the label in the center.

The MyScrollbar class extends Scrollbar and provides the capability to display the results of scrollbar operations using a TextArea object.

The MyScrollbar constructor takes a number of parameters that determine the characteristics of a scrollbar. These parameters are forwarded to the superclass constructor. A TextArea object is also passed as a parameter. The orientation parameter is set to the HORIZONTAL and VERTICAL constants of the Scrollbar class. These constants specify whether the scrollbar should be displayed horizontally or vertically. The min and max parameters specify a range of integer values that are associated with the scrollbar. The value parameter sets the initial position of the scrollbar between the min and max values. The visible parameter identifies the size of the visible portion of the scrollable area. This determines how the current scrollbar position is updated as the result of a page-up or page-down scrollbar operation. The addAdjustmentListener() method is used to set up an object of the HandleScrolling inner class as a scrollbar event handler.

The adjustmentValueChanged() method of the HandleScrolling class handles scrollbar events by using the getValue() method of the AdjustmentEvent class to obtain the current scrollbar position, and then displaying this value in the text area. Note that the HandleScrolling class implements the AdjustmentListener interface.

FIGURE 6.14. The Scroller applet shows how scrollbars work.

LISTING 6.17. THE Scroller APPLET.

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class Scroller extends Applet {
 Label label = new Label("Scrollbar Position");
 MyScrollbar hscroll = new MyScrollbar(Scrollbar.HORIZONTAL,
  50,10,0,100,label);
 MyScrollbar vscroll = new MyScrollbar(Scrollbar.VERTICAL,
  500,100,0,1000,label);
 public void init() {
  setLayout(new BorderLayout());
  add("Center",label);
  add("West",vscroll);
  add("North",hscroll);
 }
}
class MyScrollbar extends Scrollbar {
 Label position;
 String direction = "     Horizontal";
 public MyScrollbar(int orientation,int value,int visible,int min,int max,
  Label label) {
  super(orientation,value,visible,min,max);
  position=label;
  if(orientation==Scrollbar.VERTICAL) direction = "     Vertical";
  addAdjustmentListener(new MyScrollbar.HandleScrolling());
 }
 class HandleScrolling implements AdjustmentListener {
  public void adjustmentValueChanged(AdjustmentEvent e){
   position.setText(direction+" Position: "+e.getValue());
  }
 }

}

LISTING 6.18. AN HTML FILE FOR RUNNING THE Scroller APPLET.

<HTML>
<HEAD>
<TITLE>Scrollbars</TITLE>
</HEAD>
<BODY>
<APPLET CODE="Scroller.class" WIDTH=400 HEIGHT=350>
</APPLET>
</BODY>

</HTML>

The ScrollPane Class

Java 1.1 introduced the ScrollPane class to simplify the development of scrollable applications. The ScrollPane class is like a combination of a panel and vertical and horizontal scrollbars. The great thing about it is that it performs all of the scrollbar event handling and screen redrawing internally. The fact that the ScrollPane class handles events is significant. By handling events internally, it allows scrolling-related operations to run significantly faster.

The ScrollPane class extends the Container class and therefore can contain other components. It is designed to automate scrolling for a single contained component, such as a Canvas object. It provides two constructors--a single parameterless constructor and a constructor that takes an int argument. The parameterless constructor creates a ScrollPane object that displays scrollbars only when they are needed. The other constructor takes one of the three constants: SCROLLBARS_ALWAYS, SCROLLBARS_AS_NEEDED, and SCROLLBARS_NEVER. These constants determine if and when scrollbars are displayed by the ScrollPane object.

The initial size of the ScrollPane object is 100¥100 pixels. The setSize() method can be used to resize it. The ScrollPane class provides methods for accessing and updating its internal scrollbars, but in most cases this is both unnecessary and ill-advised. Other methods are provided to get and set the current scrollbar positions.

Summary

In this chapter, you learned how to build a GUI using the classes of the AWT. You worked with labels, buttons, text fields, checkboxes, choices, lists, and scrollbars. You learned how to use components and containers, and how to handle events. In the next chapter, you'll learn how to draw graphics and text on Canvas object.


Contents

© Copyright, Macmillan Computer Publishing. All rights reserved.