Any operating environment that supports GUIs constantly monitors events such as keystrokes or mouse clicks. The operating environment reports these events to the programs that are running. Each program then decides what, if anything, to do in response to these events. In languages like Visual Basic, the correspondence between events and code is obvious. One writes code for each specific event of interest and places the code in what is usually called an event procedure. For example, a Visual Basic button named HelpButton would have a HelpButton_Click event procedure associated with it, and VB would activate the code in this procedure in response to that button being clicked. Each Visual Basic user interface component responds to a fixed set of events, and it is impossible to change the events to which a Visual Basic component responds. On the other hand, if you use a language like raw C to do event-driven programming, you need to write the code that checks the event queue constantly for what the operating environment is reporting. (You usually do this by encasing your code in a giant loop with a massive switch statement!) This technique is obviously rather ugly, and, in any case, it is much more difficult to code. The advantage is that the events you can respond to are not as limited as in languages, like Visual Basic, that go to great lengths to hide the event queue from the programmer. Java takes an approach somewhat between the Visual Basic approach and the raw C approach in terms of power and, therefore, in resulting complexity. Within the limits of the events that the AWT knows about, you completely control how events are transmitted from the event sources (such as buttons or scrollbars) to event listeners. You can designate any object to be an event listener-in practice, you pick an object that can conveniently carry out the desired response to the event. This event delegation model gives you much more flexibility than is possible with Visual Basic, where the listener is predetermined, but it requires more code and is more difficult to untangle (at least until you get used to it). Event sources have methods that allow you to register event listeners with them. When an event happens to the source, the source sends a notification of that event to all the listener objects that were registered for that event. As one would expect in an object-oriented language like Java, the information about the event is encapsulated in an event object. In Java, all event objects ultimately derive from the class java.util.EventObject. Of course, there are subclasses for each event type, such as ActionEvent and WindowEvent. Different event sources can produce different kinds of events. For example, a button can send ActionEvent objects, whereas a window can send WindowEvent objects. To sum up, here's an overview of how event handling in the AWT works.

You register the listener object with the source object by using lines of code that follow the model:

eventSourceObject.addEventListener(eventListenerObject);

For example,

ActionListener listener = . . .;
JButton button = new JButton("Ok");
button.addActionListener(listener);

Now the listener object is notified whenever an "action event" occurs in the button. For buttons, as you might expect, an action event is a button click. Code like the above requires that the class to which the listener object belongs implements the appropriate interface (which in this case is the ActionListener interface). As with all interfaces in Java, implementing an interface means supplying methods with the right signatures. To implement the ActionListener interface, the listener class must have a method called actionPerformed that receives an ActionEvent object as a parameter.

class MyListener
 implements ActionListener
{
 . . .
 public void actionPerformed(ActionEvent event)
 {
 // reaction to button click goes here
 . . .
 }
}

Whenever the user clicks the button, the JButton object creates an ActionEvent object and calls listener.actionPerformed(event) passing that event object. It is possible for multiple objects to be added as listeners to an event source such as a button. In that case, the button calls the actionPerformed methods of all listeners whenever the user clicks the button. shows the interaction between the event source, event listener, and event object.

Event notification

Java graphics 08fig01Java graphics notes_icon

In this chapter, we put particular emphasis on event handling for user interface events such as button clicks and mouse moves. However, the basic event handling architecture is not specific to user interfaces. For example, in the JavaBeans chapter of Volume 2 you will see how to a component can fire events when its properties change.

Example: Handling a button click

As a way of getting comfortable with the event delegation model, let's work through all details needed for the simple example of responding to a button click. For this example, we will want:

With this scenario, each time a user clicks on any of the buttons on the panel, the associated listener object then receives an ActionEvent that indicates a button click. In our sample program, the listener object will then change the background color of the panel. Before we can show you the program that listens to button clicks, we first need to explain how to create buttons and how to add them to a panel. (For more on GUI elements, please see .) You create a button by specifying a label string, an icon, or both in the button constructor. Here are two examples:

JButton yellowButton = new JButton("Yellow");
JButton blueButton = new JButton(new ImageIcon("blue-ball-java-image.gif"));

Adding buttons to a panel occurs through a call to a method named (quite mnemonically) add. The add method takes as a parameter the specific component to be added to the container. For example,

class ButtonPanel extends JPanel
{
 public ButtonPanel()
 {
 JButton yellowButton = new JButton("Yellow");
 JButton blueButton = new JButton("Blue");
 JButton redButton = new JButton("Red");
 add(yellowButton);
 add(blueButton);
 add(redButton);
 }
}

shows the result.

A panel filled with buttons

Java graphics 08fig02

Now that you know how to add buttons to a panel, you'll need to add code that lets the panel listen to these buttons. This requires classes that implement the ActionListener interface, which, as we just mentioned, has one method: actionPerformed, whose signature looks like this:

public void actionPerformed(ActionEvent event)

Java graphics notes_icon

The ActionListener interface we used in the button example is not restricted to button clicks. It is used in many separate situations such as:

You will see more details in this chapter and the next. The way to use the ActionListener interface is the same in all situations: the actionPerformed method (which is the only method in ActionListener) takes an object of type ActionEvent as a parameter. This event object gives you information about the event that happened.When a button is clicked, then we want to set the background color of the panel to a particular color. We store the desired color in our listener class.

class ColorAction implements ActionListener
{
 public ColorAction(Color c)
 {
 backgroundColor = c;
 }
 public void actionPerformed(ActionEvent event)
 {
 // set panel background color
 . . .
 }
 private Color backgroundColor;
}

We then construct one object for each color and set the objects as the button listeners.

ColorAction yellowAction = new ColorAction(Color.YELLOW);
ColorAction blueAction = new ColorAction(Color.BLUE);
ColorAction redAction = new ColorAction(Color.RED);
yellowButton.addActionListener(yellowAction);
blueButton.addActionListener(blueAction);
redButton.addActionListener(redAction);

For example, if a user clicks on the button marked "Yellow," then the actionPerformed method of the yellowAction object is called. Its backgroundColor instance field is set to Color.YELLOW, and it can now proceed to set the panel's background color. There is just one remaining issue. The ColorAction object doesn't have access to the panel variable. You can solve this problem in two ways. You can store the panel in the ColorAction object and set it in the ColorAction constructor. Or, more conveniently, you can make ColorAction into an inner class of the ButtonPanel class. Then its methods can access the outer panel automatically. (For more information on inner classes, see .) We will follow the latter approach. Here is how you place the ColorAction class inside the ButtonPanel class.

class ButtonPanel extends JPanel
{
 . . .
 private class ColorAction implements ActionListener
 {
 . . .
 public void actionPerformed(ActionEvent event)
 {
 setBackground(backgroundColor);
 // i.e. outer.setBackground(...)
 }
 private Color backgroundColor;
 }
}

Look closely at the actionPerformed method. The ColorAction class doesn't have a setBackground method. But the outer ButtonPanel class does. The methods are invoked on the ButtonPanel object that constructed the inner class objects. (Note again that outer is not a keyword in the Java coding language. We just use it as an intuitive symbol for the invisible outer class reference in the inner class object.) This situation is very common. Event listener objects usually need to carry out some action that affects other objects. You can often strategically place the listener class inside the class whose state the listener should modify. contains the complete program. Whenever you click one of the buttons, the appropriate action listener changes the background color of the panel.

Example ButtonTest.java

 1. import java.awt.*;
 2. import java.awt.event.*;
 3. import javax.swing.*;
 4.
 5. public class ButtonTest
 6. {
 7. public static void main(String[] args)
 8. {
 9. ButtonFrame frame = new ButtonFrame();
10. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
11. frame.show();
12. }
13. }
14.
15. /**
16. A frame with a button panel
17. */
18. class ButtonFrame extends JFrame
19. {
20. public ButtonFrame()
21. {
22. setTitle("ButtonTest");
23. setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
24.
25. // add panel to frame
26.
27. ButtonPanel panel = new ButtonPanel();
28. Container contentPane = getContentPane();
29. contentPane.add(panel);
30. }
31.
32. public static final int DEFAULT_WIDTH = 300;
33. public static final int DEFAULT_HEIGHT = 200;
34. }
35.
36. /**
37. A panel with three buttons.
38. */
39. class ButtonPanel extends JPanel
40. {
41. public ButtonPanel()
42. {
43. // create buttons
44.
45. JButton yellowButton = new JButton("Yellow");
46. JButton blueButton = new JButton("Blue");
47. JButton redButton = new JButton("Red");
48.
49. // add buttons to panel
50.
51. add(yellowButton);
52. add(blueButton);
53. add(redButton);
54.
55. // create button actions
56.
57. ColorAction yellowAction = new ColorAction(Color.YELLOW);
58. ColorAction blueAction = new ColorAction(Color.BLUE);
59. ColorAction redAction = new ColorAction(Color.RED);
60.
61. // associate actions with buttons
62.
63. yellowButton.addActionListener(yellowAction);
64. blueButton.addActionListener(blueAction);
65. redButton.addActionListener(redAction);
66. }
67.
68. /**
69. An action listener that sets the panel's background color.
70. */
71. private class ColorAction implements ActionListener
72. {
73. public ColorAction(Color c)
74. {
75. backgroundColor = c;
76. }
77.
78. public void actionPerformed(ActionEvent event)
79. {
80. setBackground(backgroundColor);
81. }
82.
83. private Color backgroundColor;
84. }
85. }

Java graphics notes_icon

If you have programmed graphical user interfaces in Java 1.0, then you may well be horribly confused after reading this section. In Java 1.0, life was simple: you didn't need to worry about listeners. Instead, you added code in methods like action and handleEvent to the classes that contained the user interface elements. For example, testing a button click would look like this:

class ButtonPanel
{
 . . .
 public boolean action(Event event, Object arg)
 {
 if (arg.equals("Yellow")) setBackground(Color.YELLOW);
 else if (arg.equals("Blue")) setBackground(Color.BLUE);
 else if (arg.equals("Red")) setBackground(Color.RED);
 return true;
 }
}

There are two important differences between the new event model and the older one:

  1. In Java 1.0, a button click is always received by the object that contains the button (that is, the panel). Now, information about the button click is sent only to objects that were added as an actionListener for the button.
  2. In Java 1.0, all events are caught in the action and handleEvent methods. Now, there are many separate methods (such as actionPerformed and windowClosing) that can react to events.

For simple programs, the old event model is easier to program (although whether it is conceptually as simple is another question). But for complex programs, the old event model has severe limitations. The new model, while initially more involved, is far more flexible and is potentially faster since events are sent only to the listeners that are actually interested in them.

java.swing.JButton 1.2

Java graphics api_icon