This article focuses on the building blocks of Java's Graphical User Interface (GUI) building package, which is called java.awt, or just the AWT for short. AWT stands for Abstract Windowing Toolkit. It provides a platform-independent set of tools used by the Java developer to create buttons, windows, checkboxes and other common GUI elements (widgets). The base class for all of Java's widgets is called Component. In addition to providing a wide variety of widgets, the AWT also provides a mechanism to execute code when something happens to the widget. When the user presses your button, or selects an item from your list, an event is generated. The event contains information about what just happened to the GUI. We will see later in this article how code can be specified that will handle the events generated by a GUI.
Java provides a wide variety of GUI accoutrements, shown in Table 1.
Even though these widgets will behave the same on different platforms, they will not look the same. A GUI running on a Windows machine will have a Windows look and feel while that same GUI running on a Macintosh or Solaris environment will have a Mac or Solaris look and feel. This is because awt widgets have native peers. This means, for example, that a Java Button has a corresponding native implementation. That is, the actual code that draws the button on the screen is written in C or C++ for the target machine that the Java VM is running on. Every widget has a peer that is implemented natively.
This peer architecture is completely hidden from the Java programmer. Java developers don't have to be concerned with the native implementation of widgets. Instead, developers work with the individual widget classes that make calls to their native counterparts behind the scenes.
All of Java's widgets inherit their base functionality from the abstract class Component. This means that all of Java's widgets share some behaviors, states and the identity: Component. Component is a large class that defines many behaviors and it is interesting to note that there are more common behaviors defined in Component than differences between individual widget classes. So, even though a Button and a Choice look and behave dramatically differently, there are actually more common behaviors (defined in the base class Component) than differences.
Look at the API documentation for class Component. There are many methods with confusing names. It is difficult to guess what a call to those methods will result in. Don't be alarmed. Many of the methods defined in Component are meant for other classes to call, not the programmer (unless the programmer is doing some complicated operation). However, some important methods should be noted.
Since all Components, be they buttons or scroll bars, have a size, the behaviors that deal with size are defined in this base class. All of these methods return an instance of class Dimension. A Dimension has two data members, length and width, which are adequate to describe a size. The method getMaximumSize() will return the maximum size that a Component (widget) can be. getMinimumSize(), getPreferredSize(), will return a Dimension reflecting the Component's desired minimum and preferred sizes. The getSize() method will indicate a Component's current size. Method setSize(Dimension d) allows the programmer to size a Component on the screen. However, this method should rarely be used by application developers. The use of this method will be discussed in a later column on LayoutManagers.
It is important to realize here that since all descendants of Component need to manage their sizes, the behaviors are defined in this base class. This is a common practice in object-oriented programming. Leveraging inheritance makes classes more organized, encourages code reuse and eases debugging. Imagine if these methods were defined in every widget class. Repeating these declarations is wasteful. Furthermore, if a bug arises, the programmer only has to look in one place (the base class) to track the error.
Methods setVisible(boolean b), set Background(Color c), setEnabled(boolean b) are other methods commonly used when programming with Components. They are all self explanatory and won't be explained further except to note that they too are common behaviors of all of Components child classes. Component also has several methods of the form:
add<some KIND OF EVENT>Listener( <some KIND OF EVENT LISTENER> listener )
These methods are used in event handling. Java 1.1 uses a different system of event handling than the 1.0 release. In 1.1, any class can be an event listener simply by implementing an event listener interface. An event listener is an object that is interested in hearing about certain kinds of events. For example, if an object is interested in knowing whether a particular Textfield has gained or lost focus, it would implement the FocusListener interface. Components keep an internal list of event listeners that are interested in hearing about events generated by that Component. When an event is generated, the Component will call a particular method (depending on the event type) in all of the listeners kept in the internal list. Event listeners are added to the internal list by the add<some KIND OF EVENT>Listener(<some KIND OF EVENT LISTENER> listener ) method. For example, if a Textfield wanted to alert a certain FocusListener when it loses focus, the addFocusListener method would be called, passing in the FocusListener as an argument. Take a look at Listing 1 for the details.
Java provides several kinds of event listeners. They are defined in the java.awt.event package. Probably the most commonly used one is ActionListener. An ActionEvent is an event that gets generated when a Component performs its primary purpose in life. For example, when a Button is pressed, an ActionEvent is generated. The ActionListener interface has only one method to implement. ActionPerformed(ActionEvent e) gets called whenever an ActionListener receives an event.
The method addActionEventListener(ActionEventListener a) is not defined in Component. Instead, a Component can add ComponentListeners, FocusListeners, KeyListeners, MouseListeners and MouseMotionListeners.
Widgets are not the only kind of descendant classes of Component. Some children of Component are used only to hold other Components. After all, what is the structure that holds Buttons, Scrollbars and Textareas? The other descendant of Component is called Container. A Container is a Component whose sole purpose in life is to hold other Components.
Container, like Component, is an abstract class. The children of class Container are Panel, ScrollPane, Window, Dialog, Frame and FileDialog. A Panel is an area of the screen that can contain other Components. Panels are very common in Java and it is interesting to note that class Applet extends Panel. The reasoning behind this is that an Applet has to contain Components to display to a user. Container too defines behaviors that are common to all of its children. A Container is aware of which Components it holds, and has several getComponent methods to report this information. A Container also provides the ability to add a Component, and there are several add methods.
The add methods all take a Component as an argument and this is where things can get interesting. A Container descends Component; therefore, it can be identified as a Component. So, using the add(Component) method, one can add a Container to a Container! This is often useful when placing Components at particular locations on the screen.
Which gives rise to the next question: How does one specify a location for a Component? There are methods defined in Component that can be used to explicitly declare a location, but it was said earlier that they should not be used. Where does a Component wind up when added to a Container?
A layout manager is defined for a Container. A layout manager is a class that defines rules for Component placement. There are default layout managers for Containers so you can do some experimenting without knowing too much about layout managers, but be prepared for unpredictable results! The next column will focus on using layout managers.
Your assignment is simple. Write an applet that has only one button. Create an ActionListener and add it to the button. In the actionPerformed method, use a System.out.println(String) to prove that the button was pressed. If you want to do more, try creating a MouseMotionListener and adding it to the Panel. Use System.out.println(String) to see what happens when you get mouse events.
About the Author
John V. Tabbone is a lecturer at New York University's Information Technologies Institute, where he teaches two Java programming courses and advises on curriculum development. He has been a professional Java programmer since early 1996 and continues to consult on and develop systems for a variety of New York-based businesses. You may e-mail him with questions and comments at [email protected]
Class MyFocusListener implements FocusListener
public void focusGained( FocusEvent fe )
System.out.println( “I get printed when the Component attains focus!” );
public void focusLost( FocusEvent fe )
System.out.println( “I get printed when the Component looses focus!” );
// elsewhere in the code
Textfield tf = new Textfield(),
MyFocusListener mfl = new MyFocusListener();
// Now, add the listener to the Component
tf.addFocusListener( mfl );