Previous | Next | Trail Map | Creating a User Interface | Overview of the Java UI


Event Handling

When the user acts on a Component -- clicking it or pressing the Return key, for example -- an Event object is created. The AWT event-handling system passes the Event up the Component hierarchy, giving each Component a chance to react to the event before the platform-dependent code that implements the Component fully processes it.

Each Component's event handler can react to an event in any of the following ways:

From a Component's view, the AWT event-handling system is more like an event-filtering system. Platform-dependent code generates an event, but Components get a chance to modify, react to, or intercept the event before the platform-dependent code fully processes the event. The following figure shows the chain of event handling for a TextField event in the example program.

Note: In the current release, mouse events are forwarded to Components after the platform-dependent code has fully processed the event. So although Components can intercept all keyboard events, they can't currently intercept mouse events.

Although the AWT defines a wide variety of Event types, the AWT doesn't see every event that occurs. Thus, not every user action becomes an Event. The AWT can see only those events that the platform-dependent code lets it see. For example, Motif text fields don't forward mouse move events to the AWT. For this reason, TextField subclasses or containers can't rely on getting mouse move events -- on Solaris, at least, they simply have no way of knowing that the event has occurred, since they don't receive an Event when the mouse moves. If you want access to a wide range of event types, you might need to implement a Canvas subclass, since the platform-dependent implementation of Canvas forwards all events.

The Event Object

Each event results in the creation of an Event(in the API reference documentation) object. An Event object includes the following information:

How to Implement an Event Handler

The Component class defines many event-handling methods, and you can override any of them. Except for one all-purpose method (handleEvent()), each event-handling method can be used for only one particular type of event. We recommend that you avoid the all-purpose method, if possible, and instead override the event-handling method that's specific to the type of event you need to handle. This approach tends to have fewer unintended side effects.

The Component class defines the following methods for responding to events (the event type each handles is listed after the method name):

When an event occurs, the event-handling method that matches the event type is called. Specifically, the Event is first passed to the handleEvent() method, which (in the default implementation of handleEvent()) calls the appropriate method for the event type.

The action() method is an especially important event-handling method. Only basic control components -- Button, Checkbox, Choice, List, MenuItem, and TextField objects -- produce action events. They do so when the user indicates somehow that the control should perform an action. For example, when the user clicks a button, an action event is generated. By implementing the action() method, you can react to user actions on controls without worrying about the low-level events, such as key presses and mouse clicks, that caused the action.

All the event-handling methods have at least one argument (the Event) and return a boolean value. The return value indicates whether the method completely handled the event. By returning false, the event handler indicates that the event should continue to be passed up the component hierarchy. By returning true, the event handler indicates that the event should not be forwarded any further. The handleEvent() method should almost always return super.handleEvent(), to ensure that all events are forwarded to the appropriate event-handling method.

Important: Like drawing methods, all event handler methods must execute quickly! Otherwise, they'll destroy the perceived performance of your program. If you need to perform some lengthy operation as the result of an event, do it by starting up another thread (or sending a request to another thread) to perform the operation. For help on using threads, see Threads of Control(in the Writing Java Programs trail).

In the example program, all the event handling is performed by ConversionPanels. They use the action() method to catch events resulting from user actions on the text field (TextField), and pop-up list (Choice). To catch events resulting from user actions on the slider (Scrollbar), they must use the handleEvent() method, since Scrollbars don't produce action events and Component doesn't define any methods specific to Scrollbar events.

Here is the ConversionPanel implementation of the action() and handleEvent() methods:

/** Respond to user actions on controls. */
public boolean action(Event e, Object arg) {
    if (e.target instanceof TextField) {
        setSliderValue(getValue());
        controller.convert(this);
        return true;
    }
    if (e.target instanceof Choice) {
        controller.convert(this);
        return true;
    } 
    return false;
}

/** Respond to the slider. */
public boolean handleEvent(Event e) {
    if (e.target instanceof Scrollbar) {
        textField.setText(String.valueOf(slider.getValue()));
        controller.convert(this);
    } 
    return super.handleEvent(e);
}

The methods simply make sure that the ConversionPanel's slider and text field both show the same value, and then ask the Converter object to update the other ConversionPanel. The action() method returns true if it handled the event. This stops the event from unnecessarily traveling further up the component hierarchy. If the action() method can't handle the event, it returns false, so its higher ups in the component hierarchy can have a look at the event. The handleEvent() method always returns super.handleEvent() so that every event will be fully processed.

A Note about the action() Method: Action events are high-level events. They're caused by one or more low-level events such as key and mouse presses. For this reason, it's OK to return true to stop action events from travelling up the component hierarchy after you've handled them -- the platform-specific code has already handled the key or mouse events that triggered the action, so it doesn't need to see the action event.

Note: If handleEvent() returned true or false (instead of calling its superclass's implementation), the action() method would never be called. Risks like this are part of the reason why we advise you to avoid implementing handleEvent() unless it's absolutely necessary.

The Keyboard Focus

Many components -- even those primarily operated with the mouse, such as buttons -- can be operated by the keyboard. For a key press to affect a component, the component must have the keyboard focus.

At any given time, at most one window and one component in that window can have the keyboard focus. How windows get the keyboard focus is system dependent. But once a window has the focus, you can use the Component requestFocus() method to request that a component get the focus.

When a component gets the focus, its gotFocus() method is called. When a component loses the focus, its lostFocus() method is called.


Previous | Next | Trail Map | Creating a User Interface | Overview of the Java UI