Chapter 5

Java's User Interface Components


CONTENTS

Back in the days before the graphical browser, the World Wide Web was dominated by boring, text-only pages, and the only people using the Web were researchers, programmers, and some college students. The graphical browser changed all that by presenting more visually appealing pages. Suddenly, everyone is surfing the Web. Clearly, the change to an appealing, graphical user interface has much to do with the growth in popularity of the Web. The information in this chapter teaches you how to create an appealing user interface for your Java applets. Without an appealing, intuitive, easy-to-use user interface, it is very likely your Java applets will not have the success you desire for them.

Elements of an Applet's Interface

In Java, an applet's user interface is created mostly by combining classes that represent the following elements:

These visual elements can be combined in any manner to create the user interface your applet needs. For example, Figure 5.1 shows a Web-based applet that enables golfers to enter their scores and submit them to a remote server.

Figure 5.1 : The Golf Scorekeeper applet illustrates most user interface components.

This figure shows all the common elements of a Java user interface. The golfer's name is collected in a text field. The number of holes played (9 or 18) is a Checkbox group. A List is used for the course and a Choice is used for the time of day. The score can be entered directly into a text field or can be increased or decreased by using the adjacent scrollbar. The amenities (Golf Cart and Caddy) are checkboxes. The comment area is a multiline text area. The use of each of these user interface elements is explained in detail in the following sections.

Everything Is a Component

You were introduced to the Component class in Chapter 4, "Applet Programming Fundamentals," because of its importance in passing events from one subclass of Component to another. Component serves as the base class for most of Java's user interface classes.

As the base class for Java's user interface classes, Component does much more than provide a common set of event handlers for each user interface object. Component is a very large class that makes over 70 methods available to its subclasses. The most important or frequently used of these are shown in Table 5.1.

Table 5.1. Some useful public members of Component.

MethodPurpose
actionAn event-handling method called when action events occur.
disableDisables the component.
enableEnables the component.
getBackgroundReturns the background color of the component.
getFontReturns the font in use on the component.
getFontMetricsReturns the font metrics (character height, width, and so on) of the component's font.
getForegroundReturns the foreground color of the component.
getGraphicsGets a reference to the graphics object for the component.
getParentGets the component's parent.
gotFocusAn event-handling method called when the component receives the focus.
handleEventA generic event-handling method.
hideMakes the component invisible.
insideDetermines whether a position given by x, y coordinates is inside the component.
isEnabledReturns true if the component is enabled.
isVisibleReturns true if the component is visible.
lostFocusAn event-handling method called when the component loses the focus.
minimumSizeReturns the minimum size required by the component.
mouseDownAn event-handling method called when the mouse button is pressed down.
mouseDragAn event-handling method called when the mouse is moved while the button is held down.
mouseEnterAn event-handling method called when the mouse enters the component.
mouseExitAn event-handling method called when the mouse exits the component.
mouseMoveAn event-handling method called when the mouse is moved without holding the button down.
mouseUpAn event-handling method called when the mouse button is released.
moveRelocates the component.
nextFocusGives focus to the next component.
paintPaints the component.
preferredSizeReturns the preferred dimensions of the component.
repaintCauses the component to be repainted.
resizeChanges the dimensions of the component.
setBackgroundSets the background color of the component.
setFontSets the font that will be used by the component.
setForegroundSets the foreground color of the component.
showMakes the component visible.

Buttons

Buttons are one of the simplest Java user interface classes and are therefore a good starting point. To create a button you can use either of the constructors shown in Table 5.2. Of course, you'll find the constructor that takes a label as a parameter much more useful than the one that does not, unless you want users to have to guess what a button does.

Table 5.2. Button constructors.

ConstructorPurpose
Button()Creates a button without a label.
Button(String label)Creates a button with the specified label.

As an example of how to construct and use a button, consider EX05A as shown in Listing 5.1. This is a no-frills example of placing a button on an applet and then displaying a message when the button is pressed. After the button is created the add method is used to add the button to the applet. Because add is defined in java.awt.Container and Applet is a subclass of Container, the button is added to the applet and will be displayed when the applet is run. The results of running this applet can be seen in Figure 5.2.

Figure 5.2 : EX05A after the button has been pushed.


Listing 5.1. EX05A.java.
import java.applet.*;
import java.awt.*;

public class EX05A extends Applet
{
    Button myButton;

    public void init()
    {
        resize(320, 240);

        myButton = new Button("Push me!");
        add(myButton);
    }

    public boolean action(Event evt, Object obj)
    {
        boolean result=false;

        if("Push me!".equals(obj)) {
            getGraphics().drawString("You pushed it!", 20, 20);
            result = true;
        }
        return result;
    }
}

Handling Button Events

In Chapter 4 you learned how to process mouse and keyboard events. Although a button may be pushed as a result of the user clicking a mouse button, Java provides a different message-handling method for use when a button has been pushed. In Listing 5.1, you may have noticed the inclusion of the action method, a method that hasn't been mentioned so far. The action method is used to handle a variety of events such as button presses, checkbox selection, or menu selection.

The action method is passed the event that has occurred and the object to which the event happened. In Listing 5.1 the equals method is used to compare the Object parameter and the button's label. If equal, the object was the button. In this example a message is displayed.

NOTE
In Listing 5.1, the allocated button was stored in a member variable. However, because the button can later be referenced by its label (for example, "Push me!") and because Java supports automatic memory management, this is not necessary. The Button constructor can return the new button directly as a parameter to the add method (or any other method) as shown in the following

   public void init()
   {
   resize(320, 240);
   add(new Button("Push me!"));
   }

Public Button Methods

In addition to its constructors and to the methods available to it as a subclass of Component, the Button class provides the public member methods shown in Table 5.3.

Table 5.3. Public member methods of the Button class.

MethodPurpose
addNotify()Creates a peer for the component.
getLabel()Returns the button's current label.
paramString()Returns the button's parameter string.
SetLabel(String)Sets the button's label to the specified string.

EX05B is a more involved example of how to use the Button class and is shown in Listing 5.2. In this example, two buttons are created, one of which is disabled. This is shown in Figure 5.3.

Figure 5.3 : EX05B before any buttons are pushed.


Listing 5.2. EX05B.java.
import java.applet.*;
import java.awt.*;

public class EX05B extends Applet
{
    int count = 0;
    Button button2 = new Button("Now push me");

    public void init()
    {
        resize(320, 240);

        add(new Button("Push me!"));

        button2.disable();
        add(button2);
    }

    public boolean action(Event evt, Object obj)
    {
        boolean result=false;

        if("Push me!".equals(obj)) {
            count++;

            switch(count) 
            {
            case 1:
                button2.setLabel("almost");
                break;
            case 2:
                button2.setLabel("Now push me");
                button2.enable();
                break;
            }
            result = true;
        }
        else if("Now push me".equals(obj)) {
            getGraphics().drawString("Thank you", 20, 60);
            result = true;
        }
        return result;
    }
}

When EX05B starts running only the button labeled Push me! is enabled. Pushing this button will invoke the applet's action method, which keeps track of how many times the button has been pushed. If it has been pushed once, the second button's label is changed to Almost. If it has been pushed twice, the second button's label is reset to Now push me, and the button is enabled. Once the Now push me button is pushed, it displays a Thank you message as shown in Figure 5.4.

Figure 5.4 : EX05B after pushing the Now push me button.

Text Fields and Areas

A text field is a one-line area for data-entry. A text area is multiline text field that includes scrollbars. There are various ways of constructing a text field or text area, as can be seen in the following examples:

TextField tf1 = new TextField(25);
TextField tf2 = new TextField("This is a TextField");
TextArea ta1 = new TextArea(10, 50);
TextArea ta2 = new TextArea("This is a 10 x 50 TextArea", 10, 50);

In this example, tf1 is constructed as a 25-column, 1-row text field; tf2 is constructed as a text field wide enough to hold the string "This is a TextField". Both text areas ta1 and ta2 will be 10 rows by 50 columns, but ta2 will contain the initial text shown. There are additional constructors for text field and text area beyond those demonstrated in these examples. Tables 5.4 and 5.5 show the constructors available for TextField and TextAreaField, respectively.

Table 5.4. Constructors for the TextField class.

ConstructorPurpose
TextField()Creates a new, empty text field.
TextField(int)Creates a new text field with the specified number of columns.
TextField(String)Creates a new text field containing the specified String.
TextField(String, int)Creates a new text field with the specified number of columns and containing the specified string.

Table 5.5. Constructors for the TextAreaField class.

ConstructorPurpose
TextArea()Creates a new, empty text area.
TextArea(int, int)Creates a new text area with the specified number of rows and columns.
TextArea(String)Creates a new text area containing the specified String.
TextArea(String,int,int)Creates a new text area with the specified number of rows and columns and containing the specified string.

Both TextField and TextArea are subclasses of the TextComponent class. This common base class provides the public methods listed in Table 5.6.

Table 5.6. Public members of TextComponent.

MethodPurpose
getSelectedText()Returns the currently selected text.
GetSelectionEnd()Returns the ending column number of the selected text.
GetSelectionStart()Returns the starting column number of the selected text.
getText()Returns the text in TextComponent.
IsEditable()Returns true if TextComponent is editable.
ParamString()Returns a parameter string for TextComponent.
RemoveNotify()Removes TextComponent's peer.
select(int, int)Selects the text between the specified columns.
SelectAll()Selects all of the text.
SetEditable(boolean)Indicates whether TextComponent is editable by the user.
setText(String)Sets TextComponent to contain the specified string.

Beyond the methods shared through the TextComponent class, TextField and TextArea each implements its own additional member methods. These are listed in Tables 5.7 and 5.8.

Table 5.7. Public members of TextField.

MethodPurpose
addNotify()Creates a peer for the component.
EchoCharIsSet()Returns true if an echo character has been set.
GetColumns()Returns the number of columns for TextField.
GetEchoChar()Returns the echo character that will be used.
MinimumSize(int)Returns the minimum dimensions for TextField with the specified number of columns.
MinimumSize()Returns the minimum dimensions for TextField.
ParamString()Returns a parameter string for TextField.
PreferredSize(int)Returns the preferred dimensions for TextField with the specified number of columns.
PreferredSize()Returns the preferred dimensions for TextField.
SetEchoCharacter(char)Sets the echo character to the specified character.

Table 5.8. Public members of TextArea.

MethodPurpose
addNotify()Creates a peer for the component.
appendText(String)Appends the specified string to TextArea.
getColumns()Returns the number of columns for TextArea.
getRows()Returns the number of rows for TextArea.
insertText(String, int)Inserts the specified String at the specified column.
minimumSize(int, int)Returns the minimum dimensions for TextArea with the specified number of rows and columns.
minimumSize()Returns the minimum dimensions for TextArea.
paramString()Returns a parameter string for TextArea.
preferredSize(int, int)Returns the preferred dimensions for TextArea with the specified number of rows and columns.
preferredSize()Returns the preferred dimensions for TextArea.

ReplaceText(String, int, int) Uses the specified string to replace text between the specified columns.

A Text Field and Text Area Example

Listing 5.3 contains the EX05C class, which is a demonstration of using text fields and text areas. In this case, two text fields and two text areas are created. In the init method, each of these components is resized to its preferred size and then added to the applet. The first text field, tf1, uses setEchoCharacter to set the asterisk character as the character that will appear on the display regardless of what is typed. A button is created that, when pushed, will copy the text from each of the text fields into the text areas.


Listing 5.3. EX05C.java.
import java.applet.*;
import java.awt.*;

public class EX05C extends Applet
{
    TextField tf1 = new TextField(25);
    TextField tf2 = new TextField("This is a TextField");
    TextArea ta1 = new TextArea(10, 50);
    TextArea ta2 = new TextArea("This is a 10 x 50 TextArea", 10, 50);

    public void init()
    {
        resize(320, 400);

        tf1.resize(tf1.preferredSize());
        tf1.setEchoCharacter('*');
        add(tf1);

        ta1.resize(ta1.preferredSize());
        add(ta1);

        tf2.resize(tf2.preferredSize());
        add(tf2);


        ta2.resize(ta2.preferredSize());
        add(ta2);

        add(new Button("Update"));
    }

    public boolean action(Event evt, Object obj)
    {
        boolean result=false;

        if("Update".equals(obj)) {
            ta1.appendText(tf1.getText() + "\r\n");
            ta2.insertText(tf2.getText() + "\r\n", 0);

            tf1.setText("");
            tf2.setText("");

            result = true;
            return result;
    }
}

The action method of EX05C checks to see whether the action was generated by the Update button. If so, tf1.getText and ta1.appendText are used to copy the text from the first text field, tf1, into the first text area, ta1. Similar operations are performed on the second fields except that the text is inserted into the beginning of the second text area, ta2, instead of appended to its end. After the text has been copied from the two TextField objects, each is set to an empty string. The results of running EX05C are shown in Figure 5.5. Notice that even though tf1 uses an echo character, the underlying text and not the echo character is copied to ta1.

Figure 5.5 : EX05C after several updates.

Labels

Of course, if you put a text field or text area on the screen, you should probably tell your user what you expect him to enter. You may have noticed that there is no capability to associate a prompt directly with a text field or text area. Instead, in Java, you use a different user interface class to create prompts: Label. The Label class contains three constructors as shown in Table 5.9.

Table 5.9. Constructors for the Label class.

ConstructorPurpose
Label()Creates a label without a name.
Label(String)Creates a new label with the specified String.
Label(String, int)Creates a new label using the specified String and alignment value.

The alignment values specify how the text on the label should appear. Your choices are Label.LEFT, Label.RIGHT, and Label.CENTER. The following two lines will create two new labels:

add(new Label("Social Security Number:", Label.RIGHT);
add(new Label("First Name:");

In addition to its constructors, the Label class provides the public methods shown in Table 5.10. Among other things, these methods enable you to change a label's text and alignment while the applet is running. As an example of this, look at class EX05D shown in Listing 5.4.

Table 5.10. Public methods of the Label class.

MethodPurpose
addNotify()Creates a peer for the component.
GetAlignment()Returns the alignment value for the label.
GetText()Returns the text on the label.
ParamString()Returns a parameter string for the label.
SetAlignment(int)Sets the alignment value for the label.
SetText(String)Sets the text of the label to the specified String.

An Example

EX05D is an applet that could be the front end of a database program that lets users search for people in the database by Social Security number or name. When first loaded, the applet is ready to search by Social Security number, as shown in Figure 5.6.

Figure 5.6 : Searching EX05D by Social Security number.

However, if the user doesn't know the Social Security number of the person he is searching for, he can select the Change button. This will change the search to be a name search, as shown in Figure 5.7.

Figure 5.7 : In EX05D, selecting Change enables you to search by name.

In class EX05D, a label displaying the applet's name is constructed and added to the applet. In the init method, an eponymously named label is constructed with the text Social Security Number. After the label is added to the applet, a text field and button are also added.


Listing 5.4. EX05D.java.
import java.applet.*;
import java.awt.*;

public class EX05D extends Applet
{
    Label label;

    public void init()
    {
        resize(320, 240);

        add(new Label("Example EX05D: Labels and Buttons"));

        label = new Label("Social Security Number:", Label.RIGHT);
        add(label);
        add(new TextField(25));
        add(new Button("Change"));
    }

    public boolean action(Event evt, Object obj)
    {
        boolean result=false;

        if("Change".equals(obj)) {
            if(label.getText().equals("Name:"))
                label.setText("Social Security Number:");
            else
                label.setText("Name:");
        }
            result = true;
        return result;
    }
}

The action method looks to see whether the Change button has been pressed. If so, it toggles the text on the label between "Name:" and "Social Security Number:". When label was constructed, its alignment was set as Label.RIGHT. Because of this, the shorter "Name:" label will be right-aligned in the space initially used by "Social Security Number:". This can be seen in Figure 5.8. If label had been constructed without specifying an alignment, the text would have been left-aligned.

Figure 5.8 : Grouped and ungrouped checkboxes in action.

Checkboxes and Checkbox Groups

Java checkboxes come in two varieties: grouped and ungrouped. Instances of the Java Checkbox classes are grouped using the CheckboxGroup class. If a CheckboxGroup contains more than one checkbox, only one of the checkboxes in the group can be set at any given time. In other programming environments, you may be used to referring to grouped checkboxes as radio buttons. You can see an example of a CheckboxGroup in Figure 5.8. The Male and Female buttons are grouped together and are therefore mutually exclusive.

Ungrouped checkboxes, on the other hand, can be checked or unchecked without regard to other checkboxes. In Figure 5.9 each of the sports is associated with an ungrouped checkbox because a user may need to mark more than one box.

Figure 5.9 : The results of running EX05E.

There are three constructors for the Checkbox class, as shown in Table 5.11. Only one of the constructors assigns the new Checkbox object to a CheckboxGroup. The CheckboxGroup class has a single constructor that requires no parameters. The easiest way to assign a Checkbox to a CheckboxGroup is with the appropriate constructor. In the following code, a two-item group is created and then three ungrouped items are created:

CheckboxGroup genderGroup = new CheckboxGroup();
add(new Checkbox("Male",   genderGroup, false));
add(new Checkbox("Female", genderGroup, true));

add(new Checkbox("Option 1"));
add(new Checkbox("Option 2"));
add(new Checkbox("Option 3"));

Table 5.11. Constructors for the Checkbox class.

ConstructorPurpose
Checkbox()Creates a checkbox without a label.
Checkbox(String)Creates a checkbox using the specified String.

Checkbox(String, CheckboxGroup, boolean) Creates a checkbox in the specified group, using the specified String and set to the specified default value.

In addition to their constructors, the Checkbox and CheckboxGroup classes offer additional public methods, as shown in Tables 5.12 and 5.13.

Table 5.12. Public methods of the Checkbox class.

MethodPurpose
addNotify()Creates a peer for the component.
GetCheckboxGroup()Returns the checkbox group to which the checkbox belongs.
getLabel()Returns the text on the checkbox.
getState()Returns true if the checkbox is selected.
ParamString()Returns a parameter string for the label.
SetCheckboxGroup(CheckboxGroup) Assigns the checkbox to the specified CheckboxGroup.
setLabel(String)Sets the text on the checkbox.
setState(boolean)Checks or unchecks the checkbox.

Table 5.13. Public methods of the CheckboxGroup class.

MethodPurpose
getCurrent()Returns the currently selected checkbox.
SetCurrent(Checkbox)Makes the specified checkbox the current selection for the group.
toString()Returns a String showing the current values of the CheckboxGroup. Useful for debugging.

An Example

As an example of using Checkboxes and CheckboxGroups, consider class EX05E as shown in Listing 5.5. This class creates the applet shown in Figure 5.8. The init method adds two checkboxes to the gender CheckboxGroup, five ungrouped checkboxes, a Save button, and a text area. When the Save button is pushed, the value in each of the checked checkboxes will be written to the text area.


Listing 5.5. EX05E.java.
import java.applet.*;
import java.awt.*;

public class EX05E extends Applet
{
    CheckboxGroup genderGroup = new CheckboxGroup();
    Checkbox runCheckbox  = new Checkbox("Run");
    Checkbox bikeCheckbox = new Checkbox("Bike");
    Checkbox swimCheckbox = new Checkbox("Swim");
    Checkbox rowCheckbox  = new Checkbox("Row");
    Checkbox liftCheckbox = new Checkbox("Weight Lift");
    TextArea results = new TextArea(10, 25);


    public void init()
    {
        resize(320, 240);

        add(new Label("Gender: "));
        add(new Checkbox("Male",   genderGroup, false));
        add(new Checkbox("Female", genderGroup, true));

        add(new Label("In your spare time, what sports do you participate in?"));
        add(runCheckbox);
        add(bikeCheckbox);
        add(swimCheckbox);
        add(rowCheckbox);
        add(liftCheckbox);

        add(new Button("Save"));

        add(results);
    }

    public boolean action(Event evt, Object obj)
    {
        boolean result=false;

        if("Save".equals(obj)) {
            // clear the results area
            results.setText("");

            // display the gender
            Checkbox current = genderGroup.getCurrent();
            results.appendText(current.getLabel() + "\r\n");

            // check each of the sports
            if (swimCheckbox.getState() == true)
                results.appendText("Swim\r\n");
            if (bikeCheckbox.getState() == true)
                results.appendText("Bike\r\n");
            if (runCheckbox.getState() == true)
                results.appendText("Run\r\n");
            if (rowCheckbox.getState() == true)
                results.appendText("Row\r\n");
            if (liftCheckbox.getState() == true)
                results.appendText("Weight Lift\r\n");
            
            result = true;
        }
        return result;
    }
}

As you've seen in other examples in this chapter, the action method takes care of detecting when a button has been pushed. In this case the text area, results, is cleared. The current selection in genderGroup is then retrieved and the label on the current genderGroup checkbox is written to results. Finally, each of the ungrouped checkboxes is checked and text is appended to results for each that is checked. The results of this can be seen in Figure 5.9.

Lists and Choices

Checkboxes are not the only way to present options to your users. You can also use Java's Choice and List classes. In Java, a choice is a drop-down list from which the user may make a single selection. A list is a variable sized region from which the user may select one or more items. Figure 5.10 shows a screen that makes use of a choice to gather a person's gender and a list of sports, similar to what was done with Checkboxes in example EX04E.

Figure 5.10 : A Gender choice and a Sports list.

When you want to place a list or a choice onto an applet (or other Java container), you must take the following three steps:

  1. Construct the new List or Choice object.
  2. Add items to the List or Choice object.
  3. Place the List or Choice object onto the applet.

The following code illustrates these steps in creating a new choice:

// create a Choice object
Choice genderChoice = new Choice();

// add items to the Choice
genderChoice.addItem("Male");
genderChoice.addItem("Female");

// then add the Choice to the applet
add(genderChoice);

The Choice class only has a single, no-parameter constructor. An object of the List class, however, can be created using either of the two constructors shown in Table 5.14. Usually you will use the second constructor in order to specify the number of visible rows and whether the user can select multiple items in the List.

Table 5.14. Constructors for the List class.

ConstructorPurpose
List()Creates a new, empty list without any visible lines.
List(int, boolean)Creates a new list with the specified number of visible lines. The boolean parameter specifies whether to allow multiple selections.

As an example of creating a new list and adding five items to it, consider the following:

// create a List object
List sportList = new List(5, true);

// add items to the List
sportList.addItem("Swim");
sportList.addItem("Bike");
sportList.addItem("Run");
sportList.addItem("Row");
sportList.addItem("Weight Lift");

// then add the List to the applet
add(sportList);

List and Choice Public Methods

Because of the many ways in which a user can interact with a list or a choice, these Java classes include many public member methods. The public methods of List are shown in Table 5.15, and the public methods of choice are shown in Table 5.16.

Table 5.15. Public methods of the List class.

MethodPurpose
addItem(String)Adds the specified string to the bottom of the list.
AddItem(String, int)Adds the specified string at the specified index in the list.
AddNotify()Creates a peer for the component.
AllowsMultipleSelection()Returns true if the user can select multiple list items.
Clear()Removes all items from the list.
CountItems()Returns the quantity of items in the list.
DelItem(int)Deletes the item at the specified index.
DelItems(int, int)Deletes the specified range of items.
Deselect(int)Deselects the item at the specified index.
GetItem(int)Returns the item string at the specified index.
GetRows()Returns the number of visible rows.
GetSelectedIndex()Returns the index number of the selected item.
GetSelectedIndexes()Returns the index numbers of the selected items.
getSelectedItem()Returns the item string of the selected item.
getSelectedItems()Returns the item string of the selected items.
getVisibleIndex()Returns the index of the last item passed to makeVisible.
isSelected(int)Returns true if the specified item is selected.
makeVisible(int)Makes the specified item visible in the list.
minimumSize(int)Returns the minimum dimensions for a list with the specified number of rows.
minimumSize()Returns the minimum dimensions for the list.
paramString()Returns a parameter string for the list.
preferredSize(int)Returns the preferred dimensions for a list with the specified number of rows.
preferredSize()Returns the preferred dimensions for the list.
removeNotify()Remove the list's peer.
ReplaceItem(String, int)Replaces the item at the specified index with the specified string.
Select(int)Selects the specified item.
SetMultipleSelection(boolean) Enables or disables the selection of multiple items in the list.

Table 5.16. Public methods of the Choice class.

MethodPurpose
addItem(String)Adds the specified string to the bottom of the choice.
AddNotify()Creates a peer for the component.
CountItems()Returns the quantity of items in the choice.
GetItem(int)Returns the item string at the specified index.
GetSelectedIndex()Returns the index number of the selected item.
GetSelectedItem()Returns the item string of the selected item.
ParamString()Returns a parameter string for the choice.
Select(int)Selects the specified item.
Select(String)Selects the specified string in the choice.

An Example

To see the use of the List and Choice classes and some of their public member methods, consider EX05F, shown in Listing 5.6. This example creates the applet window shown in Figure 5.11.

Figure 5.11 : EX05F after adding and deleting some sports.


Listing 5.6. EX05F.java.
import java.applet.*;
import java.awt.*;

public class EX05F extends Applet
{
    Choice genderChoice = new Choice();
    List sportList = new List(5, true);
    TextArea results = new TextArea(10, 25);
    TextField newSport = new TextField(15);

    public void init()
    {
        add(new Label("Gender: "));

        genderChoice.addItem("Male");
        genderChoice.addItem("Female");
        add(genderChoice);

        add(new Label("Sports: "));
        sportList.addItem("Swim");
        sportList.addItem("Bike");
        sportList.addItem("Run");
        sportList.addItem("Row");
        sportList.addItem("Weight Lift");
        add(sportList);

        add(new Label("New Sport: "));
        add(newSport);
        add(new Button("Add"));
        add(new Button("Delete"));

        add(new Button("Save"));

        add(results);

        resize(320, 240);
    }

    public boolean action(Event evt, Object obj)
    {
        boolean result=false;
        int i;

        if("Save".equals(obj)) {
            // clear the results area
            results.setText("");

            // display the gender
            String gender = genderChoice.getSelectedItem();
            results.appendText(gender + "\r\n");

            for(i=0;i<sportList.countItems();i++) {
                if(sportList.isSelected(i))
                    results.appendText(sportList.getItem(i) + "\r\n");
            }
            
            result = true;
        }
        else if("Add".equals(obj)) {
            String sport = newSport.getText();
            if(sport.length() > 0) {
                sportList.addItem(sport);
                newSport.setText("");
            }
        }
        else if("Delete".equals(obj)) {
            for(i=sportList.countItems()-1; i>=0; i-) {
                if(sportList.isSelected(i))
                    sportList.delItem(i);
            }
        }
        return result;
    }
}

EX05F is similar to EX05E except that it uses List and Choice objects instead of checkboxes, and it supports the capability to add and delete sports. After the user interface components are created in the init method, they are manipulated by the action method whenever the Save, Add, or Delete buttons are pushed. If the Save button is pushed, a string indicating if the user is male or female is created with genderChoice.getSelectedItem. This string is written to the results text area. Next, the code loops through the items in sportList. For each selected item that is found, the getItem method is used to retrieve the text of the selected item. This text is also added to the results text area.

If the Add button is pushed instead, the text in the newSport text field is retrieved. If this text field is not empty, sportList.addItem is used to add the new sport to the bottom of the list.

Finally, if Delete is pushed, the code loops through sportList using delItem to delete any selected items. Note that the loop counts down from sportList.countItems()-1 to 0. To have counted up would have possibly caused invalid numbers to be passed to delItem.

The result of EX05F after having added and deleted a few items is shown in Figure 5.11.

Scrollbars

Scrollbars can be used to satisfy a number of user interface goals. They are used on windows and lists to indicate that there is more to the object than meets the eye. They can also be used to allow a user to select a value from a range of values. For example, if you were designing a user interface that represented a home thermostat, a scrollbar would be an excellent choice. In Java, scrollbars can be created by using the Scrollbar class. The constructors for this class are shown in Table 5.17.

Table 5.17. Constructors for the Scrollbar class.

ConstructorPurpose
Scrollbar()Creates a vertical scrollbar.
Scrollbar(int)Creates a scrollbar in the specified orientation.
Scrollbar(int,int,int,int,int) Creates a scrollbar in the specified orientation, initial value, visible area, minimum, and maximum value.

As you can see from Table 5.17, it is possible to construct a scrollbar in more than one screen orientation. The values Scrollbar.HORIZONTAL and Scrollbar.VERTICAL are defined in java.awt.Scrollbar. By default, a scrollbar will be positioned vertically. To create scrollbars using these constructors, consider the following examples:

Scrollbar bar1 = new Scrollbar(Scrollbar.HORIZONTAL, 50, 10, 0, 100);
Scrollbar bar2 = new Scrollbar();
Scrollbar bar3 = new Scrollbar(Scrollbar.VERTICAL);

In this case, bar1 is created as a horizontal bar that can accept values from 0 to 100. Initially, it will hold a value of 50. The visible portion of bar1 will be 10. In the second example, bar2 is created using the default constructor, which means it will be oriented vertically. Finally, bar3 is created using the constructor that requires only a parameter for the orientation. Because Scrollbar.VERTICAL is passed, a vertical scrollbar will be created.

The Scrollbar class offers a number of public methods. These are shown in Table 5.18.

Table 5.18. Public methods of the Scrollbar class.

MethodPurpose
addNotify()Creates a peer for the component.
getLineIncrement()Returns the amount by which the scrollbar will change when moving one line.
getMaximum()Returns the maximum value for the scrollbar.
getMinimum()Returns the minimum value for the scrollbar.
getOrientation()Returns the orientation of the scrollbar.
GetPageIncrement()Returns the amount by which the scrollbar will change value when moving by a page.
getValue()Returns the current value of the scrollbar.
GetVisible()Returns a value indicating how much of the scrollbar is visible.
ParamString()Returns a parameter string for Choice.
SetLineIncrement()Sets the amount by which to change the current value when moving one line.
SetPageIncrement()Sets the amount by which to change the current value when moving one page.
setValue()Sets the current value of the scrollbar.
setValues(int,int,int,int)Sets the current value, visible amount, minimum, and maximum values of the scrollbar.

An Example

As an example of using the Scrollbar class in the user interface of an applet, EX05G was created and is shown in Listing 5.7. In this example, the user is asked to enter his score for a round of golf using a scrollbar. The scrollbar is associated with a read-only text field that displays the value of the scrollbar. This can be seen in Figure 5.12.

Figure 5.12 : Entering a golf score of 79 in EX05G by using the scrollbar.


Listing 5.7. EX05G.java.
import java.applet.*;
import java.awt.*;

public class EX05G extends Applet
{
    Scrollbar scoreBar = new Scrollbar(Scrollbar.HORIZONTAL, 72, 10, 50, 120);
    TextField score = new TextField(5);

    public void init()
    {
        resize(320, 240);

        add(new Label("Score: "));
        
        score.setText("72");
        score.setEditable(false);
        add(score);

        add(scoreBar);
    }

    public boolean handleEvent(Event evt)
    {
        boolean result=false;
        int i;

        if(evt.target == scoreBar) {
            int value = scoreBar.getValue();
            String str = String.valueOf(value);

            score.setText(str);

            result = true;
        }
        return result;
    }
}

The member variable scoreBar is used to represent the scrollbar. Its constructor specifies horizontal alignment, an initial value of 72, a visible region of 10, a minimum value of 50, and a maximum value of 120. If you can shoot better than 50 for a round of golf, you're too busy golfing to enter your scores in my applet and if your score is over 120, I don't want to bother tracking it. (Sorry, Ken.) The TextField score will be used to always display the current value of the scrollbar.

In the init method, the components are added to the applet, the initial text is written to the text field, and score.setEditable(false) is used so that the user cannot change his score without using the scrollbar.

In the previous examples in this chapter, event handling was performed in the action method of the applet. To handle scrollbar events, however, you can use handleEvent. In this example, evt.target == scoreBar is used to determine whether the event was generated by the scrollbar. If it was, getValue is used to retrieve the value and the TextField score is updated.

Laying Out Controls

You may have already noticed that you've been using the add method to place user interface components on the applet and that you've never had to tell Java where to place each component. Somehow Java knew where each component belonged and placed it there. It almost seems like magic but what was really happening was that Java was making use of a layout manager to know how to place each new component.

The Java Software Developer's Kit (SDK) supports five layout managers, and you can write your own. A layout manager can be used by each of the Java classes that can be used to hold user interface components: Applet, Panel, Frame, and Dialog. Each of these classes has a default layout manager associated with it, as shown in Table 5.19. However, any layout manager can be used with any of these classes.

Table 5.19. Default layout managers.

ClassDefault Layout Manager
AppletFlowLayout
DialogBorderLayout
FrameBorderLayout
PanelFlowLayout

Layout Managers and the Resource Wizard

Layout managers can be a hassle to deal with. They keep you from writing useful code and focus you on a boring, trivial aspect of your applet: writing the user interface code. Fortunately, the creators of Visual J++ noticed this and have provided a solution. In addition to the layout managers included with the Java SDK, Visual J++ provides you with a tool for visually designing and laying out your controls. This process is identical to creating a Windows resource file.

These controls are then processed by the Visual J++ Resource Wizard, which creates code to add user interface components to an applet, dialog, frame, or panel. Additionally, the Resource Wizard provides you with a custom layout manager that will position your controls for you in the locations you indicated while visually designing your interface.

However, this doesn't mean you don't need to understand how the layout managers provided with Java work. Just because you have Visual J++ doesn't mean you can throw away the dozens of lines of legacy Java code already in use in your workplace that were written without Visual J++.

FlowLayout

The FlowLayout control is the default layout manager for the Applet class, so it has been used by default in all of the examples so far in this chapter. What FlowLayout does is to continue placing components on the same line until no more will fit. If a component won't fit on the current line, FlowLayout moves to the next line and places the component there. Components are placed on the screen in the order they are added to the applet.

Sometimes you can achieve desirable results this way, but you may have to work at it a bit. For example, look back at Figure 5.6. This example shows a text field above a text area and then another text field above another text area. I originally designed this screen to have the two text areas on top of two side-by-side text fields. Unfortunately, when I ran the applet, I discovered that because of the sizes of the components, they didn't line up the way I wanted them to. Although FlowLayout is not a great layout manager if you have more than a handful of components to display, it is an excellent choice for small quantities.

If you are working with a Dialog or Frame and want to create and use an instance of FlowLayout you can do so with the setLayout method as follows:

setLayout(new FlowLayout());

This will create a new instance of FlowLayout and will assign it to the object that executes this method.

BorderLayout

The BorderLayout class is very useful when you have a relatively small number of components to place and you want to have more control over how they are placed than is available with FlowLayout. A BorderLayout can control up to five components. Each component is placed in one of the following areas:

When you add a component using a BorderLayout, you specify the location in which to place the component. For example, the following code places a button in the North and a button in the South:

add("North", new Button("OK"));
add("South", new Button("Cancel"));

As you would expect, the North is the top of a container, the South is the bottom, the East is the right, and the West is the left. The Center is everything else. Usually this means that a component placed in the Center will be larger than other components. For example, look at Figure 5.13. This figure illustrates an applet that created five buttons, one in each position. Notice how the Center button grew to fill the available space in much the same manner as my waist grows to fill the last available hole in my belt.

Figure 5.13 : An applet using the BorderLayout layout manager.

Listing 5.8 contains the code that created EX05H, which was shown in Figure 5.13. Notice that setLayout(new BorderLayout()) was used to assign the BorderLayout to the applet.


Listing 5.8. EX05H.java.
import java.applet.*;
import java.awt.*;

public class EX05H extends Applet
{
    public void init()
    {
        setLayout(new BorderLayout());

        add("North", new Button("North"));
        add("South", new Button("South"));

        add("East", new Button("East"));
        add("West", new Button("West"));

        add("Center", new Button("Center"));

        resize(320, 240);
    }

}

CardLayout

The CardLayout class is useful for presenting a user interface in which components can come and go. For example, you could use CardLayout to create an interface that included property pages or tabbed dialogs. Each page would be a separate card in this layout manager's lexicon. Using the methods of CardLayout, you can allow a user the freedom to switch between cards. As an example, consider the code in Listing 5.9.


Listing 5.9. EX05I.java.
import java.applet.*;
import java.awt.*;

public class EX05I extends Applet
{
    CardLayout layout;
    public void init()
    {
        resize(320, 240);
        layout = new CardLayout();
        setLayout(layout);
        add("Page1", new Button("Go to Page 2"));
        add("Page2", new Button("Go to Page 3"));
        add("Page3", new Button("Go to Page 4"));
        add("Page4", new Button("Go to Page 1"));
    }
   

    public boolean action(Event evt, Object obj)
    {
        boolean result=false;

        if("Go to Page 1".equals(obj)) {
            layout.show(this, "Page1");
            result = true;
        }
        else if("Go to Page 2".equals(obj)) {
            layout.next(this);
            result = true;
        }
        else if("Go to Page 3".equals(obj)) {
            layout.show(this, "Page3");
            result = true;
        }
        else if("Go to Page 4".equals(obj)) {
            layout.last(this);
            result = true;
        }
        return result;
    }
}

In this example, the goal is to create an applet that displays a page with a button on it. Pressing the button will move the user to another page with another button. When the user reaches the final page, he or she can press a button that will return him or her to the first page where it all starts again. Hopefully, the user will figure out that the cycle is repeating itself and, unlike Sisyphus, will stop before repeating the process too many times. An example of the first page of the example is shown in Figure 5.14.

Figure 5.14 : An example of the first page of a CardLayout.

In EX05I, an instance of a CardLayout is created and stored in layout. This is assigned as the applet's layout manager using setLayout. Next, four buttons are created and added to the applet. As each button is added, it is given a name; for example, Page1 is the name given to the first button.

The action method takes care of displaying the proper page. If the button labeled Go to Page 1 is pushed, the layout.show method is passed the name (Page1) of the page to display. This updates the display with the appropriate button. A similar piece of code switches from page two to three. However, to demonstrate an alternate approach, the move from page one to two is done using layout.next, and layout.last is used to move to the fourth page.

NOTE
One potentially disconcerting fact about CardLayout is that each card, or page, can contain only one component. This is the reason why only a single button was placed on each card in class EX05I. However, this limitation is not a real concern because Java includes a Panel class. You can place multiple components on an instance of Panel and then place a single Panel on a card. The Panel class is described in the next chapter, and an example is given of how to manipulate Panels on a CardLayout

GridLayout

The GridLayout class is useful when you have a set of controls you want to place that are all the same size. For example, Figure 5.15 shows class EX05J, which has created a three-row by three- column grid. Each of the grid's components-eight buttons and a text field-are made the same size by GridLayout.

Figure 5.15 : Using GridLayout in EX05J.

As you can see in Listing 5.10, using GridLayout is as simple as using FlowLayout. In creating a new instance of GridLayout, you can specify the number of desired rows and columns as in new GridLayout(3,3). As with the other layout managers, you then use setLayout to associate the layout manager with the applet. Finally, you can use add to assign new components to the applet.


Listing 5.10. EX05J.java.
import java.applet.*;
import java.awt.*;

public class EX05J extends Applet
{
    public void init()
    {
        resize(320, 240);

        setLayout(new GridLayout(3,3));

        add(new Button("1,1"));
        add(new Button("1,2"));
        add(new Button("1,3"));
        add(new Button("2,1"));
        add(new TextField("This is a TextField"));
        add(new Button("2,3"));
        add(new Button("3,1"));
        add(new Button("3.2"));
        add(new Button("3,3"));

    }
}

GridBagLayout

The final layout manager, GridBagLayout, is the most involved to use but also gives you the most flexibility in placing your components. When using this layout manager, you also create an instance of GridBagConstraints. You then repeat a pattern of defining constraints ("this component should be the last one on the line," "this component should be twice as tall as other components," and so on), constructing a new component, and then applying the constraints to the component. The results can be worth the effort as this enables you to create complex screens, as shown in Figure 5.16.

Figure 5.16 : GridBagLayout can be used to create complex screens.

The code that creates the screen shown in Figure 5.16 is from example EX05K given in Listing 5.11.


Listing 5.11. EX05K.java.
import java.applet.*;
import java.awt.*;

public class EX05K extends Applet
{
    public void init()
    {
        GridBagLayout layout = new GridBagLayout(); 
        GridBagConstraints gbc = new GridBagConstraints(); 

        setLayout(layout); 

        // add the first button
        gbc.fill = GridBagConstraints.BOTH; 
        Button button = new Button("Top Left"); 
        layout.setConstraints(button, gbc); 
        add(button); 

        // add a button to the same row
        gbc.gridwidth = GridBagConstraints.RELATIVE; 
        button = new Button("Top Center"); 
        layout.setConstraints(button, gbc); 
        add(button); 

        // place a final button on the first row
        gbc.gridwidth = GridBagConstraints.REMAINDER; 
        button = new Button("Top Right"); 
        layout.setConstraints(button, gbc); 
        add(button); 

        // create a  button the width of the applet
        gbc.weighty = 1.0;
        button = new Button("Full Row"); 
        layout.setConstraints(button, gbc); 
        add(button); 

        // create a tall button along the left side
        gbc.gridwidth = 1;                   
        gbc.gridheight = 2; 
        gbc.weighty = 1.0; 
        button = new Button("Tall, Left Side"); 
        layout.setConstraints(button, gbc); 
        add(button); 

        // create a skinny button to the right edge
        gbc.weighty = 0.0;                   
        gbc.gridwidth = GridBagConstraints.REMAINDER; 
        gbc.gridheight = 1;                 
        button = new Button("Skinny Wide Button"); 
        layout.setConstraints(button, gbc); 
        add(button); 

        // create a button in the middle of the bottom
        gbc.gridwidth = GridBagConstraints.RELATIVE; 
        button = new Button("Bottom Middle"); 
        layout.setConstraints(button, gbc); 
        add(button); 

        // create a button at the bottom right
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        button = new Button("Bottom Right"); 
        layout.setConstraints(button, gbc); 
        add(button); 

        resize(320, 240);
    }
}

In this example, new layout and constraint objects are constructed. The first button to be added to the screen is then created with the following code:

gbc.fill = GridBagConstraints.BOTH; 
Button button = new Button("Top Left"); 
layout.setConstraints(button, gbc); 
add(button); 

The fill member of the GridBagConstraints object, gbc, is set to BOTH, which means the component will stretch both vertically and horizontally to fill the display area of the applet. Next the button is created, and layout.setConstraints is used to apply the constraints in gbc to the newly constructed button.

The second button is created by using the same GridBagConstraints object, gbc, so the same constraints will apply. Additionally, a new constraint, gbc.gridwidth = GridBagConstraints.RELATIVE, is added. This means that the component will be the next to the last component on a line. Similarly, when the third component is added the constraint gbc.gridwidth= GridBagConstraints.REMAINDER is used. This will make that component the last component on the line. The next item after an item with the REMAINDER constraint will begin a new line in the layout.

Other buttons are added in similar manners. Throughout the rest of EX05K, other member values of the constraint instance are set to demonstrate their effects.

Summary

This chapter covered a lot of territory. Along the way, you learned that the Component class is the base class for many of the Java user interface classes. You learned about the Button, TextField, TextArea, Label, Checkbox, List, Choice, and Scrollbar classes and how you can use them to create an applet's user interface. You were also introduced to layout manager and saw how to use a layout manager to control the placement of components. In the next chapter, you will continue learning about Java user interface programming by looking at windows, frames, dialogs, and menus.