Teach Yourself Java in 21 Days tyj13x.htm

- Day 13

- The Java Abstract Windowing Toolkit

- An AWT Overview
- The Basic User Interface Components

- Labels
- Buttons
- Checkboxes
- Radio Buttons
- Choice Menus
- Text Fields

- Panels and Layout

- Layout Managers

- The FlowLayout Class
- Grid and Grid Bag Layouts
- Border Layouts
- Card Layouts

- Insets

- Handling UI Actions and Events
- Nesting Panels and Components

- Nested Panels
- Events and Nested Panels

- More UI Components

- Text Areas
- Scrolling Lists
- Scrollbars and Sliders
- Canvases

- More UI Events
- A Complete Example RGB to HSB Converter

- Create the Applet Layout
- Create the Panel Layout
- Define the Subpanels
- Handle the Actions
- Update the Result
- The Complete Source Code

- Summary
- Q&A

#
Day 13

## The Java Abstract Windowing Toolkit

by Laura Lemay

For the past five days you've concentrated on creating applets that do very simple things: display text, play an animation or a sound, or enable very basic interactions with the user. Once you get past that point, however, you may want to start creating more complex applets that behave like real applications, embedded in a Web page—applets that start to look like real GUI applications with buttons, menus, text fields and other elements of a real application.

It's this sort of real work in Java applets and applications that Java's Abstract Windowing Toolkit, or AWT, was designed for. You've actually been using the AWT all along, as you might have guessed from the classes you've been importing. The Applet class and most of the classes you've been using this week are all integral parts of the AWT. In fact, the HotJava browser is also written in Java and uses the AWT as well.

The AWT provides the following:

- A full set of UI widgets and other components, including windows, menus, buttons, checkboxes, text fields, scrollbars, and scrolling lists
- Support for UI "containers," which can contain other embedded containers or UI widgets
- An event system for managing system and user events between and among parts of the AWT
- Mechanisms for laying out components in a way that enables platform-independent UI design

Today, you'll learn about how to use all these things in your Java applets. Tomorrow, you'll learn about creating windows, menus, and dialogs, which enable you to pop up separate windows from the browser window. In addition, you can use the AWT in stand-alone applications, so everything you've learned so far this week can still be used. If you find the framework of the Web browser too limiting, you can take your AWT background and start writing full-fledged Java applications.

Today, however, you'll continue focusing on applets.

Note: This is by far the most complex lesson so far. There's a lot to cover and a lot of code to go through today, so if it starts becoming overwhelming, you might want to take two days (or more) for this one.

### An AWT Overview

The basic idea behind the AWT is that a Java window is a set of nested components, starting from the outermost window all the way down to the smallest UI component. Components can include things you can actually see on the screen, such as windows, menubars, buttons, and text fields, and they can also include containers, which in turn can contain other components. Figure 13.1 shows how a sample page in a Java browser might include several different components, all of which are managed through the AWT.

Figure 13.1. AWT components.

This nesting of components within containers within other components creates a hierarchy of components, from the smallest checkbox inside an applet to the overall window on the screen. The hierarchy of components determines the arrangement of items on the screen and inside other items, the order in which they are painted, and how events are passed from one component to another.

These are the major components you can work with in the AWT:

- Containers. Containers are generic AWT components that can contain other components, including other containers. The most common form of container is the panel, which represents a container that can be displayed on screen. Applets are a form of panel (in fact, the Applet class is a subclass of the Panel class).
- Canvases. A canvas is a simple drawing surface. Although you can draw on panels (as you've been doing all along), canvases are good for painting images or other graphics operations.
- UI components. These can include buttons, lists, simple popup menus, checkboxes, test fields, and other typical elements of a user interface.
- Window construction components. These include windows, frames, menubars, and dialogs. These are listed separately from the other UI components because you'll use these less often—particularly in applets. In applets, the browser provides the main window and menubar, so you don't have to use these. Your applet may create a new window, however, or you may want to write your own Java application that uses these components.

The classes inside the java.awt package are written and organized to mirror the abstract structure of containers, components, and individual UI components. Figure 13.2 shows some of the class hierarchy that makes up the main classes in the AWT. The root of most of the AWT components is the class Component, which provides basic display and event handling features. The classes Container, Canvas, TextComponent, and many of the other UI components inherit from Component. Inheriting from the Container class are objects that can contain other AWT components—the Panel and Window classes, in particular. Note that the java.applet.Applet class, even though it lives in its own package, inherits from Panel, so your applets are an integral part of the hierarchy of components in the AWT system.

Figure 13.2. A partial AWT class hierarchy.

A graphical user interface-based application that you write by using the AWT can be as complex as you like, with dozens of nested containers and components inside each other. AWT was designed so that each component can play its part in the overall AWT system without needing to duplicate or keep track of the behavior of other parts in the system.

### The Basic User Interface Components

The simplest form of AWT component is the basic UI component. You can create and add these to your applet without needing to know anything about creating containers or panels—your applet, even before you start painting and drawing and handling events, is already an AWT container. Because an applet is a container, you can put other AWT components—such as UI components or other containers—into it.

In this section, you'll learn about the basic UI components: labels, buttons, checkboxes, choice menus, and text fields. In each case, the procedure for creating the component is the same—you first create the component, and then add it to the panel that holds it, at which point it is displayed on the screen. To add a component to a panel (such as your applet, for example), use the add() method:

    public void init() {

        Button b = new Button("OK");

        add(b);

    }

Note that where the component appears in the panel depends on the layout that panel is defined to have. The default layout for panels such as applets is FlowLayout, with a centered alignment, which means that components are added from left to right in rows, and then row by row as they fit, with each row centered. This explains why some of the examples in this section look a little funny. You'll learn more about panels and layouts in the next section.

Note also that each of these components has an action associated with it—that is, something that component does when it's activated. Actions generally trigger events or other activities in your applet (often called callbacks in other window toolkits). In this section, you'll focus on creating the components themselves; you'll learn about adding actions to them later in today's lesson.

On to the components!

#### Labels

The simplest form of UI component is the label.

Labels are, effectively, text strings that you can use to label other UI components.

The advantages that a label has over an ordinary text string is that it follows the layout of the given panel, and you don't have to worry about repainting it every time the panel is redrawn. Labels also can be easily aligned within a panel, enabling you to attach labels to other UI components without knowing exact pixel positions.

To create a label, use one of the following constructors:

- Label() creates an empty label, with its text aligned left.
- Label(String) creates a label with the given text string, also aligned left.
- Label(String, int) creates a label with the given text string and the given alignment. The available alignments are stored in class variables in Label, making them easier to remember: Label.RIGHT, Label.LEFT, and Label.CENTER.

The label's font is determined by the overall font for the component (as set by the setFont() method).

Here's some simple code to create a few labels. Figure 13.3 shows how this looks on screen:

Figure 13.3.

    add(new Label("aligned left "));

    add(new Label("aligned center", Label.CENTER));

    add(new Label(" aligned right", Label.RIGHT));

Once you have a label object, you can use methods defined in the Label class to get and set the values of the text as shown in Table 13.1.

Table 13.1. Label methods.

    Method

    Action

getText() | Returns a string containing this label's text
setText(String) | Changes the text of this label
getAlignment() | Returns an integer representing the alignment of this label: 0 is Label.LEFT, 1 is Label.CENTER, 2 is Label.RIGHT
setAlignment(int) | Changes the alignment of this label to the given integer—use the class variables above.

#### Buttons

The second user interface component to explore is the button.

Buttons are simple UI components that trigger some action in your interface when they are pressed. For example, a calculator applet might have buttons for each number and operator, or a dialog box might have buttons for "OK" and "Cancel."

To create a button, use one of the following constructors:

- Button() creates an empty button with no label.
- Button(String) creates a button with the given string object as a label.

Once you have a button object, you can get the value of the button's label by using the getLabel() method and set the label using the setLabel(String) methods.

Figure 13.4 shows some simple buttons, created using the following code:

Figure 13.4. Buttons.

    add(new Button("Rewind"));

    add(new Button("Play"));

    add(new Button("Fast Forward"));

    add(new Button("Stop"));

#### Checkboxes

Checkboxes can be selected or deselected to provide options.

Checkboxes are user interface components that have two states: on and off (or checked and unchecked, selected and unselected, true and false, and so on). Unlike buttons, checkboxes usually don't trigger direct actions in a UI but, instead, are used to indicate optional features of some other action.

Checkboxes can be used in two ways:

- Nonexclusive, meaning that given a series of checkboxes, any of them can be selected.
- Exclusive, meaning that within one series, only one checkbox can be selected at a time.

The latter kind of checkboxes are called radio buttons or checkbox groups, and are described in the next section.

Nonexclusive checkboxes can be created by using the Checkbox class. You can create a checkbox by using one of the following constructors:

- Checkbox() creates an empty checkbox, unselected.
- Checkbox(String) creates a checkbox with the given string as a label.
- Checkbox(String, null, boolean) creates a checkbox that is either selected or unselected based on whether the boolean argument is true or false, respectively. (The null is used as a placeholder for a group argument. Only radio buttons have groups, as you'll learn in the next section).

Table 13.2 lists the checkbox methods; Figure 13.5 shows a few simple checkboxes (only Underwear is selected), generated using the following code:

Figure 13.5. Checkboxes.

    add(new Checkbox("Shoes"));

    add(new Checkbox("Socks"));

    add(new Checkbox("Pants"));

    add(new Checkbox("Underwear", null, true));

    add(new Checkbox("Shirt"));

Table 13.2. Checkbox methods.

    Method

    Action

getLabel() | Returns a string containing this checkbox's label
setLabel(String) | Changes the text of the checkbox's label
getState() | Returns true or false, based on whether the checkbox is selected or not
setState(boolean) | Changes the checkbox's state to selected (true) or unselected (false)

#### Radio Buttons

Radio buttons are a variation on the checkbox.

Radio buttons have the same appearance as checkboxes, but only one in a series can be selected at a time.

To create a series of radio buttons, first create an instance of CheckboxGroup:

    CheckboxGroup cbg = new CheckboxGroup();

Then create and add the individual checkboxes, using the group as the second argument, and whether or not that checkbox is selected (only one in the series can be selected):

    add(new Checkbox("Yes", cbg, true);

    add(new Checkbox("no", cbg, false);

Here's a simple example (the results of which are shown in Figure 13.6):

Figure 13.6. Radio buttons.

    CheckboxGroup cbg = new CheckboxGroup();

    add(new Checkbox("Red", cbg, false));

    add(new Checkbox("Blue", cbg, false));

    add(new Checkbox("Yellow", cbg, false));

    add(new Checkbox("Green", cbg, false));

    add(new Checkbox("Orange", cbg, true));

    add(new Checkbox("Purple", cbg, false));

All the checkbox methods defined in the previous section can be used with the checkboxes in the group. In addition, you can use the getCheckboxGroup() and setCheckboxGroup() methods to access and change the group of any given checkbox.

Finally, the getCurrent() and setCurrent(Checkbox) methods, defined in the checkbox group, can be used to get or set the currently selected checkbox.

#### Choice Menus

The choice menu is a more complex UI component than labels, buttons, or checkboxes.

Choice menus are popup (or pulldown) menus that enable you to select an item from that menu. The menu then displays that choice on the screen.

To create a choice menu, create an instance of the Choice class, and then use the addItem() method to add individual items to it in the order in which they should appear:

    Choice c = new Choice();

    c.addItem("Apples");

    c.addItem("Oranges");

    c.addItem("Strawberries");

    c.addItem("Blueberries");

    c.addItem("Bananas");

Finally, add the entire choice menu to the panel in the usual way:

    add(c);

Figure 13.7 shows a simple choice menu generated from code in the previous example:

Figure 13.7. Choice menus.

Tip: Choice menus allow only one selection per menu. If you want to select multiple items, use a scrolling list instead.

Once your choice menu is created, regardless of whether it's added to a panel, you can continue to add items to that menu by using the addItem() method. Table 13.3 shows some other methods that may be useful in working with choice menus.

Table 13.3. Choice menu methods.

    Method

    Action

getItem(int) | Returns the string item at the given position (items inside a choice begin at 0, same as arrays)
countItems() | Returns the number of items in the menu
getSelectedIndex() | Returns the index position of the item that's selected
getSelectedItem() | Returns the currently selected item as a string
select(int) | Selects the item at the given position
select(String) | Selects the item with the given string

#### Text Fields

Unlike the UI components up to this point, which enable you to select only among several options to perform an action, text fields allow you to enter any values.

Text fields enable your reader to enter text.

To create a text field, use one of the following constructors:

- TextField() creates an empty TextField 0 characters wide.
- TextField(int) creates an empty text field with the given width in characters.
- TextField(String) creates a text field 0 characters wide, initialized with the given string.
- TextField(String, int) creates a text field with the given width in characters and containing the given string. If the string is longer than the width, you can select and drag portions of the text within the field and the box will scroll left or right.

For example, the following line creates a text field 30 characters wide with the string "Enter Your Name" as its initial contents.

    TextField tf = new TextField("Enter Your Name", 30);

    add(tf);

Tip: Text fields include only the editable field itself. You usually need to include a label with a text field to indicate what belongs in that text field.

Note: Text fields are different from text areas; text fields are limited in size and are best used for one-line items, whereas text areas have scrollbars and are better for larger text windows. Both can be edited and enable selections with the mouse. You'll learn about text areas later today.

You can also create a text field that obscures the characters typed into it—for example, for password fields. To do this, first create the text field itself, and then use the setEchoCharacter() method to set the character that is echoed on the screen. Here is an example:

    TextField tf = new TextField(30);

    tf.setEchoCharacter('*');

Figure 13.8 shows three text boxes (and labels) that were created by using the following code:

Figure 13.8. Text fields.

    add(new Label("Enter your Name"));

    add(new TextField("your name here", 45));

    add(new Label("Enter your phone number"));

    add(new TextField(12));

    add(new Label("Enter your password"));

    TextField t = new TextField(20);

    t.setEchoCharacter('*');

    add(t);

Text fields inherit from the class TextComponent and have a whole suite of methods, both inherited from that class and defined in its own class, that may be useful to you in your Java programs. Table 13.4 shows a selection of those methods.

Table 13.4. Text field methods.

    Method

    Action

getText() | Returns the text this text field contains (as a string)
setText(String) | Puts the given text string into the field
getColumns() | Returns the width of this text field
select(int, int) | Selects the text between the two integer positions (positions start from 0)
selectAll() | Selects all the text in the field
isEditable() | Returns true or false based on whether the text is editable or not
setEditable(boolean) | true (the default) enables text to be edited; false freezes the text
getEchoChar() | Returns the character used for masking input
echoCharIsSet() | Returns true or false whether the field has a masking character or not

### Panels and Layout

You know at this point that an AWT panel can contain UI components or other panels. The question now is how those components are actually arranged and displayed on the screen.

In other windowing systems, UI components are often arranged using hard-coded pixel measurements—put text field tf at 10,30, for example—the same way you used the graphics operations to paint squares and ovals on the screen. In the AWT, the window may be displayed on many different windowing systems on many different screens and with many different kinds of fonts with different font metrics. Therefore, you need a more flexible method of arranging components on the screen so that a layout that looks nice on one platform isn't a jumbled unusable mess on another.

For just this purpose, Java has layout managers, insets, and hints that each component can provide for helping lay out the screen.

Note that the nice thing about AWT components and user interface items is that you don't have to paint them—the AWT system manages all that for you. If you have graphical components or images, or you want to create animations inside panels, you still have to do that by hand, but for most of the basic components, all you have to do is put them on the screen and Java will handle the rest.

#### Layout Managers

The actual appearance of the AWT components on the screen is determined by two things: the order in which they are added to the panel that holds them, and the layout manager that panel is currently using to lay out the screen. The layout manager determines how portions of the screen will be sectioned and how components within that panel will be placed.

Note that each panel on the screen can have its own layout manager. By nesting panels within panels, and using the appropriate layout manager for each one, you can often arrange your UI to group and arrange components in a way that is both functionally useful and also looks good on a variety of platforms and windowing systems. You'll learn about nesting panels in a later section.

The AWT provides five basic layout managers: FlowLayout, GridLayout, GridBagLayout, BorderLayout, and CardLayout. To create a layout manager for a given panel, use the setLayout() method for that panel:

    public void init() {

        setLayout(new FlowLayout());

    }

Setting the default layout manager, like creating the user interface components, is best done during the applet's initialization, which is why it's included here.

Once the layout manager is set, you can start adding components to the panel. The order in which components are added is often significant, depending on which layout manager is currently active. Read on for information about the specific layout managers and how they present components within the panel to which they apply.

The following sections describe the five basic Java AWT layout managers.

##### The FlowLayout Class

The FlowLayout class is the most basic of layouts. Using the flow layout, components are added to the panel one at a time, row by row. If a component doesn't fit onto a row, it's wrapped onto the next row. The flow layout also has an alignment, which determines the alignment of each row. By default, each row is aligned centered. Figure 13.9 shows a flow layout at its best—a simple row of buttons, centered on a line.

Figure 13.9. Flow layout.

To create a basic flow layout with a centered alignment, use the following line of code in your panel's initialization (because this is the default pane layout, you don't need to include this line if that is your intent):

    setLayout(new FlowLayout());

To create a flow layout with an alignment other than centered, add the FlowLayout.RIGHT or FlowLayout.LEFT class variable as an argument:

    setLayout(new FlowLayout(FlowLayout.LEFT));

You can also set horizontal and vertical gap values by using flow layouts. The gap is the number of pixels between components in a panel; by default, the horizontal and vertical gap values are three pixels, which can be very close indeed. Horizontal gap spreads out components to the left and to the right, vertical gap to the top and bottom of each component. Add integer arguments to the flow layout constructor to increase the gap (a layout gap of 10 points in both the horizontal and vertical directions is shown in Figure 13.10):

Figure 13.10. Flow layout with a gap of 10 points.

    setLayout(new FlowLayout(FlowLayout.LEFT), 10, 10);

##### Grid and Grid Bag Layouts

Grid layouts use a layout that offers more control over the placement of components inside a panel. Using a grid layout, you portion off the area of the panel into rows and columns. Each component you then add to the panel is placed in a "cell" of the grid, starting from the top row and progressing through each row from left to right (here's where the order of calls to the add() method are very relevant to how the screen is laid out). By using grid layouts and nested grids, you can often approximate the use of hard-coded pixel values to place your UI components precisely where you want them. Figure 13.11 shows a grid layout with three columns and three rows.

Figure 13.11. Grid layout.

To create a grid layout, indicate the number of rows and columns you want the grid to have when you create a new instance of the GridLayout class:

    setLayout(new GridLayout(3, 3));

Grid layouts can also have a horizontal and vertical gap between components; to create gaps, add those pixel values:

    setLayout(new GridLayout(3, 3, 10, 15));

Figure 13.12 shows a grid layout with a 10-pixel horizontal gap and a 15-pixel vertical gap.

Figure 13.12. Grid layouts with horizontal and vertical gap.

Grid bag layouts, as implemented by the GridBagLayout class, are variations on grid layouts. Grid bag layouts also enable you to lay out your user interface elements in a rectangular grid, but with grid bag layouts you have much more control over the presentation of each element in the grid. Grid bag layouts use a helper class, GridBagConstraints, to indicate how each cell in the grid is to be formatted.

Note: The GridBagLayout and GridBagConstraints classes were added to the Java Developer's Kit just before this book went to press. For a much better description of grid bag layouts, see the API documentation for those classes that comes with the JDK.

##### Border Layouts

Border layouts behave differently from flow and grid layouts. When you add a component to a panel that uses a border layout, you indicate its placement as a geographic direction: north, south, east, west, and center (see Figure 13.13). The components around all the edges are laid out with as much size as they need; the component in the center, if any, gets any space left over.

Figure 13.13. Border layout.

To use a border layout, you create it as you do the other layouts:

    setLayout(new BorderLayout());

Then you add the individual components by using a special add() method: the first argument to add() is a string indicating the position of the component within the layout:

    add("North", new TextField("Title", 50));

    add("South", new TextField("Status", 50));

You can also use this form of add() for the other layout managers; the string argument will just be ignored if it's not needed.

Border layouts can also have horizontal and vertical gaps. Note that the north and south components extend all the way to the edge of the panel, so the gap will result in less space for the east, right, and center components. To add gaps to a border layout, include those pixel values as before:

    setLayout(new BorderLayout(10, 10));

##### Card Layouts

Card layouts are different from the other layouts. Unlike with the other three layouts, when you add components to a card layout, they are not all displayed on the screen at once. Card layouts are used to produce slide shows of components, one at a time. If you've ever used the HyperCard program on the Macintosh, you've worked with the same basic idea.

Generally when you create a card layout, the components you add to it will be other container components—usually panels. You can then use different layouts for those individual "cards" so that each screen has its own look.

When you add each "card" to the panel, you can give it a name. Then you can use methods defined on the CardLayout class to move back and forth between different cards in the layout.

For example, here's how to create a card layout containing three cards:

    setLayout(new CardLayout());

    Panel one = new Panel()

    add("first", one);

    Panel two = new Panel()

    add("second", two);

    Panel three = new Panel()

    add("third", three);

    show(this, "second");

#### Insets

Whereas horizontal gap and vertical gap are used to determine the amount of space between components in a panel, insets are used to determine the amount of space around the panel itself. The insets class provides values for the top, bottom, left, and right insets, which are then used when the panel itself is drawn. Figure 13.14 shows an inset in a GridLayout.

Figure 13.14. Insets.

To include an inset, override the insets() method in your class (your Applet class or other class that serves as a panel):

    public Insets insets() {

       return new Insets(10, 10, 10, 10);

    }

The arguments to the Insets constructor provide pixel insets for the top, bottom, left, and right edges of the panel. This particular example provides an inset of 10 pixels on all four sides of the panel.

### Handling UI Actions and Events

If you stopped reading today's lesson right now, you could go out and create an applet that had lots of little UI components, nicely laid out on the screen with the proper layout manager, gap, and insets. If you did stop right here, however, your applet would be really dull, because none of your UI components would actually do anything when they were pressed or typed into or selected.

For your UI components to do something when they are activated, you need to hook up the UI's action with an operation.

Testing for an action by a UI component is a form of event management—the things you learned yesterday about events will come in handy here. In particular, UI components produce the special kind of event called an action. To intercept an action by any UI component, you define an action() method in your applet or class:

    public boolean action(Event evt, Object arg) {

        ...

    }

The action() method should look familiar to the basic mouse and keyboard event methods. Like those methods, it gets passed the event object that represents this event. It also gets an extra object, which can be of any type. What's that second argument for?

The second argument to the action method depends on the UI component that's generating the event. The basic definition is that it's any arbitrary argument—when a component generates an event, it can pass along any extra information that might later be needed. Because that extra information may be useful for you, it's passed on through the action() method.

All the basic UI components (except for labels, which have no action) have different actions and arguments:

- Buttons create actions when they are selected, and a button's argument is the label of the button.
- Checkboxes, both exclusive and nonexclusive, generate actions when a box is checked. The argument is always true.
- Choice menus generate an action when a menu item is selected, and the argument is that item.
- Text fields create actions when the user presses Return inside that text field. Note that if the user tabs to a different text field or uses the mouse to change the input focus, an action is not generated. Only a Return triggers the action.

Note that with actions, unlike with ordinary events, you can have many different kinds of objects generating the event, as opposed to a single event such as a mouseDown. To deal with those different UI components and the actions they generate, you have to test for the type of object that sent/created the event in the first place inside the body of your action() method. That object is stored in the event's target instance variable, and you can use the instanceof operator to find out what kind of UI component sent it:

    public boolean action(Event evt, Object arg) {

        if (evt.target instanceof TextField)

            handleText(evt.target);

        else if (evt.target instanceof Choice)

            handleChoice(arg);

    ...

    }

Although you can handle UI actions in the body of the action() method, it's much more common simply to define a handler method and call that method from action() instead. Here, there are two handler methods: one to handle the action on the text field (handleText()) and one to handle the action on the choice menu (handleChoice()). Depending on the action you want to handle, you may also want to pass on the argument from the action, the UI component that sent it, or any other information that the event might contain.

Here's a simple applet that has five buttons labeled with colors. The action() method tests for a button action and then passes off the word to a method called changeColor(), which changes the background color of the applet based on which button was pressed (see Figure 13.15 to see the applet in action):

Figure 13.15. The ButtonAction applet.

    import java.awt.*;

    public class ButtonActionsTest extends java.applet.Applet {

        public void init() {

            setBackground(Color.white);

            add(new Button("Red"));

            add(new Button("Blue"));

            add(new Button("Green"));

            add(new Button("White"));

            add(new Button("Black"));

        }

        public boolean action(Event evt, Object arg) {

            if (evt.target instanceof Button)

                changeColor((String)arg);

            return true;

        }

        void changeColor(String bname) {

            if (bname.equals("Red")) setBackground(Color.red);

            else if (bname.equals("Blue")) setBackground(Color.blue);

            else if (bname.equals("Green")) setBackground(Color.green);

            else if (bname.equals("White")) setBackground(Color.white);

            else setBackground(Color.black);

        }

    }

### Nesting Panels and Components

Adding UI components to individual applets is fun, but applets begin to turn into lots of fun when you begin working with nested panels. By nesting different panels inside your applet, and panels inside those panels, you can create different layouts for different parts of the overall applet area, isolate background and foreground colors and fonts to individual parts of an applet, and manage the design of your UI components much more cleanly and simply. The more complex the layout of your applet, the more likely you're going to want to use nested panels.

#### Nested Panels

Panels, as you've already learned, are components that can be actually displayed on screen; Panel's superclass Container provides the generic behavior for holding other components inside it. The Applet class, which your applets all inherit from, is a subclass of Panel. To nest other panels inside an applet, you merely create a new panel and add it to the applet, just as you would add any other UI component:

    setLayout(new GridLayout(1, 2, 10, 10));

    Panel panel1 = new Panel();

    Panel panel2 = new Panel();

    add(panel1);

    add(panel2);

You can then set up an independent layout for those subpanels and add AWT components to them (including still more subpanels) by calling the add() method in the appropriate panel:

    panel1.setLayout(new FlowLayout());

    panel1.add(new Button("Up"));

    panel1.add(new Button("Down"));

Although you can do all this in a single class, it's common in applets that make heavy use of the panels to factor out the layout and behavior of the subpanels into separate classes, and to communicate between the panels by using method calls. You'll look at an extensive example of this later on in today's lesson.

#### Events and Nested Panels

When you create applets with nested panels, those panels form a hierarchy from the outermost panel (the applet, usually), to the innermost UI component. This hierarchy is important to how each component in an applet interacts with the other components in the applet or with the browser that contains that applet; in particular, the component hierarchy determines the order in which components are painted to the screen.

More importantly, the hierarchy also affects event handling, particularly for user input events such as mouse and keyboard events.

Events are received by the innermost component in the component hierarchy and passed up the chain to the root. Suppose, for example, that you have an applet with a subpanel that can handle mouse events (using the mouseDown() and mouseUp() methods) and that panel contains a button. Clicking on the button means that the button receives the event before the panel does; if the button isn't interested in that mouseDown(), the event gets passed to the panel, which can then process it or pass it further up the hierarchy.

Remember the discussion about the basic event methods yesterday? You learned that the basic event methods all return boolean values. Those boolean values become important when you're talking about handling events or passing them on.

An event handling method, whether it is the set of basic event methods or the more generic handleEvent(), can do one of three things, given any random event:

- Not be interested in the event (this is usually true only for handleEvent(), which receives all the events generated by the system). If this is the case, the event is passed on up the hierarchy until a component processes it (or it is ignored altogether). In this case, the event handling method should return false.
- Intercept the event, process it, and return true. In this case, the event stops with that event method. Recall that this is the case with the basic mouseDown() and keyDown() methods that you learned about yesterday.
- Intercept the method, process it, and pass it on to the next event handler. This is a more unusual case, but you may create a user interface by using nested components that will want to do this. In this case, the event method should return false to pass the event on to the next handler in the chain.

### More UI Components

Once you master the basic UI components and how to add them to panels and manage their events, you can add more UI components. In this section, you'll learn about text areas, scrolling lists, scrollbars, and canvases.

Note that the UI components in this section do not produce actions, so you can't use the action() method to handle their behavior. Instead, you have to use a generic handleEvent() method to test for specific events that these UI components generate. You'll learn more about this in the next section.

#### Text Areas

Text areas are like text fields, except they have more functionality for handling large amounts of text. Because text fields are limited in size and don't scroll, they are better for one-line responses and text entry; text areas can be any given width and height and have scrollbars by default, so you can deal with larger amounts of text more easily.

To create a text area, use one of the following constructors:

- TextArea() creates an empty text area 0 rows long and 0 characters wide. Given that a text area with no dimensions can't be displayed, you should make sure you change the dimensions of this new text area before adding it to a panel (or just use the next constructor instead).
- TextArea(int, int) creates an empty text area with the given number of rows and columns (characters).
- TextArea(String) creates a text area displaying the given string, 0 rows by 0 columns.
- TextArea(String, int, int) creates a text area displaying the given string and with the given dimensions.

Figure 13.16 shows a simple text area generated from the following code:

Figure 13.16. A text area.

    String str = "Once upon a midnight dreary, while I pondered, weak and weary,\n" +

        "Over many a quaint and curious volume of forgotten lore,\n" +

        "While I nodded, nearly napping, suddenly there came a tapping,\n" +

        "As of some one gently rapping, rapping at my chamber door.\n" +

        "\"'Tis some visitor,\" I muttered, \"tapping at my chamber door-\n

        + ... ;

    add(new TextArea(str,10,60));

Both text areas and text fields inherit from the TextComponent class, so a lot of the behavior for text fields (particularly getting and setting text and selections) is usable on text areas as well (refer to Table 13.4). Text areas also have a number of their own methods that you may find useful. Table 13.5 shows a sampling of those methods.

Table 13.5. Text area methods.

    Method

    Action

getColumns() | Returns the width of the text area, in characters or columns
getRows() | Returns the number of rows in the text area (not the number of rows of text that the text area contains)
insertText(String, int) | Inserts the string at the given position in the text (text positions start at 0)
replaceText(String, int, int) | Replace the text between the given integer positions with the new string
setLineIncrement(int inc) | Change the increment for how far to scroll when the endpoints of the scroll bar are selected. The default is 1.
getLineIncrement() | Returns the increment for how far to scroll when the endpoints of the scroll bar are selected.
setPageIncrement(int inc) | Change the increment for how far to scroll when the inside range of the scroll bar is selected. The default
is 10. |
getPageIncrement() | Returns the increment for how far to scroll when the inside range of the scroll bar is selected.

#### Scrolling Lists

Remember the choice menu, which enables you to choose one of several different options? A scrolling list is functionally similar to a choice menu in that it lets you pick several options from a list. Scrolling lists differ in two significant ways:

- Scrolling lists are not popup menus. They're lists of items in which you can choose one or more items from a list. If the number of items is larger than the list box, a scrollbar is automatically provided so that you can see the other items.
- A scrolling list can be defined to accept only one item at a time (exclusive), or multiple items (nonexclusive).

To create a scrolling list, create an instance of the List class and then add individual items to that list. The List class has two constructors:

- List() creates an empty scrolling list that enables only one selection at a time.
- List(int, boolean) creates a scrolling list with the given number of visible lines on the screen (you're unlimited as to the number of actual items you can add to the list). The boolean argument indicates whether this list enables multiple selections (true) or not (false).

After creating a List object, add items to it using the addItem() method and then add the list itself to the panel that contains it. Here's an example, the result of which is shown in Figure 13.17:

Figure 13.17. A scrolling list.

    List lst = new List(5, true);

    lst.addItem("Hamlet");

    lst.addItem("Claudius");

    lst.addItem("Gertrude");

    lst.addItem("Polonius");

    lst.addItem("Horatio");

    lst.addItem("Laertes");

    lst.addItem("Ophelia");

    add(lst);

Table 13.6 shows some of the methods available to scrolling lists. See the API documentation for a complete set.

Table 13.6. Scrolling list methods.

    Method

    Action

getItem(int) | Returns the string item at the given position
countItems() | Returns the number of items in the menu
getSelectedIndex() | Returns the index position of the item that's selected (used for lists that enable only single selections)
getSelectedIndexes() | Returns an array of index positions (used for lists that enable multiple selections)
getSelectedItem() | Returns the currently selected item as a string
getSelectedItems() | Returns an array of strings containing all the selected items
select(int) | Selects the item at the given position
select(String) | Selects the item with that string

#### Scrollbars and Sliders

Text areas and scrolling lists come with their own scrollbars, which are built into those UI components and enable you to manage both the body of the area or the list and its scrollbar as a single unit. You can also create individual scrollbars, or sliders, to manipulate a range of values.

Scrollbars are used to select a value between a maximum and a minimum value. To change the current value of that scrollbar, you can use three different parts of the scrollbar (see Figure 13.18):

Figure 13.18. Scrollbar parts.

- Arrows on either end, which increment or decrement the values by some small unit (1 by default).
- A range in the middle, which increments or decrements the value by a larger amount (10 by default).
- A box in the middle, often called an elevator or thumb, whose position shows where in the range of values the current value is located. Moving this box with the mouse causes an absolute change in the value, based on the position of the box within the scrollbar.

Choosing any of these visual elements causes a change in the scrollbar's value; you don't have to update anything or handle any events. All you have to do is give the scrollbar a maximum and minimum, and Java will handle the rest.

To create a scrollbar, you can use one of three constructors:

- Scrollbar() creates a scrollbar with 0, 0 as its initial maximum and initial minimum values, in a vertical orientation.
- Scrollbar(int) creates a scrollbar with 0, 0 as its initial maximum and initial minimum values. The argument represents an orientation, for which you can use the class variables Scrollbar.HORIZONTAL and Scrollbar.VERTICAL.
- Scrollbar(int, int, int, int, int) creates a scrollbar with the following arguments (each one is an integer, and must be presented in this order):
  The first argument is the orientation of the scrollbar: Scrollbar.HORIZONTAL and Scrollbar.VERTICAL.
  The second argument is the initial value of the scrollbar, which should be a value between the scrollbar's maximum and minimum values.
  The third argument is the the overall width (or height, depending on the orientation) of the scrollbar's box. In user interface design, a larger box implies that a larger amount of the total range is currently showing (applies best to things such as windows and text areas).
  The fourth and fifth arguments are the minimum and maximum values for the scrollbar.

Here's a simple example of a scrollbar that increments a single value (see Figure 13.19). The label to the left of the scrollbar is updated each time the scrollbar's value changes:

Figure 13.19. A scrollbar.

    import java.awt.*;

    public class SliderTest extends java.applet.Applet {

        Label l;

        public void init() {

            l = new Label("0");

            add(l);

            add(new Scrollbar(Scrollbar.HORIZONTAL, 1, 0, 1, 100));

        }

        public boolean handleEvent(Event evt) {

            if (evt.target instanceof Scrollbar) {

                int v = ((Scrollbar)evt.target).getValue();

                l.setText(String.valueOf(v));

            }

            return true;

        }

    }

The Scrollbar class provides several methods for managing the values within scrollbars (see Table 13.7).

Table 13.7. Scrollbar methods.

    Method

    Action

getMaximum() | Returns the maximum value
getMinimum() | Returns the minimum value
getOrientation() | Returns the orientation of this scrollbar:
| 0 is Scrollbar.HORIZONTAL, 1 is Scrollbar.VERTICAL
getValue() | Returns the scrollbar's current value
setValue(int) | Sets the current value of the scrollbar

#### Canvases

Although you can draw on most AWT components, such as panels, canvases do little except let you draw on them. They can't contain other components, but they can accept events, and you can create animations and display images on them. Canvases, in other words, could have been used for much of the stuff you learned about earlier this week.

A canvas is a component that you can draw on.

To create a canvas, use the Canvas class and add it to a panel as you would any other component:

    Canvas can = new Canvas();

    add(can);

### More UI Events

Yesterday, you learned about some basic event types that are generated from user input to the mouse or the keyboard. These event types are stored in the Event object as the event ID, and can be tested for in the body of a handleEvent() method by using class variables defined in Event. For many basic events, such as mouseDown() and keyDown(), you can define methods for those events to handle the event directly. You learned a similar mechanism today for UI actions where creating an action() method handled a specific action generated by a UI component.

The most general way of managing events, however, continues to be the handleEvent() method. For events relating to scrollbars and scrolling lists, the only way to intercept these events is to override handleEvent().

To intercept a specific event, test for that event's ID. The available IDs are defined as class variables in the Event class, so you can test them by name. You learned about some of the basic events yesterday; Table 13.8 shows additonal events that may be useful to you for the components you've learned about today (or that you might find useful in general).

Table 13.8. Additional events.

    Event ID

    What It Represents

ACTION_EVENT | Generated when a UI component action occurs
KEY_ACTION | Generated when text field action occurs
LIST_DESELECT | Generated when an item in a scrolling list is deselected
LIST_SELECT | Generated when an item in a scrolling list is selected
SCROLL_ABSOLUTE | Generated when a scrollbar's box has been moved
SCROLL_LINE_DOWN | Generated when a scrollbar's bottom or left endpoint (button) is selected
SCROLL_LINE_UP | Generated when a scrollbar's top or right endpoint (button) is selected
SCROLL_PAGE_DOWN | Generated when the scrollbar's field below (or to the left of) the box is selected
SCROLL_PAGE_UP | Generated when the scrollbar's field above (or to the right of) the box is selected

### A Complete Example RGB to HSB Converter

Let's take a break here from theory and smaller examples to create a larger, more complex example that puts together much of what you've learned so far. The following applet example demonstrates layouts, nesting panels, creating user interface components, and catching and handling actions, as well as using multiple classes to put together a single applet. In short, it's the most complex applet you'll create so far.

Figure 13.20 shows the applet you'll be creating in this example. The ColorTest applet enables you to pick colors based on RGB (red, green, and blue) and HSB (hue, saturation, and brightness) values.

Figure 13.20. The ColorTest applet.

The ColorTest applet has three main parts: a colored box on the left side and two groups of text fields on the right. The first group indicates RGB values, the right, HSB. By changing any of the values in any of the text boxes, the colored box is updated to the new color, as are the values in the other group of text boxes.

This applet uses two classes:

- ColorTest, which inherits from Applet. This is the controlling class for the applet itself.
- ColorControls, which inherits from Panel. You'll create this class to represent a group of three text fields and to handle actions from those text fields. Two instances of this class, one for the RGB values and one for the HSB ones, will be created and added to the applet.

Let's work through this step by step, because it's very complicated and can get confusing. All the code for this applet will be shown at the end of this section.

#### Create the Applet Layout

The best way to start creating an applet that uses AWT components is to worry about the layout first and then worry about the functionality. When dealing with the layout, you also should start with the outermost panel first and work inward.

Making a sketch of your UI design can help you figure out how to organize the panels inside your applet or window to best take advantage of layout and space. Figure 13.21 shows the ColorTest applet with a grid drawn over it so that you can get an idea of how the panels and embedded panels work.

Figure 13.21. The ColorTest applet panels and components.

#### Create the Panel Layout

Let's start with the outermost panel—the applet itself. This panel has three parts: the color box on the left, the RGB text fields in the middle, and the HSB fields on the right.

Because this is the applet, your ColorTest class will be the applet class and inherit from Applet. You'll also import the AWT classes here (note that because you use so many of them in this program, it's easiest to just import the entire package):

    import java.awt.*;

    public class ColorTest extends java.applet.Applet {

        ...

    }

Let's start with the init() method, where all the basic initialization and layout takes place. There are four major steps:

1. Set the layout for the big parts of the panel. Although a flow layout would work, a grid layout with one row and three columns is a much better idea.
2. Create the three components of this applet: a canvas for the color box and two subpanels for the text fields.
3. Add those components to the applet.
4. Finally, initialize the default color and update all the panels to reflect that default color.

Before you do any of that, let's set up instance variables to hold the three major components of this applet. You need to keep hold of these objects so you can update things when a value changes.

The color box is easy—it's just a canvas. Call it swatch.

    Canvas swatch;

Now onto the subpanels. There are two of them, and although they have different labels and values, they're essentially the same panel. You could just create code for each one here, but you'd end up duplicating a lot of the same code. This is a perfect opportunity, therefore, to create another class to represent the subpanels with the text fields on them. Call them ColorControls (you'll get around to creating the class later) and define two variables, rgbControls and hsbControls, to hold them:

    ColorControls rgbControls, hsbControls;

Back to the init() method. Step one is the layout. Let's use a grid layout and a gap of ten points to separate each of the components:

    setLayout(new GridLayout(1, 3, 10, 10));

Step two is creating the components, the canvas first. You have an instance variable to hold that one:

    swatch = new Canvas();

You need to create two instances of your as-of-yet nonexistent ColorControls panels here as well, but you don't know exactly what you need to create them yet, so let's put in some basic constructors and fill in the details later:

    rgbControls = new ColorControls()

    hsbControls = new ColorControls();

Step three is adding them to the panel.

    add(swatch);

    add(rgbControls);

    add(hsbControls);

While you're working on layout, add an inset just for fun—ten points along all the edges:

    public Insets insets() {

        return new Insets(10, 10, 10, 10);

    }

Got it so far? Now you have a skeleton init() method and an insets() method in your ColorTest class. Let's move on now to creating the subpanel layout—to creating that ColorControls class.

#### Define the Subpanels

The ColorControls class will have behavior for laying out and handling the subpanels that represent the RGB and HSB values for the color. ColorControls doesn't need to be a subclass of Applet because it isn't actually an applet, it's just a panel. Define it to inherit from Panel:

    class ColorControls extends Panel {

        ...

    }

Note: You can put the ColorControls class in the same file as the ColorTest class. You haven't been doing this so far because the applets and applications you've been creating had only one class. If you remember way back to Day 1, however, you learned that you can have multiple class definitions in a single file as long as only one of those definitions is declared public. In this case, the ColorTest class is public (it's an applet, so it has to be), but the ColorControls class doesn't need to be, so everything works out fine. Normally, you'd put ColorControls in a separate file, anyway.

You need a couple of instance variables in this class. The first thing you need is a hook back up to the applet class that contains this panel. Why? The applet class is the class that oversees how the subcomponents work, so it's going to be the class that updates everything. Eventually, you're going to have to call a method in that class to indicate that something in this panel has changed. Without an actual reference to that outer class, there's no way to do this. So, instance variable number one is a reference to the class ColorTest:

    ColorTest outerparent;

If you figure that the applet class is the one that's going to be updating everything, that class is going to need a way to get hold of the pieces inside this class. In particular, it's going to be interested in the individual text fields, so you're going to need instance variables to hold those. This creates three of them:

    TextField f1, f2, f3;

Now for the constructor for this class. Again, this isn't an applet, so you don't use init(); all you need is a constructor method.

What do you need inside that constructor? You need to set the layout for the subpanel, create the text fields, and add them to the panel. The goal here is to make the ColorControls class generic enough so that you can use it for both the RGB fields and the HSB fields.

The two different panels differ in two respects: the labels for the text fields, and the initial values for the text fields. That's six values to get before you can create the object. You can pass those six values in through the constructors in ColorTest. You also need one more. Because you need that hook back to the applet class, you should also pass in a reference to that object as part of the constructor.

You now have seven arguments to the basic constructor for the ColorControls class. Here's the signature for that constructor:

    ColorControls(ColorTest target,

            String l1, String l2, String l3,

            int v1, int v2, int v3) {

    }

Given those arguments, you can assign the right values to your instance variables:

    outerparent = target;

    f1 = new TextField(String.valueOf(v1),10);

    f2 = new TextField(String.valueOf(v2),10);

    f3 = new TextField(String.valueOf(v3),10);

Note that because the first argument to the TextField constructor is a string, and the values that you passed in were integers, you have to use the valueOf() class method (defined in String) to convert the integer to a string before creating each text field.

Next, you create the layout for this panel. You also use a grid layout for these subpanels, as you did for the applet panel, but this time the grid will have three rows (one for each of the text field and label pairs) and two columns (one for the labels and one for the fields).

Given the 3-by-2 grid, you can now add the text fields and labels to that panel. Note that by separating the labels and the text fields into separate cells in the grid, you can align the labels, creating a nice aligned layout.

    add(new Label(l1, Label.RIGHT));

    add(f1);

    add(new Label(l2, Label.RIGHT));

    add(f2);

    add(new Label(l3, Label.RIGHT));

    add(f3);

Finally (because I like insets), you'll inset the contents of the subpanel a bit—only on the top and bottom edges—by including an insets() method:

    public Insets insets() {

            return new Insets(10, 10, 0, 0);

     }

You're almost there. You have 98 percent of the layout in place and ready to go, but you're missing two things: creating the ColorControls objects in ColorTest, and initializing everything so that all the components have the right values.

For both, you need to go back to the ColorTest class and the init() method you defined there. Let's start with the initialization part, because that's easy. The default color is black. Set up a local variable to hold that color object:

    Color theColor = new Color(0, 0, 0);

To set the initial color of the color box, all you need to do is set its background:

    swatch.setBackground(theColor);

Now, let's finally tackle initializing those subpanels. The constructor for ColorControls has seven arguments: the ColorTest object, three labels (strings), and three initial values for the text fields (integers). Let's do the RGB controls first, because you can easily extract the initial red, green, and blue values out of the Color object:

    rgbControls = new ColorControls(this, "Red", "Green", "Blue",

            theColor.getRed(), theColor.getGreen(),

            theColor.getBlue());

Things get complicated on the HSB side of the panel. The Color class provides you with a method to get the HSB values out of a Color object, but there are two problems:

- The RGBtoHSB() method is a single class method that insists on returning an array of the three values.
- The HSB values are measured in floating-point values. I prefer to think of HSB as integers, wherein the hue is a degree value around a color wheel (0 through 360), and saturation and brightness are percentages from 0 to 100. Having HSB as integer values also enables you to have a generic subpanel, as was the intent.

Initializing the HSB subpanel is going to be a little difficult.

First, let's extract those HSB values. Given that the method takes three RGB arguments—an array of three floats—and returns an array of three floats, you have to go through this process to get those values:

    float[] HSB = Color.RGBtoHSB(theColor.getRed(),

        theColor.getGreen(), theColor.getBlue(),(new float[3]));

Now you have an array of floats, where hsb[0] is the hue, hsb[1] is the saturation, and hsb[2] is the brightness. You can now (finally!) initialize the HSB side of the applet, making sure that when you pass those HSB values into the subpanel, you multiply them by the right values (360 for the hues, 100 for the saturation and the brightness) and convert them to integers:

    hsbcontrols = new ColorControls(this,

            "Hue", "Saturation", "Brightness",

            (int)(hsb[0] * 360), (int)(hsb[1] * 100),

            (int)(hsb[2] * 100));

Ready to give up? Fear not—you've done the hard part. From here, it's (mostly) easy. Once you have your layout working, you can compile your Java program and see how it looks. None of your UI components actually does anything, but perfecting the layout is half the battle.

#### Handle the Actions

After creating the layout, you set up actions with the UI components so that when the user interacts with the applet, the applet can respond.

The action of this applet occurs when the user changes a value in any of the text fields. By causing an action in a text field, the color changes, the color box updates to the new color, and the values of the fields in the opposite subpanel change to reflect the new color.

The ColorTest class is responsible for actually doing the updating, because it keeps track of all the subpanels. You should be tracking and intercepting events in the subpanel in which they occur, however. Because the action of the applet is an actual text action, you can use an action() method to intercept it:

    public boolean action(Event evt, Object arg) {

            if (evt.target instanceof TextField) {

                outerparent.update(this);

                return true;

            }

            else return false;

        }

In the action() method, you test to make sure the action was indeed generated by a text field (because there are only text fields available, that's the only action you'll get, but it's a good idea to test for it anyhow). If so, call the update() method, defined in ColorTest, to update the applet to reflect all the new values. Because the outer applet is responsible for doing all the updating, this is precisely why you need that hook back to the applet—so you can call the right method at the right time.

#### Update the Result

The only part left now is to update all the values and the color swatch if one of the values changes. For this, you define the update() method in the ColorTest class. This update() method takes a single argument—the ColorControls instance that contains the changed value (you get that argument from the action() method in the subpanel).

Note: Won't this update() method interfere with the system's update() method? Nope. Remember, methods can have the same names, but different signatures and definitions. Because this update() has a single argument of type ColorControls, it doesn't interfere with the other version of update(). Normally, all methods called update() should mean basically the same thing; it's not ture here, but it's only an example.

The update() method is responsible for updating all the panels in the applet. To know which panel to update, you need to know which panel changed. You can find out by testing to see whether the argument you got passed is the same as the subpanels you have stored in the RGBcontrols and HSBcontrols instance variables:

    void update(ColorControls in) {

        if (in == rgbControls) { // the change was in rgb

            ...

        }

        else { // change was in hsb

    }

This test is the heart of the update() method. Let's start with that first case—a number has been changed in the RGB text fields. So now, based on those new RGB values, you have to generate a new color object and update the values on the HSB panel. To reduce some typing, you create a few local variables to hold some basic values. In particular, the values of the text fields are strings, and you get into them by accessing the text field instance variables for the ColorControls panel (f1, f2, f3) and then using the getText() method to extract the actual values. Extract those values and store them in string variables so that you don't have to keep typing:

    String v1 = in.f1.getText();

    String v2 = in.f2.getText();

    String v3 = in.f3.getText();

Given those string values for RGB, you now create a color object by converting those strings to integers:

    Color c;

    c = new Color(Integer.parseInt(v1),Integer.parseInt(v2), Integer.parseInt(v3));

Note: This part of the example isn't very robust; it assumes that the user has indeed entered real numbers into the text fields. A better version of this would test to make sure that no parsing errors had occurred (I was trying to keep this example small).

When you have a color object, you can update the color swatch:

    swatch.setBackground(c);

The next step is to update the HSB panel to the new HSB values. Doing this in the init() method is no fun at all, and it's even less fun here. To do this, you call RGBtoHSB to get the floating-point values, convert them to integers with the right values, convert them to strings, and then put them back into the text fields for the HSB subpanel. Got all that? Here's the code:

    float[] HSB = Color.RGBtoHSB(c.getRed(),c.getGreen(),

                c.getBlue(), (new float[3]));

    hsb[0] *= 360;

    hsb[1] *= 100;

    hsb[2] *= 100;

    hsbControls.f1.setText(String.valueOf((int)hsb[0]));

    hsbControls.f2.setText(String.valueOf((int)hsb[1]));

    hsbControls.f3.setText(String.valueOf((int)hsb[2]));

The second part of the update() method is called when a value on the HSB side of the panel is changed. This is the "else" in the if-else that determines what to update, given a change.

Believe it or not, it's easier to update RGB values given HSB than it is to do it the other way around. First, convert the string values from the HSB text fields to integers by using these lines:

    int f1 = Integer.parseInt(v1);

    int f2 = Integer.parseInt(v2);

    int f3 = Integer.parseInt(v3);

There's a class method in the Color class that creates a new color object when given three HSB values. The catch is that those values are floats, and they're not the values you currently have. To call getHSBColor() (that's the name of the method), convert the integers to floats and divide by the right amounts:

    c = Color.getHSBColor((float)f1 / 360, (float)f2 / 100, (float)f3/100);

Now that you have a color object, the rest is easy. Set the color swatch:

    swatch.setBackground(c);

Then update the RGB text fields with the new RGB values from the color object:

    rgbControls.f1.setText(String.valueOf(c.getRed()));

    rgbControls.f2.setText(String.valueOf(c.getGreen()));

    rgbControls.f3.setText(String.valueOf(c.getBlue()));

#### The Complete Source Code

Listing 13.1 shows the complete source code; often it's easier to figure out what's going on in this applet when it's all in one place and you can follow the method calls and how values are passed back and forth. Start with the init() method in applet, and go from there.

Listing 13.1. The ColorTest applet.

      1: import java.awt.*;

      2:

      3: public class ColorTest extends java.applet.Applet {

      4:     ColorControls rgbControls, hsbControls;

      5:     Canvas swatch;

      6:

      7:     public void init() {

      8:         Color theColor = new Color(0, 0, 0);

      9:         float[] hsb = Color.RGBtoHSB(theColor.getRed(),

     10:             theColor.getGreen(), theColor.getBlue(),

     11:             (new float[3]));

     12:

     13:         setLayout(new GridLayout(1, 3, 10, 10));

     14:

     15:         // The color swatch

     16:         swatch = new Canvas();

     17:         swatch.setBackground(theColor);

     18:

     19:         // the control panels

     20:         rgbControls = new ColorControls(this,

     21:             "Red", "Green", "Blue",

     22:             theColor.getRed(), theColor.getGreen(),

     23:             theColor.getBlue());

     24:

     25:         hsbControls = new ColorControls(this,

     26:             "Hue", "Saturation", "Brightness",

     27:             (int)(hsb[0] * 360), (int)(hsb[1] * 100),

     28:             (int)(hsb[2] * 100));

     29:

     30:         add(swatch);

     31:         add(rgbControls);

     32:         add(hsbControls);

     33:

     34:     }

     35:

     36:     public Insets insets() {

     37:         return new Insets(10, 10, 10, 10);

     38:     }

     39:

     40:     void update(ColorControls in) {

     41:         Color c;

     42:         String v1 = in.f1.getText();

     43:         String v2 = in.f2.getText();

     44:         String v3 = in.f3.getText();

     45:

     46:         if (in == rgbControls) {    // change to RGB

     47:             c = new Color(Integer.parseInt(v1),

     48:                     Integer.parseInt(v2),

     49:                     Integer.parseInt(v3));

     50:             swatch.setBackground(c);

     51:             float[] HSB = Color.RGBtoHSB(c.getRed(),c.getGreen(),

     52:                     c.getBlue(), (new float[3]));

     53:             hsb[0] *= 360;

     54:             hsb[1] *= 100;

     55:             hsb[2] *= 100;

     56:             hsbControls.f1.setText(String.valueOf((int)HSB[0]));

     57:             hsbControls.f2.setText(String.valueOf((int)HSB[1]));

     58:             hsbControls.f3.setText(String.valueOf((int)HSB[2]));

     59:         }

     60:         else {    // change to HSB

     61:             int f1 = Integer.parseInt(v1);

     62:             int f2 = Integer.parseInt(v2);

     63:             int f3 = Integer.parseInt(v3);

     64:             c = Color.getHSBColor((float)f1 / 360,

     65:                     (float)f2 / 100, (float)f3/100);

     66:             swatch.setBackground(c);

     67:             RGBcontrols.f1.setText(String.valueOf(c.getRed()));

     68:             RGBcontrols.f2.setText(String.valueOf(

     69:                 c.getGreen()));

     70:             RGBcontrols.f3.setText(String.valueOf(c.getBlue()));

     71:         }

     72:     }

     73: }

     74:

     75:

     76: class ColorControls extends Panel {

     77:     TextField f1, f2, f3;

     78:     ColorTest outerparent;

     79:

     80:     ColorControls(ColorTest target,

     81:             String l1, String l2, String l3,

     82:             int v1, int v2, int v3) {

     83:

     84:         this.outerparent = target;

     85:         setLayout(new GridLayout(3,4,10,10));

     86:

     87:         f1 = new TextField(String.valueOf(v1),10);

     88:         f2 = new TextField(String.valueOf(v2),10);

     89:         f3 = new TextField(String.valueOf(v3),10);

     90:

     91:         add(new Label(l1, Label.RIGHT));

     92:         add(f1);

     93:         add(new Label(l2, Label.RIGHT));

     94:         add(f2);

     95:         add(new Label(l3, Label.RIGHT));

     96:         add(f3);

     97:     }

     98:

     99:     public Insets insets() {

    100:         return new Insets(10,10,0,0);

    101:     }

    102:

    103:     public boolean action(Event evt, Object arg) {

    104:         if (evt.target instanceof TextField) {

    105:             outerparent.update(this);

    106:             retrue true;

    107:         }

    108:         else return false;

    109:     }

    110: }

### Summary

The Java AWT, or Abstract Windowing Toolkit, is a package of Java classes and interfaces for creating full-fledged access to a window-based graphical user interface system, with mechanisms for graphics display, event management, text and graphics primitives, user interface components, and cross-platform layout. The AWT is used by the HotJava browser itself for all its functionality. Applets are also an integral part of the AWT toolkit.

Today has been a big day; the lesson has brought together everything you've learned up to this point about simple applet management and added a lot more about creating applets, panels, and user interface components and managing the interactions between all of them. With the information you got today and the few bits that you'll learn tomorrow, you can create cross-platform Java applications that do just about anything you want.

### Q&A

Q: You've mentioned a lot about the Component and Container classes, but it looks like the only Container objects that ever get created are Panels. What do the Component and Container classes give me?

A: Those classes factor out the behavior for components (generic AWT components) and containers (components that can contain other components). Although you don't necessarily create direct instances of these classes, you can create subclasses of them if you want to add behavior to the AWT that the default classes do not provide. As with most of the Java classes, any time you need a superclass's behavior, don't hesitate to extend that class by using your own subclass.

Q: Can I put a UI component at a specific x and y position on the screen?

A: By using the existing layout managers supplied with the AWT toolkit, no. This is actually a good thing because you don't know what kind of display environment your applet will be run under, what kind of fonts are installed, or what kind of fonts are being currently used. By using the layout managers provided with the AWT, you can be reasonably sure that every portion of your window will be viewable and readable and usable (fonts may cause you trouble). You can't guarantee anything like that with hard-coded layouts.

Q: I was exploring the AWT package, and I saw this subpackage called peer. There's also references to the peer classes sprinkled throughout the API documentation. What do peers do?

A: Peers are responsible for the platform-specific parts of the AWT. For example, when you create a Java AWT window, you have an instance of the Window class that provides generic Window behavior, and then you have an instance of a class implementing WindowPeer that creates the very specific window for that platform—a motif window under X Window, a Macintosh-style window under the Macintosh, or a Windows 95 window under Windows 95. These "peers" classes also handle communication between the window system and the Java window itself. By separating the generic component behavior (the AWT classes) from the actual system implementation and appearance (the peer classes), you can focus on providing behavior in your Java application and let the Java implementation deal with the platform-specific details.

Q: There's a whole lot of functionality in the AWT that you haven't talked about here. Why?

A: Given that even a basic introduction took this long, I figured that if I put in even more detail than I already have that this book would turn into Teach Yourself Java in 21 Days Plus a Few Extra for the AWT Stuff.
As it is, I've left windows, menus, and dialogs until tomorrow, so you'll have to wait for those. But you can find out about a lot of the other features of AWT merely by exploring the API documentation. Start with the Applet class and examine the sorts of methods you can call. Then look at Panel, from which applet inherits—you have all that class's functionality as well. The superclass of Panel is Container, which provides still more interesting detail. Component comes next. Explore the API and see what you can do with it. You might find something interesting.