Technical Support
Discussion Forum
Online Training
Read About Java
Java In-Depth
Product Discounts
Membership Information

Java Cup Logo

JDC Home Page


Top
Back
Next
Online Training
shadowSearchFAQFeedback

Overview of GUIs in Java

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 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 helpful 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 becomes 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 ask 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="/applets/magelang/AWT-Training/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 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: "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 our 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 and 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.

Up to this point, we have discussed 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 can 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 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 can 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.