Overview of the Java UI |
This section describes the 1.0 event scheme. See Introduction to the New AWT Event Model for a description of the new model.
Note: You should use the new event model whenever possible. It is intended to replace the event model described on this page.
In 1.0, 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:
- By ignoring the event and allowing it to be passed up the Component hierarchy. This is what the default Component implementation does. For example, since the TextField class and its TextComponent superclass implement no event handlers, TextFields get the default Component implementation. So when a TextField receives an Event, it ignores it and allows its container to handle it.
- By modifying the Event instance before it goes further up the hierarchy. For example, a TextField subclass that displays all letters in uppercase might react to the keypress of a lowercase letter by changing the Event to contain the uppercase version of the letter.
- By reacting in some other way to the event. For example, a TextField subclass (or a TextField's container) could react to a Return keypress by calling a method that processes the text field's contents.
- By intercepting the event, stopping it from being processed further. For example, if an invalid character is entered in a text field, an event handler could simply stop the resulting Event from propagating upward. As a result, the platform-dependent implementation of the text field would never see the event.
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 1.0, 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 object. An Event object includes the following information:
- The type of the event -- for example, a key press or mouse click, or a more abstract event such as an "action" or window iconification.
- The object that was the "target" of the event -- for example, the Button corresponding to the onscreen button the user clicked, or the TextField corresponding to the field that user just typed in.
- A timestamp indicating when the event occurred.
- The (x,y) location where the event occurred. This location is relative to the origin of the Component whose event handler this Event is passed into.
- The key that was pressed (for keyboard events).
- An arbitrary argument (such as the string displayed on the Component) associated with the Event.
- The state of modifier keys, such as Shift and Control, when the event occurred.
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):
action()
(Event.ACTION_EVENT)mouseEnter()
(Event.MOUSE_ENTER)mouseExit()
(Event.MOUSE_EXIT)mouseMove()
(Event.MOUSE_MOVE)mouseDown()
(Event.MOUSE_DOWN)mouseDrag()
(Event.MOUSE_DRAG)mouseUp()
(Event.MOUSE_UP)keyDown()
(Event.KEY_PRESS or Event.KEY_ACTION)keyUp()
(Event.KEY_RELEASE or Event.KEY_ACTION_RELEASE)gotFocus()
(Event.GOT_FOCUS)lostFocus()
(Event.LOST_FOCUS)handleEvent()
(all event types)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 ofhandleEvent()
) 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 theaction()
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 returningtrue
, the event handler indicates that the event should not be forwarded any further. ThehandleEvent()
method should almost always returnsuper.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 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 thehandleEvent()
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()
andhandleEvent()
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 returnstrue
if it handled the event. This stops the event from unnecessarily traveling further up the component hierarchy. If theaction()
method can't handle the event, it returns false, so its higher ups in the component hierarchy can have a look at the event. ThehandleEvent()
method always returnssuper.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 returntrue
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()
returnedtrue
orfalse
(instead of calling its superclass's implementation), theaction()
method would never be called. Risks like this are part of the reason why we advise you to avoid implementinghandleEvent()
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, itslostFocus()
method is called.
Overview of the Java UI |