Overview of GUIs in Java

Written by MageLang Institute

A GUI consists of a set of components (widgets), such as buttons and text fields, laid out in some form in an applet window. The components respond to events, initiated by physical stimuli such as mouse clicks or key presses, by changing the state of the display or modifying some internal data structure. For example, here is a Button object that, when pushed, increments an integer in a TextField object:



All of the widgets that can be added to an applet are subclasses of Component and Java provides a number of them, although you can define your own by subclassing the Canvas Component.

How do you make a widget appear in an applet? The basic formula is:

  1. Create the Component with the Java new operator. For example,
    Button b = new Button("Hit me");
    TextField tf = new TextField("0");
    
  2. Add the Component to the applet. For example,
    add(b);
    add(tf);
    

How does adding a Component to an applet cause it to appear? To answer that, it is useful to understand the notion of a Container. Containers contain Component objects that are to appear within the confines of the visible regions associated with the Containers. In effect, the simple act of adding a Component to a Container causes the underlying window toolkit (such as Motif) to physically draw the Component in the Container's region (see Details of the Component Architecture). For example, an Applet object is a kind of Container associated with a rectangular region inside a web browser window (Applet is a subclass of Panel, which is a subclass of Container). The init lifecycle method can be defined to add a few Component objects, thus, making them visible to the user:

class Counter extends java.awt.Applet {
    Button b;
    TextField tf;
    public void init() {
        b = new Button("Hit me");
        tf = new TextField("0");
        add(b);
        add(tf);
    }
}

The "containment hierarchy" created by the Counter applet can be viewed as follows:

This type of hierarchy diagram will become a useful learning tool when you learn about partitioning the applet region with nested containers and when you learn about events.

The first big question people have about Java GUI construction is "How do I get the widgets laid out the way I want?" In other words, why is it that, in the Counter applet, the TextField appears to the right of the Button? The first possibility that comes to mind is that components are placed at specific x,y coordinates. While this approach works, it is tedious to specify coordinates for every Component and does not stretch or reposition the components according to different applet window sizes. Java's approach is to use a layout manager for each Container object.

A layout manager is an object that positions and resizes the components in a Container according to some algorithm; for example, the FlowLayout layout manager lays out components from left to right until it runs out of room and then continues laying out components below that row. FlowLayout is the default layout manager for applets and, therefore, the Button and TextField that were added appeared next to each other on a row; the Button is positioned on the left because it was added to the applet first. If the applet region were more narrow than wide, then the HTML invocation:

<applet code=Counter.class codebase=Examples
width=50 height=100></applet>
would result in a different physical layout even though the Java source code was not modified nor recompiled:


Rarely will one layout manager be sufficient to position all of the components in your applet--the applet region will have to be broken down into subregions, each of them a Container with a layout manager. The various nested Container objects will look just like Component objects and will be added to your applet in the same manner.

Now that you are familiar with how GUI components are created and placed in the applet region, you must consider the question: "How do I make the components of my GUI respond to user stimuli?" Another way to ask the same question is to ask "where do I define a method so that it is called when I push a certain Button object with the mouse?" The answer is that you have a choice. Either the Component that was activated responds, the Component's Container responds, or both respond. The containment hierarchy illustrates this nicely:

The activated Component is normally given a chance to respond by calling its action method. The default action method for Button and any Component is to do nothing but return false, which indicates that the Component did not handle the event. Returning false from a Component event handler lets the event flow through to the enclosing Container. For example, you can execute a code block upon a Button push in the Counter applet by defining an action method with the appropriate signature inside class Counter:

public boolean action(Event e, Object arg) {
    // get the text inside the TextField
    int v = Integer.parseInt(tf.getText());
    // increment its value and reset the TextField
    tf.setText(String.valueOf(v+1));
    return true;
}

The action method returns true to indicate that it has successfully handled the event.

Note that the action method of a Container will be called for any unhandled event generated for any contained Component. In the Counter example, a TextField as well as a Button are present. The Counter applet behavior is such that you want to include a statement in action to ignore events generated by the TextField (otherwise hitting return inside the TextField would increment it also). The complete Counter applet source is:

import java.awt.*;
class Counter extends java.awt.Applet {
    Button b;
    TextField tf;
    public void init() {
        b = new Button("Hit me");
        tf = new TextField("0");
        add(b);
        add(tf);
    }
    public boolean action(Event e, Object arg) {
        if ( e.target != b ) return false;
        int v = Integer.parseInt(tf.getText());
        tf.setText(String.valueOf(v+1));
        return true;
    }
}

Shouldn't the behavior of a Button press be encapsulated in the Button object rather than having one giant action method in the applet? What happened to object-oriented programming? Yes, it is best to encapsulate the action method within a Component by subclassing it to override action.

To this point, you have learned the procedure for creating GUIs using widgets such as buttons and for displaying information with text fields. But, what about drawing simple lines and displaying GIF files? An applet may draw a number of shapes such as lines, circles, and rectangles using the Graphics class. The Graphics class represents a graphics context or perspective that also allows you to display text and display images. For example, the applet lifecycle method paint is passed the Graphics context of the overall applet. Here is an applet that displays some text and draws a line from the upper-left corner to 50 pixels over and 30 pixels down:



The corresponding Java code is very simple:

import java.applet.*;
import java.awt.*;
public class DrawApplet extends Applet {
    public void paint(Graphics g) {
        g.drawLine(0,0,50,30);
        g.drawString("0,0 to 50,30", 35,15);
    }
}

GIF files may be displayed by loading and then drawing them such as in the following applet that displays duke:



The source code gets an image from the images subdirectory of the directory where the SimpleImage.class is stored and draws it when the applet is told to refresh the screen (in paint).

import java.applet.Applet;
import java.awt.*;
public class SimpleImage extends Applet {
    Image im;
    public void init() {
        // getCodeBase returns directory where SimpleImage
        // came from.
        im = getImage(getCodeBase(), "images/duke.gif");
    }
    public void paint(Graphics g) {
        // display upper-left of image at applet upper-left.
        // 'this' argument is the observer; drawImage
        // wants to know who to notify when image is
        // complete (images are loaded in drawImage)
        g.drawImage(im, 0, 0, this);
    }
}
See also Overview of AWT Graphics Support.