by David R. Chung
The Java Abstract Windowing Toolkit (AWT) is a general-purpose, multiplatform windowing library. The AWT provides classes that encapsulate many useful graphical user interface (GUI) components (also called widgets or controls). The AWT also includes classes to manage component layout and utility classes to handle fonts, colors, and other GUI-related items. Because Java is a multiplatform solution, the AWT provides a common interface to the native GUI components on a wide variety of platforms. This abstraction makes the AWT highly portable.
The tradeoff is that the AWT does not fully encapsulate the machine-specific GUI features of any specific platform. Even so, the AWT is a fully capable GUI, and you can use it to create powerful user interfaces that run on a wide variety of platforms.
The AWT classes can be divided into three groups:
This chapter examines the classes of the AWT and explains how to incorporate them into your applets and applications. Example applets demonstrate the various AWT controls.
The control classes of the AWT provide a platform-independent wrapper for the basic GUI widgets. These classes include most of the components necessary to create a modern user interface for your Java applets or applications.
The AWT component classes are all derived from a common base class:
the Component class. The
Component class is an abstract
class. This class defines the elements common to all GUI components.
The Component class is derived
from the Object class. The
Component class also implements
the ImageObserver interface.
Note |
An abstract class is a class that contains one or more methods declared to be abstract. (Abstract methods are similar to pure virtual functions in C++.) Abstract classes cannot actually be instantiated. To make use of these classes, you must derive a class from the abstract class and provide an implementation for each abstract method. |
The Component class provides a unified interface to all the graphic components of the AWT. Figure 16.1 shows all the AWT widgets derived from the Component class.
Figure 16.1: The Java Component class hierarchy.
The components in this hierarchy can be divided into the following functional groups:
The Java AWT encapsulates many of the controls common to most GUIs. Specifically, these are the Button, Checkbox, Choice, Label, List, and Scrollbar classes. Figure 16.2 shows an applet that displays the AWT simple widgets.
Figure 16.2: The simple AWT components.
The following code shows the Simple applet, which contains the simple AWT widgets. The applet's init() method creates an example of each simple widget and adds it to the applet.
import java.awt.*; public class Simple extends java.applet.Applet { public void init() { Button button = new Button( "Quit" ) ; Checkbox checkbox = new Checkbox( "Test" ) ;
Both the Choice and List objects include addItem() methods. These methods allow you to fill the control with the items you specify. Unlike some GUIs, the AWT Choice and List controls do not sort the items they contain. They are displayed in the order in which you add them.
Choice choice = new Choice() ; // fill the Choice choice.addItem( "Clinton" ) ; choice.addItem( "Dole" ) ; choice.addItem( "Perot" ) ; choice.addItem( "Browne" ) ; choice.addItem( "Nader" ) ; Label label = new Label( "This is a label" ) ; List list = new List( 5, false ) ; // fill the List list.addItem( "Clinton" ) ; list.addItem( "Dole" ) ; list.addItem( "Perot" ) ; list.addItem( "Browne" ) ; list.addItem( "Nader" ) ; Scrollbar scrollbar = new Scrollbar( Scrollbar.HORIZONTAL ) ;
To display these controls, you must add them to the applet's layout using the add() method:
// add the controls to the default layout add( button ) ; add( checkbox ) ; add( choice ) ; add( label ) ; add( list ) ; add( scrollbar ) ; } }
This applet displays the simple widgets. To be truly useful, an applet should do more than just display the controls-the applet must also be interactive. This brings up the topic of event handling. The DemoFrame applet presented later in this chapter demonstrates event handling.
The AWT contains a group of controls whose purpose is to allow users to enter and display text. Unlike the simple widgets just discussed, the text controls let users enter freeform text into applications. The TextField and TextArea classes are derived from TextComponent as shown in Figure 16.3.
Figure 16.3: The AWT text component hierarchy.
TextComponent is an abstract base class. It provides common methods for displaying text, getting text from the user, and selecting portions of text. The base class also allows the control to be made editable or read-only.
The TextField class encapsulates a nonscrollable text box. TextField controls are commonly used to allow users to enter single values.
TextArea controls are more versatile and allow multiple lines and scrolling. Both TextField and TextArea components can be used to get user input or to display values.
Now let's work through the code for the Text
applet that shows the use of the AWT text
components:
import java.awt.*; public class Text extends java.applet.Applet { public void init() {
Both the TextArea and TextField
classes (and many other Component-derived
classes) have overloaded constructor methods.
Note |
Overloaded methods are methods with the same name that take different parameters. Constructors are commonly overloaded to provide different types of initialization for a class. |
This applet uses the TextArea and TextField constructors to specify the number of rows and columns displayed in each control. These constructors also specify the text that will be initially displayed in the control.
TextArea textarea = new TextArea( "This text area has 3 rows and 40 columns", 3, 40 ) ; TextField textfield = new TextField( "This text field has 30 columns", 30 ) ; add( textarea ) ; add( textfield ) ; } }
The Text applet demonstrates the AWT text components. Figure 16.4 shows the Text applet.
Figure 16.4: The AWT text components.
The AWT Canvas class is a generic Component class. The Canvas class is most often used to display images. It is also used as the background for user drawing. Chapter 21, "Creating User Interface Components," contains an example that uses the Canvas class to draw an image.
The AWT Container class is a Component class that can contain other Component objects. Although Container is an abstract class, its derived classes are among the most useful in the AWT. Figure 16.5 shows the Container class hierarchy.
Figure 16.5: The container class hierarchy.
Container classes are the basic framework from which you build the GUI components of your applets. The Container hierarchy can be divided into two categories: the Panel and Window classes.
The Panel class is a generic Container class. Panel is not an abstract class and can be instantiated or subclassed. Panels are Java's multipurpose containers and do not provide any special functionality-except for the capability to embed other GUI objects. Panels give you a convenient method of forming composite controls. In Chapter 21, you use the Panel class to combine other controls. Sample applets in that chapter demonstrate many of the GUI components available in the AWT.
The Panel class performs
one other important function: It is the superclass of the AWT
Applet class. This makes
sense because one of the things you do with applets is embed controls
in them.
Note |
The Applet class is not actually part of the java.awt package. Applet is contained in the java.applet package. |
Like the Panel class, the Window class is derived from the Container class. Although it is not an abstract class, you rarely create Window objects. However, the Window class is the root class for two very useful classes: Frame and Dialog. The Window class and its derived classes allow you to create windows that can float over or even outside the browser window. These Window objects can be resizable and can have their own titles and menus.
Because Frame objects are derived from the Container class, they can contain other controls. The FrameTest applet creates a Frame with embedded Button and Label objects. Figure 16.6 shows the FrameTest applet in operation.
Figure 16.6: The FrameTest applet (with Frame displayed).
This applet has a Button object that displays or hides a Frame. The button is an AWT Button; the Frame object is the DemoFrame class derived from Frame. The applet doesn't actually create a Frame, rather it creates a DemoFrame object derived from Frame.
class DemoFrame extends Frame { Label label ; Button button ; // variable to store button presses int count ;
Note |
Calls to a superclass constructor should be made only from a derived class constructor. They must be the first line of code in the derived class constructor. Other superclass methods can be called from any derived class method. The syntax is super.superclassMethod(). |
The constructor also creates the embedded Label object and sets its text to an empty string. The Label.CENTER parameter indicates that the label text should be centered in the control.
The resetCount() and showCount() methods are defined a little later in this section. These methods set the value of count and display it in the embedded Label. Both of these methods are public so that the owner of this Frame (in this case, the FrameTest applet) can use them to reset and display the count value:DemoFrame( String title ) { super( title ) ; label = new Label( "", Label.CENTER ) ;
Next, the constructor creates the Button object. The Button face displays the label Count. To display the Label above the Button, you must set the Frame's Layout to GridLayout and add the controls. GridLayout lets you lay out controls in a regular grid-in this case, two rows and one column:resetCount() ; showCount() ;
The resetCount() method is a public method. This method is designed to allow access to the count variable. Of course, you can make the count variable public, but then count could be modified by anyone. By using a public access method, you can control who accesses your data members and what they can do with them.button = new Button( "Count" ) ; setLayout( new GridLayout( 2,1 ) ) ; add( label ) ; add( button ) ; }
The showCount() method updates the Label text. Every time the Frame's button is pressed, the count variable is incremented and the showCount() method is called to display the results:public void resetCount(){ count = 0 ; }
public void showCount(){ label.setText( "Button pressed " + count + " times." ) ; }
The action() method takes
two parameters: an Event
and an Object. Both parameters
are objects. Event provides
information about the type of event and the control that originated
it. The Object argument is
a generic argument that contains information specific to the type
of event that occurs.
Note |
Object is the superclass to all Java classes; therefore, the Object parameter can be used to pass any type of object. To make use of this parameter, you must cast it to some specific type. |
The action() method is supposed to respond to the Frame's button. The first thing the method must do is determine the type of event this is. The if statement checks the target member of the Event argument to see whether it is a Button (or a class derived from Button).
The expression in the if statement uses the instanceof operator. This operator is similar to the == and > operators. It compares its right and left operands to see whether they are of the same type. This is an example of runtime type checking. (C++ is just now getting runtime type checking.)
If the Event was generated by a Button, the next if statement checks to see which Button was pressed. Because this is a Button Event, the Object parameter is a String containing the text on the Button face. Because the parameter is of type Object (remember that all Java classes, including String, are derived from Object), you must cast the parameter to a String.
If this Event was generated by the Count button, the action() function increments count. Next, the function calls the showCount() method to display the new value.
Finally, if the action() method has completely handled the Event, it should return true. If further processing is required, the method returns false.
public boolean action( Event evt, Object arg ) { if ( evt.target instanceof Button ) { if ( ((String)arg).equals( "Count" ) ){ count++ ; showCount() ; // the event has been handled return true ; } } // the event has not been handled return false ; } }
The DemoFrame class is used to create a Frame for the applet. The applet creates a button that alternately displays or hides the Frame:
import java.awt.*; public class FrameTest extends java.applet.Applet { DemoFrame frameWindow ; Button showFrame ;
The applet's init() method creates the Button and the DemoFrame. Notice that the button is added to the applet's Layout but the DemoFrame is not. Because Frame objects are top-level windows, they cannot be embedded in other containers. Frames actually float outside the applet and browser windows. They can be hidden by other windows or tiled along with other top-level windows.
In this example, the Frame object is created and a String is passed to the Frame constructor. The String is used as the Frame window title.
When you create a Frame, it is initially invisible. When the user presses the button, the applet displays (or hides) the Frame. The Frame must also be given a size; this is done by calling its resize() method:
public void init() { showFrame = new Button( "Show Frame" ) ; add( showFrame ) ; frameWindow = new DemoFrame( "This is a Frame" ) ; frameWindow.resize( 200, 100 ) ; }
The FrameTest applet handles Button events the same way the DemoFrame class does. A subtle difference is that each time the button is pressed, the text on its face changes. When the Button says Show Frame, it resets the count to zero and shows the Frame. The method also changes the text on the button to Hide Frame. Likewise, if the Button face says Hide Frame, the Frame is hidden.
public boolean action( Event evt, Object arg ) { if ( evt.target instanceof Button ) { if (((String)arg).equals( "Show Frame" )){ frameWindow.resetCount() ; frameWindow.showCount() ; frameWindow.show() ; showFrame.setLabel( "Hide Frame" ) ; // the event has been handled return true ; } else if (((String)arg).equals( "Hide Frame" )){ frameWindow.hide() ; showFrame.setLabel( "Show Frame" ) ; // the event has been handled return true ; } } // the event has not been handled return false ; } }
If you run this applet in a browser, you may notice that the Frame window has a status bar with the message Untrusted Applet Window, Warning Applet Window , or Unsigned Java Applet Window. These warnings are displayed by browser implementations of Java whenever a Java applet creates a Frame window. This prevents Frame windows from masquerading as a local application.
The AWT contains a group of classes designed to handle placement of controls in Container objects. These are the layout classes. All layout classes are derived directly from Object. These classes all implement the LayoutManager interface.
The AWT implements the following layout classes:
These classes provide a flexible, platform-independent means of arranging Component objects in your Container objects. It is possible that these five classes provide all the flexibility your applets and applications need. If you have specific needs, you can implement your own layout class by deriving it from Object and implementing the LayoutManager interface.
The FlowLayout class allows you to lay out controls in rows. Controls are placed in rows as long as there is room. After a row has been filled, subsequent controls are placed in the next row. Figure 16.7 shows the flow applet.
The flow applet creates a simple FlowLayout:
import java.awt.*; public class flow extends java.applet.Applet { public void init() { setLayout( new FlowLayout() ) ; add( new Button( "One" ) ) ; add( new Button( "Two" ) ) ; add( new Button( "Three" ) ) ; add( new Button( "Four" ) ) ; add( new Button( "Five" ) ) ; } }
In this applet, the FlowLayout constructor is called with no parameters. There are two other overloaded constructors for the FlowLayout class. These allow you to fine tune the FlowLayout to meet your particular needs.
The first constructor takes one parameter. By passing FlowLayout.LEFT, FlowLayout.CENTER, or FlowLayout.RIGHT, you specify the alignment for the controls. The default alignment (when you don't specify one) is FlowLayout.CENTER. Therefore, to align the buttons on the left in the flow applet, you replace the call to setLayout() with this call:
setLayout( new FlowLayout( FlowLayout.LEFT ) ) ;
Layouts also give you control over the amount of space between controls. The FlowLayout() method fills the first row and then each subsequent row as necessary. If the layout requires more than one row, you can specify the vertical spacing as well. The constructor that does this takes an alignment parameter followed by two parameters specifying the spacing between controls. To make the flow applet place its controls centered with ten pixels of horizontal gap and five pixels of vertical gap, use the following code:
setLayout( new FlowLayout( FlowLayout.CENTER, 10, 5 ) ) ;
The AWT BorderLayout class places controls so that they fill their Container object. The controls are placed according to a geographic position that you specify. Controls can be placed on the north, south, east, and west edges of the Container. You can also place a control in the center of the Container. The centered control is then expanded to fill the remaining space.
Figure 16.8 shows the border applet with five controls.
Figure 16.8: The border applet.
The border applet creates a simple BorderLayout:
import java.awt.*; public class border extends java.applet.Applet { public void init() { setLayout( new BorderLayout() ) ; add( "North", new Button( "NORTH" ) ) ; add( "South", new Button( "SOUTH" ) ) ; add( "East", new Button( "EAST" ) ) ; add( "West", new Button( "WEST" ) ) ; add( "Center", new Button( "CENTER" ) ) ; } }
When you create a BorderLayout, you can specify vertical and horizontal gap values as you can with the setLayout() method (described in the flow applet).
The AWT CardLayout class is unique. Rather than placing multiple controls in a Container object, this layout displays the controls one at a time (much like the familiar deck of cards in the ubiquitous Solitaire game). The controls that are displayed may, in fact, be composite controls. Therefore, you can present entirely different sets of controls to the user in a manner similar to the tabbed dialog boxes that Microsoft Windows uses. Figure 16.9 shows an applet with five buttons laid out in a CardLayout fashion.
The card applet creates a CardLayout with five buttons:
import java.awt.*; public class card extends java.applet.Applet { CardLayout layout ; public void init() { layout = new CardLayout() ; setLayout( layout ) ; add( new Button( "First" ) ) ; add( new Button( "Second" ) ) ; add( new Button( "Third" ) ) ; add( new Button( "Fourth" ) ) ; add( new Button( "Fifth" ) ) ; } public boolean action( Event evt, Object arg ) { if ( evt.target instanceof Button ) { layout.next(this) ; return true ; } return false ; } }
The card applet places five Button objects in a CardLayout. When any button is pressed, the action() method calls the CardLayout's next() method to display the next card in order. This layout also allows you to label the various controls that are added.
The add() method takes an optional String parameter that labels the controls you add. The following call adds a Button labeled my button with the label Push Me:
add( "my button", new Button( "Push Me" ) ;
Once the controls have labels, you can display them without having to show them in order, without calling the layout's next() method. To display my button, simply call the layout's show() method:
show( this, "my button" ) ;
The show() method displays a specified control; the next() method displays the next control in order. The CardLayout class provides the following functions to navigate the controls in the layout:
All these navigational functions take a reference to a Container
object as a parameter. The show()
method takes a String containing
the label given to the control when it was added.
Note |
What's this? In Java, this is a keyword that represents a reference to a given object. When an applet calls one of the CardLayout's navigational methods, it needs a Container as a parameter. Because these functions are called in the context of a Container member method, this represents the current Container. |
As its name suggests, the GridLayout class places controls in the Container in a grid. It is important to note that this is a regular grid-all the grid cells are the same size. The applet in Figure 16.10 shows a GridLayout.
Figure 16.10: The grid applet.
The grid applet defines a grid with two rows and three columns. The add() method adds each control starting with row 1, column 1 followed by row 1, column 2 and so on.
import java.awt.*; public class grid extends java.applet.Applet { public void init() { setLayout( new GridLayout( 2, 3 ) ) ; add( new Button( "One" ) ) ; add( new Button( "Two" ) ) ; add( new Button( "Three" ) ) ; add( new Button( "Four" ) ) ; add( new Button( "Five" ) ) ; } }
You can also create a GridLayout with vertical and horizontal gap values by using the setLayout() method as you do with the FlowLayout and BorderLayout classes.
The GridBagLayout class is complex enough to fill an entire chapter by itself. This class was added to the AWT very late in the Java beta. Therefore, many early acceptors of Java didn't use this layout at all. Some early books omit it completely.
Of all the layouts offered by the AWT, GridBagLayout is the most versatile. Despite its funny name, once you learn how to use GridBagLayout, it will become an indispensable part of your Java toolkit.
Like GridLayout, GridBagLayout places controls in a Container in a grid. The difference is that in a GridBagLayout, controls can span any number of grid cells vertically, horizontally, or both. Controls can be placed in any grid cell. Cells can be of differing sizes as well. Figure 16.11 shows the gridbag applet.
Figure 16.11: The Gridbag applet.
The gridbag applet displays five buttons in a GridBagLayout arrangement:
import java.awt.*; public class gridbag extends java.applet.Applet { public void init() { Button b1 = new Button( "One" ) ; Button b2 = new Button( "Two" ) ; Button b3 = new Button( "Three" ) ; Button b4 = new Button( "Four" ) ; Button b5 = new Button( "Five Thousand" ) ; GridBagLayout gridbag = new GridBagLayout(); setLayout( gridbag ) ; { GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH ; c.gridx = 1 ; c.gridy = 1 ; gridbag.setConstraints(b1, c); add( b1 ) ; } { GridBagConstraints c = new GridBagConstraints(); c.anchor = GridBagConstraints.WEST ; c.gridx = 2 ; c.gridheight = 2 ; gridbag.setConstraints(b2, c); add( b2 ) ; } { GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH ; c.gridx = 1 ; c.gridy = 2 ; c.gridwidth = 2 ; gridbag.setConstraints(b3, c); add( b3 ) ; } { GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH ; c.gridx = 1 ; c.gridy = 3 ; c.gridwidth = 3 ; gridbag.setConstraints(b4, c); add( b4 ) ; } { GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.VERTICAL ; c.gridx = 3 ; c.gridy = 1 ; c.gridheight = 2 ; gridbag.setConstraints(b5, c); add( b5 ) ; } } }
The key to using GridBagLayout is the GridBagConstraints class. This class is used to encapsulate information about each control that is added to the layout. Setting the class data members determines how the controls will be placed.
To use a GridBagLayout, you
must create a GridBagConstraints
object. Then set the data members of the GridBagConstraints
object to appropriately lay out the given control. Next, call
the GridBagLayout's setConstraints()
method to associate a GridBagConstraints
object with a control. Finally, add the control.
Note |
C and C++ programmers may be asking why GridBagConstraints is a class. It appears to have only data members. In fact, if this were C or C++, GridBagConstraints would be a structure. Because Java does not support structures, GridBagConstraints must be implemented as a class. |
The following GridBagConstraints public data members determine how your controls are placed:
By using the GridBagLayout and GridBagConstraints classes, you can produce layouts to meet nearly all your needs. If these are not flexible enough for you, there is always the option of creating your own LayoutManager. You create your own LayoutManager by creating a class (subclassed from Object) that implements the LayoutManager interface.
The AWT provides a hierarchy of classes that allow you to include menus in your applets and applications. Figure 16.12 shows the AWT menu classes.
Figure 16.12: The AWT menu class hierarchy.
All the menu classes are derived from the MenuComponent class. The MenuBar and Menu classes both implement the MenuContainer interface.
The MenuFrame applet shows a Frame with a menu. Selecting Hide Frame from the menu closes the frame window. Figure 16.13 shows the MenuFrame applet.
Figure 16.13: The MenuFrame applet.
The MenuFrame applet creates MenuItem, Menu, and MenuBar objects. The Frame's action() method determines whether the Hide Frame menu item has been selected.
import java.awt.*; public class MenuFrame extends java.applet.Applet { DemoFrame frameWindow ; Button showFrame ; public void init() { showFrame = new Button( "Show Frame" ) ; add( showFrame ) ; frameWindow = new DemoFrame( "This is a Frame" ) ; frameWindow.resize( 200, 100 ) ; } public boolean action( Event evt, Object arg ) { if ( evt.target instanceof Button ) { if ( !frameWindow.isShowing() ) { frameWindow.show() ; return true ; } } return false ; } } class DemoFrame extends Frame { MenuItem menuItem ; Menu menu ; MenuBar menuBar ; Label label ; DemoFrame( String title ) { super( title ) ; menuItem = new MenuItem( "Hide Frame" ) ; menu = new Menu( "My Menu" ) ; menu.add( menuItem ) ; menuBar = new MenuBar() ; menuBar.add( menu ) ; setMenuBar( menuBar ) ; } public boolean action( Event evt, Object arg ) { if ( evt.target instanceof MenuItem ) { if ( ((String)arg).equals( "Hide Frame" ) ) { hide() ; return true ; } } return false ; }
The Java AWT contains a rich collection of controls. By using these controls, you can create a variety of truly multiplatform GUI applications. The AWT also provides generic container and window classes you can use to create your own custom controls.
The layout classes of the AWT answer the question of how to place controls so that they appear properly on all platforms. You can even create custom layout managers.
Java and the AWT provide a credible solution to developing fully featured GUI applications for a wide variety of target platforms.