Introduction to the Abstract Windowing Toolkit

Let's look at some of the principles behind AWT before looking at an AWT-based solution to our hypothetical coding problem. AWT consists of a set of classes contained within the package java.awt. In this section, we'll study the contents of the java.awt package.

AWT Components and Containers

Many of the classes that make up AWT represent widgets of one type or another. The purposes of classes like Button, Label, Checkbox, and Scrollbar are clear. The hierarchy of these widget classes is shown in Figure 4-2.

The first thing to notice in Figure 4-2 is that the classes making up AWT have a common base class in Component. The abstract class Component defines a set of methods that all AWT components share. For example, all components share the resize() method to resize an object and the setVisible(boolean) method to make an object visible or invisible.

The second thing you might notice from Figure 4-2 is that the AWT component classes break down into two groups. The simple component classes are those along the right side of the figure. These classes represent discrete widgets that might appear on the interface.

The second set of classes is the compound components that extend the abstract class Container. Container classes are classes that can contain components. For example, a Window class might contain multiple Button objects. Thus, the most important method that Component adds is the method add(), which enables a program to add a component to the container. For example, a button can be added to a dialog.

Java Click to view at full size.

Screenshot-2. The class hierarchy of the Abstract Windowing Toolkit.

Several subclasses of Container are of particular note. The class Applet represents an applet that runs within an Internet browser. We'll return to this class in the discussion of applets in Part III.

Frames, dialogs, and panels

The class Frame represents what we as users might call a window. A frame is a window with defined edges, a title bar, and minimize, maximize, and close buttons on the far right side of the title bar. By default, the frame can be resized by dragging any side or corner with the mouse. (The resizing capability can be turned off programmatically.)

The class Dialog represents a window that is similar in appearance to a panel. The distinguishing feature of a dialog is that it can be modal, meaning that mouse and keyboard input is allowed to the dialog box only as long as the dialog box is visible. A panel represents an area of the screen. The class Panel is the simplest concrete implementation of the abstract Container class.

NOTE
Remember that you can't create an instance of an abstract class because an abstract class has one or more abstract methods. An abstract method is a method that has been declared but not implemented—in other words, a method containing no code. A subclass of an abstract class becomes concrete by implementing all of the abstract methods.

Unlike a frame, a panel has no visible borders and no title bar. Since a panel isn't visible, it doesn't really have any properties of its own. This might lead you to wonder, "What good is a panel anyway?"

NOTE
The class Container extends the class Component, which means that a container is a component. A container can therefore be added to a container like any other component. There is one exception: even though Frame extends Component, because of its window-sizing buttons a frame can't be added to a container.

The primary use of a panel is to group components located within another container, such as a frame or dialog. This still doesn't completely address the need for a panel, but before I try to fully answer that question let's look at a simple example of a frame without panels: SimpleFrame.

NOTE
The complete code samples from this chapter are located in the Generic apps folder in the sample files on the companion CD.

A simple frame example

/**
 * This class demonstrates a simple AWT frame.
 */
import java.awt.*;
public class SimpleFrame extends Frame {
 /**
 * The main entry point for the app. */
 public static void main (String[] args)
 {
 SimpleFrame sf = new SimpleFrame();
 sf.init();
 }
 /**
 * Add a few components to the container and
 * make the frame visible.
 */
 public void init()
 {
 // add a few components
 add(new Label("Enter name:"));
 add(new TextField(10));
 add(new Button("Submit"));
 // now set the size…
 this.setSize(300, 100);
 // and make the results visible
 setVisible(true);
 }
}


Like all the apps we have seen so far in this tutorial, this program was created using the Console app Wizard. Also, like all Visual J Plus Plus apps, this program begins execution with the public main() method. The main() method performs two steps. First it creates an object of class SimpleFrame. It then invokes the init() method of SimpleFrame to populate the frame with components.

NOTE
Using an init() method to populate the frame is common because it follows the pattern of an applet, as we'll see in Part III.

The init() method adds three components:

Finally, the SimpleFrame object sets its size to be 300 units wide and 100 units tall. Since display characteristics are somewhat machine-dependent, a unit has never been exactly defined; however, on most platforms, including the PC, it corresponds to a pixel.

The result of this program is shown in Figure 4-3.

Screenshot

Screenshot-3. The disappointing results from our first AWT frame.

Notice that only the Submit button is visible in the resulting frame. Although the output is disappointing, there are a lot of features present for relatively little code. By extending Frame, SimpleFrame has a title bar, although it doesn't populate the title bar with a title. In addition, SimpleFrame has the three window buttons. The maximize and minimize buttons actually work, but the close button has no effect. This is because the base class Frame knows how to handle maximizing and minimizing the window but doesn't know how you want to terminate your program gracefully.

AWT Layout Managers

In the output from the SimpleFrame object shown in Figure 4-3, only the Submit button is visible. Why is this so?

The problem is that as the program added components to the SimpleFrame, it didn't tell the Frame base class where to put each object. Not knowing any differently, Frame put each object in the middle, one on top of the other. Since the button was the last object to be added, it's the only one visible.

"No problem," says the experienced Microsoft Visual C++ or Microsoft Visual Basic programmer. "I'll just add a few calls to the setPosition() method to position the components relative to each other in the frame." However, this doesn't work because AWT has no such method. This seems surprising until you consider that the same Java program is intended to work on different types of machines with different screen resolutions and sizes. Java applets must even be able to run under the control of an Internet browser. Therefore, Java can make no assumptions about the size or resolution of the display.

border layout manager

Rather than use absolute positioning, Java uses a series of layout managers that position the components relative to each other in the container. All of the layout managers use some form of relative positioning. Instead of using pixel positions to set the absolute position of each component in SimpleFrame, you, for example, place one component to the left of another or put a component on the right side of the frame. With some layout managers, you can divide the available horizontal space, vertical space, or both, evenly between the components.

The default layout manager for the Frame class is BorderLayout. The BorderLayout layout manager attaches components relative to the four borders of the frame. For whatever reason, the founders of Java weren't fond of left and right directions; they named the frame borders West, East, North, and South instead. (They could have just as well used Port, Starboard, Bow, and Stern.) Whatever is left over is called Center (there is no compass direction for where you're at).

The changes to the init() method to utilize the BorderLayout layout manager are shown in the following code.

public void init()
{
 // add a few components
 add("West", new Label("Enter name:"));
 add("Center", new TextField(10));
 add("East", new Button("Submit"));
 // now set the size…
 this.setSize(300, 100);
 // and make the results visible
 setVisible(true);
}


Screenshot-4 shows the results of this version of the init() method.

Screenshot

Screenshot-4. The somewhat improved output obtained by using relative placing of the components within the frame.

While Figure 4-4 is certainly an improvement over its predecessor, this still isn't really what we might want. For example, the TextField object is several rows high even though it can accept only a single line of text. Is there not some way to further improve the appearance of SimpleFrame?

Using panels with the BorderLayout layout manager

To improve the appearance of SimpleFrame, you must understand exactly how the BorderLayout manager works. It takes the following steps:

  1. It allocates space to the components assigned to the North and South sides of the frame. These objects are stretched horizontally to consume all of the available horizontal space from one side of the frame to the other, and are assigned only as much vertical space as they need.
  2. It expands the components assigned to the West and East sides of the frame vertically to fill any remaining vertical space left by the North and South objects. These objects are assigned only as much horizontal space as they need.
  3. It expands the Center component to fill any remaining space.

The result looks like the diagram shown in Figure 4-5.

Screenshot

Screenshot-5. The regions of the BorderLayout layout manager.

One improvement over the last version of the init() method would be to bundle the prompt and the input text area into a single line and place this near the top of the frame. We could then put the Submit button at the bottom. Under this scenario, the center would remain empty.

The following version of init() does exactly this through the use of the Panel class:

public void init()
{
 // add a few components
 // use a panel to combine the label and text field
 // along the top of the display
 Panel panel = new Panel();
 panel.setLayout(new BorderLayout());
 panel.add("West", new Label("Enter name:"));
 panel.add("Center", new TextField());
 add("North", panel);
 // now put the button at the bottom
 add("South", new Button("Submit"));
 // now set the size…
 this.setSize(300, 100);
 // and make the results visible
 setVisible(true);
}


Here you can see that the panel is assigned a BorderLayout object using the setLayout() method. We didn't have to specify the layout for the Frame object because the default layout is already BorderLayout, but the panel receives a different layout by default. The label and text field are then attached to the panel object.

Once the layout manager for the panel has been established, the label is placed on the West side of this invisible panel. All remaining space is allocated to the text field. Once it is completed, the panel object is added to the north side of the frame. The border layout manager of the frame will stretch the panel horizontally to fill the available horizontal space. The vertical size of the panel is set by the space needs of the label and the text field. The Submit button is then placed along the South side of the window. The result is shown in Figure 4-6.

Screenshot

Screenshot-6. The improved output resulting from the use of panels to group components.

Other layout managers

There are many other Visual J Plus Plus layout managers besides the BorderLayout layout manager. For example,the simple FlowLayout manager allocates space from left to right in a row until it runs out of horizontal space, at which point it moves down to the second row and starts allocating horizontal space again.

A complete discussion of layout managers is beyond the scope of this tutorial. In any case, by means of judicious and clever use of panels you can generally achieve the results you want with the BorderLayout manager and perhaps one or two other managers.

AWT Events

By using panels and the BorderLayout manager, we have improved the appearance of SimpleFrame, but the program still doesn't do anything. Neither choosing the Submit button nor clicking the frame's close button has any effect. In this section, I will show you how to modify SimpleFrame to actually do something. This new program, SimpleActiveFrame, terminates when you click the close button or displays the contents of the text field on standard output when you choose the Submit button.

/**
 * This class demonstrates a simple AWT frame that
 * can respond to a button press. (This app uses
 * the older Java 1.0-style event processing.)
 */
import java.awt.*;
public class SimpleActiveFrame extends Frame {
 // name field
 TextField name;
 /**
 * The main entry point for the app. */
 public static void main (String[] args)
 {
 SimpleActiveFrame sf = new SimpleActiveFrame();
 sf.init();
 }
 /**
 * Add a few components to the container and
 * make the frame visible.
 */
 public void init()
 {
 // add a few components
 // use a panel to combine the label and text field
 // along the top of the display
 Panel panel = new Panel();
 panel.setLayout(new BorderLayout());
 panel.add("West", new Label("Enter name:"));
 panel.add("Center", name = new TextField());
 add("North", panel);
 // now put the button at the bottom
 add("South", new Button("Submit"));
 // now set the size…
 this.setSize(300, 100);
 // and make the results visible
 setVisible(true);
 }
 /**
 * When the Submit button is pressed, the action event
 * will bubble up to the frame. Read the name and
 * display on standard output.
 * * @deprecated
 */
 public boolean action(Event e, Object o)
 {
 // read the text out of the TextField
 String s = name.getText();
 // display it on standard output
 System.out.println("Name = \'" + s + "\'");
 // returning true indicates that we've handled
 // the event successfully
 return true; }
 /**
 * Events are always passed to handleEvent() first.
 * Action events get passed on to action() by default.
 * Other events are either handled or ignored by default.
 * Here, we handle the WINDOW_DESTROY event, which is otherwise
 * ignored.
 * * @deprecated
 */
 public boolean handleEvent(Event e)
 {
 // if this is a WINDOW_DESTROY event…
 if (e.id == Event.WINDOW_DESTROY)
 {
 // terminate the program
 System.exit(0);
 }
 return super.handleEvent(e);
 }
}


The only difference between the SimpleActiveFrame class and its predecessor SimpleFrame is the addition of the two methods action() and handleEvent(). The action() method handles the clicking of the Submit button and handleEvent() handles the WINDOW_DESTROY event, which occurs when the user clicks the window's close button.

What's an event?

An event is an object that is generated when something happens. For example, if the user clicks a button, the VM generates a button click event. When the user presses a key, the VM generates two events: one for the key going down and another for the key going up. Through these events, the VM communicates to the program what is going on in the outside world.

All events extend the class Event. This base class contains basic information, like a reference to the object that originated the event and the type id of the event. A reference to the type id is generally useful for window events; Event contains a list of possible ids. For mouse events, the Event class records the position of the mouse at the time of an event such as the button down or button up event. There is also a time in the Event class, but this does not refer to the time of day or the time of year; you can use this time only to compare two events to determine which occurred first or how far apart in time the two events were.

As events occur, the VM creates an Event object and passes it to the Component.handleEvent() method.

NOTE
Visual J Plus Plus apps have a Windows message dispatch loop, just like a Visual C++ program does; however, this loop is hidden from the Visual J Plus Plus programmer by the Java event mechanism. Windows messages get turned into Event objects which are processed as described here. The Visual J Plus Plus programmer does have access to the idle loop processing by means of WFC, as we will see in .

The Component.handleEvent() method ignores all events. This method is overridden in superior classes, such as Frame, to handle whatever events they can. For example, the Frame.handleEvent() method knows how to handle the WINDOW_MINIMIZE and WINDOW_MAXIMIZE events.

The Frame.handleEvent() method does not know how to handle the WINDOW_DESTROY event; SimpleActiveFrame must override handleEvent() to provide such processing. If the id of the Event object passed to SimpleActiveFrame.handleEvent() is Event.WINDOW_DESTROY, then SimpleActiveFrame.handleEvent() calls System.exit() to terminate the program. For other Event ids, SimpleActiveFrame.handleEvent() passes the Event object to super.handleEvent(). This allows the base class to continue to provide whatever default processing it is capable of.

CAUTION


If you override handleEvent(), be sure to pass any events you do not handle on to super.handleEvent() for processing.

Notice that the return type of handleEvent() is boolean. A true return value indicates that the event has been processed and does not require any further processing. The SimpleActiveFrame.handleEvent() method includes a return true statement even though control will never return from the exit() call. This serves no purpose other than instructional.

What's an action?

There is a certain class of events called action events. An action event is an event resulting from some action by the user. For example, a button click is an action event.

Like all events, action events first get passed to the handleEvent() method. If they are not processed there, action events then get passed on to the Component.action() method. Like the handleEvent() method, the action() method can be overridden to process the event. For example, in SimpleActiveFrame we could have extended Button into a new class, say SubmitButton, with its own action() method. This method would automatically get invoked whenever that button was clicked.

It isn't necessary to create a new subclass for every AWT object you create, however. The default processing for Component.action() is to pass the event to the action() method of the parent component, so you can handle all action events in the frame if you prefer. The SimpleActiveFrame.action() method reads the text from the text field using the getText() method and passes the text on to println() to be output to standard output. This output appears in the DOS window from which the app was started.

NOTE
There are reasons why you might not want to handle action events in the frame. Depending on your design, it might be cleaner to put the action event handler in a SubmitButton class so that all submit processing is in one place.

The only real difference between the action() method and the handleEvent() method is that the action() method has fewer events to deal with. For example, since SimpleActiveFrame has only one component, the Submit button, we were able to assume that the action event originated from the button component.

Why the warnings?

You might notice that when you compile SimpleActiveFrame several warnings appear in the task list indicating that handleEvent() and action() have been "deprecated." This means that in one of the base classes these methods have been flagged with the @deprecated pragma. (In fact, I have even flagged both methods with the @deprecated pragma in this class just to show you how it's done.)

Flagging a method as deprecated indicates that although the method works now, the designers of Visual J Plus Plus might remove that method from future versions of the class library. The action() and handleEvent() methods are deprecated because Java 1.1 introduced a new event-handling approach. I have presented the version 1.0 event-handling approach here because it is so pervasive in the Java community that all Java programmers need to be familiar with it for the foreseeable future.

I will present Java 1.1 event handling in , where we can contrast it with WFC event handling. Comments