13.1 Handling Action Events in GUI Controls
After you create a component and add it to a window, you probably want to make it actually
do
something when the user interacts with it. In Java 1.02, you do this primarily by watching for "action" events, typically by overriding the action method in the component or its enclosing window. There are a few other events of interest (list selection and deselection events, keyboard events for textfields and text areas, scrolling events), but action events are the most important. In Java 1.1, action events are still important, but other important high-level events are generated as well. Action events are generated by Button, List, MenuItem, and TextField, and are handled either in processActionEvent or in another object designated via addActionListener. Item selection events are generated by Checkbox, CheckboxMenuItem, Choice, and List, and are handled in processItemEvent or in an object designated via addItemListener. Textfields and text areas generate both keyboard events and text events; they are handled in processKeyEvent and processTextEvent or in an object designated with addKeyListener or addTextListener. Scrollbars generate adjustment events; they are handled in processAdjustmentEvent or in an object designated with addAdjustmentListener. Furthermore, the mouse and keyboard events discussed in Chapter 10 are delivered to Java 1.1 GUI components, unlike in Java 1.0. Nevertheless, the higher-level events are still the primary event types of concern.
There are two basic approaches to handling action events: letting each component process its own events, or handling the events in a centralized location such as the enclosing window. The following four sections give an example of each of these approaches in Java 1.02 and Java 1.1. Except for the frame title, all four result in an identical interface and in identical behavior: a frame with three buttons that resize the frame when pressed. Figures 13-1 through 13-3 show the result after each of the three buttons is clicked.
Figure 13-1 Frame after pressing the first
button.
Figure 13-2 Frame after pressing the second
button.
Figure 13-3 Frame after pressing the third
button.
Processing Action Events in Individual Components in Java 1.0
The first possible approach to handling user interface events is to have a GUI element handle its own events. It does this by overriding the action method. For instance, in Listings 13.1 and 13.2, a custom subclass of Button (ResizeButton) is added to a Frame. When pressed, each of these buttons behave according to their
own
copy of action (which accesses per-button instance variables), resizing the frame as shown in Figures 13-1 through 13-3. This type of decentralized processing is advantageous in that it lets developers build components with behaviors independently of the application in which the components will be used. This can simplify development and maintenance. On the other hand, if the component's behavior affects the window the component is in, it has to gain access to it in order to carry out the action. In Listing 13.2, this was done by calling getParent. However, this is not always so easy. For one thing, that assumed the buttons wanted to act on their directly enclosing window. If an intermediate Panel was introduced, the getParent call would have to change. The ResizeButton could plan for this eventuality by finding the topmost Frame instead of just using getParent, but the point remains that it often needs to know something about the environment in which it will be used. Secondly, this approach will not work when the action to be carried out requires access to protected data in the enclosing window.
Listing 13.1 ActionExample1.java
|
import java.awt.*;
public class ActionExample1 extends QuittableFrame {
public static void main(String[] args) {
new ActionExample1();
}
public ActionExample1() {
super("JDK 1.02: Handling Events in Component");
setLayout(new FlowLayout());
setFont(new Font("TimesRoman", Font.BOLD, 18));
add(new ResizeButton(300, 200));
add(new ResizeButton(400, 300));
add(new ResizeButton(500, 400));
resize(400, 300);
show();
}
}
|
Listing 13.2 ResizeButton.java
|
import java.awt.*;
public class ResizeButton extends Button {
private int width, height;
public ResizeButton(int width, int height) {
super("Resize to " + width + "x" + height);
this.width = width;
this.height = height;
}
public boolean action(Event event, Object object) {
getParent().resize(width, height);
getParent().layout();
return(true);
}
}
|
Processing Action Events in Containers in Java 1.0
The second possible way to handle GUI control events in Java 1.0 is to have a window handle events for the elements it contains. You do this by overriding the action method in the window, then checking the event's target field to find the GUI elements of interest. Listing 13.3 gives an example of such a centralized event-processing routine. Here, action is overridden in the window holding the buttons, rather than in the Button class itself. The effect is the same however; the frame is resized according to which button is selected, as in Figures 13-1 through 13-3. In the ResizeButton class (Listing 13.2), the action could be taken whenever the action method was invoked. Here, however, you have to first determine which button was pressed before knowing which action should be performed. This kind of centralized processing does not require you to make a custom Component subclass, plus it lets the behavior be based on protected methods or data in the window. On the other hand, it makes it more difficult to build components separately from the applications in which they will be used. In many cases, it is a simple matter of preference. Try it both ways a few times and see which you prefer.
Listing 13.3 ActionExample2.java
|
import java.awt.*;
public class ActionExample2 extends QuittableFrame {
public static void main(String[] args) {
new ActionExample2();
}
private Button button1, button2, button3;
public ActionExample2() {
super("JDK 1.02: Handling Events in Container");
setLayout(new FlowLayout());
setFont(new Font("TimesRoman", Font.BOLD, 18));
button1 = new Button("Resize to 300x200");
button2 = new Button("Resize to 400x300");
button3 = new Button("Resize to 500x400");
add(button1);
add(button2);
add(button3);
resize(400, 300);
show();
}
public boolean action(Event event, Object object) {
if (event.target == button1) {
resize(300, 200);
layout();
return(true);
} else if (event.target == button2) {
resize(400, 300);
layout();
return(true);
} else if (event.target == button3) {
resize(500, 400);
layout();
return(true);
} else
return(false);
}
}
|
Processing Action Events in Individual Components in Java 1.1
In Java 1.1, the first alternative for handling GUI control events is to have an element handle its own events. You do this by primarily overriding processActionEvent, but some interface elements generate events other than action events. In such a case, you'd override process
Xxx
Event instead (e.g., processItemEvent, processTextEvent, and so forth). For example, Listings 13.4 and 13.5 show the Java 1.1 version of decentralized event handling. Except for the frame title, it generates the same interface and behavior as the previous two examples. It has similar advantages and disadvantages to the 1.0 version. Don't forget to enable events via enableEvents before trying to process them; Java 1.1 doesn't send events unless you ask for them. Also, you want to call super.processActionEvent from within processActionEvent, so that any action listeners attached to the control will still be activated normally. Finally, remember to import java.awt.event.*; that's where all the event types are defined. For details on enabling and processing events, see Section 10.5 (Handling Events in Java 1.1).
Listing 13.4 ActionExample3.java
|
import java.awt.*;
// JDK 1.1 Only
public class ActionExample3 extends CloseableFrame {
public static void main(String[] args) {
new ActionExample3();
}
public ActionExample3() {
super("JDK 1.1: Handling Events in Component");
setLayout(new FlowLayout());
setFont(new Font("TimesRoman", Font.BOLD, 18));
add(new SetSizeButton(300, 200));
add(new SetSizeButton(400, 300));
add(new SetSizeButton(500, 400));
setSize(400, 300);
setVisible(true);
}
}
|
Listing 13.5 SetSizeButton.java
|
import java.awt.*;
import java.awt.event.*;
public class SetSizeButton extends Button {
private int width, height;
public SetSizeButton(int width, int height) {
super("Resize to " + width + "x" + height);
this.width = width;
this.height = height;
enableEvents(AWTEvent.ACTION_EVENT_MASK);
}
public void processActionEvent(ActionEvent event) {
getParent().setSize(width, height);
getParent().doLayout();
super.processActionEvent(event); // Handle listeners
}
}
|
Processing Action Events in Other Objects in Java 1.1
In Java 1.1, the second way to process GUI element events is to pass the events to an external object. You first create an object that implements the ActionListener interface, defining a method called actionPerformed that takes an ActionEvent as an argument. You then associate this object with the button via addActionListener. Listing 13.6 gives an example of this. In this particular case, the object was the window itself, but there is no reason it has to be. Although it is sometimes convenient to handle events in the window itself, inner classes let you pass events to external objects and still access protected data in the window. See Section 10.5 (Handling Events in Java 1.1) for more details on using inner classes for event handling, plus I'll give several examples in this chapter.
Listing 13.6 ActionExample4.java
|
import java.awt.*
import java.awt.event.*;
// JDK 1.1 Only
public class ActionExample4 extends CloseableFrame
implements ActionListener {
public static void main(String[] args) {
new ActionExample4();
}
private Button button1, button2, button3;
public ActionExample4() {
super("JDK 1.1: Handling Events in Other Object");
setLayout(new FlowLayout());
setFont(new Font("TimesRoman", Font.BOLD, 18));
button1 = new Button("Resize to 300x200");
button2 = new Button("Resize to 400x300");
button3 = new Button("Resize to 500x400");
button1.addActionListener(this);
button2.addActionListener(this);
button3.addActionListener(this);
add(button1);
add(button2);
add(button3);
setSize(400, 300);
setVisible(true);
}
public void actionPerformed(ActionEvent event) {
if (event.getSource() == button1) {
setSize(300, 200);
doLayout();
} else if (event.getSource() == button2) {
setSize(400, 300);
doLayout();
} else if (event.getSource() == button3) {
setSize(500, 400);
doLayout();
}
}
}
|