Java uses a delegation -based model for event handling: a source object fires an event, and an object interested in the event handles the event. The latter object is called a listener . Two things are needed for an object to be a listener for an event on a source object, as shown in Figure 14.3.
The listener object must be an instance of the corresponding event-listener interface to ensure that the listener has the correct method for processing the event. Java provides a listener interface for every type of GUI event. The listener interface is usually named XListener for XEvent , with the exception of MouseMotionListener . For example, the corresponding listener interface for ActionEvent is ActionListener ; each listener for ActionEvent should implement the ActionListener interface. Table 14.2 lists event types, the corresponding listener interfaces, and the methods defined in the listener interfaces. The listener interface contains the method(s), known as the handler(s) , invoked by the source object to process the event.
Event Class (Handlers) | Listener Interface | Listener Methods |
---|---|---|
ActionEvent | ActionListener | actionPerformed(ActionEvent) |
ItemEvent | ItemListener | itemStateChanged(ItemEvent) |
MouseEvent | MouseListener | mousePressed(MouseEvent) |
mouseReleased(MouseEvent) | ||
mouseEntered(MouseEvent) | ||
mouseExited(MouseEvent) | ||
mouseClicked(MouseEvent) | ||
MouseMotionListener | mouseDragged(MouseEvent) | |
mouseMoved(MouseEvent) | ||
KeyEvent | KeyListener | keyPressed(KeyEvent) |
keyReleased(KeyEvent) | ||
keyTyped(KeyEvent) | ||
WindowEvent | WindowListener | windowClosing(WindowEvent) |
windowOpened(WindowEvent) | ||
windowIconified(WindowEvent) | ||
windowDeiconified(WindowEvent) | ||
windowClosed(WindowEvent) | ||
windowActivated(WindowEvent) | ||
windowDeactivated(WindowEvent) | ||
ContainerEvent | ContainerListener | componentAdded(ContainerEvent) |
componentRemoved(ContainerEvent) | ||
ComponentEvent | ComponentListener | componentMoved(ComponentEvent) |
componentHidden(ComponentEvent) | ||
componentResized(ComponentEvent) | ||
componentShown(ComponentEvent) | ||
FocusEvent | FocusListener | focusGained(FocusEvent) |
focusLost(FocusEvent) | ||
AdjustmentEvent | AdjustmentListener | adjustmentValueChanged (AdjustmentEvent) |
The listener object must be registered by the source object. Registration methods are dependent on the event type. For ActionEvent , the method is addActionListener . In general, the method is named addXListener for XEvent . A source object may fire several types of events. For each event, the source object maintains a list of listeners and notifies all the registered listeners by invoking the handler on the listener object to respond to the event, as shown in Figure 14.4. (Figure 14.4 shows the internal implementation of a source class. It addresses the question how a handler is invoked.)
Now you have the answers to all your questions about the simple example in Listing 14.1. Since a JButton object fires ActionEvent , a listener object for ActionEvent must be an instance of ActionListener , so the listener class implements ActionListener in line 26. The source object invokes addActionListener(listener) to register a listner , as follows :
JButton jbt = new JButton( "OK" ); // Line 7 in Listing 14.1 ActionListener listener = new OKListener(); // Line 11 in Listing 14.1 jbt.addActionListener(listener ); // Line 12 in Listing 14.1
When you click the button, the JButton object fires an ActionEvent and passes it to invoke the listener's actionPerformed method to handle the event.
The event object contains information pertinent to the event, which can be obtained using the methods, as shown in Figure 14.5. For example, you can use e.getSource() to obtain the source object in order to determine whether it is a button, a check box, or a radio button. For an action event, you can use e.getWhen() to obtain the time when the event occurs.
A listener class is designed specifically to create a listener object for a GUI component (e.g., a button). The listener class will not be shared by other applications and therefore is appropriately defined inside the frame class as an inner class.
An inner class , or nested class, is a class defined within the scope of another class. The code in Figure 14.6(a) declares two separate classes, Test and A . The code in Figure 14.6(b) declares A as an inner class in Test .
The class InnerClass defined inside OuterClass in Figure 14.6(c) is another example of an inner class. An inner class may be used just like a regular class. Normally, you declare a class an inner class if it is only used by its outer class. An inner class has the following features:
An inner class is compiled into a class named OuterClassName $ InnerClassName .class. For example, the inner class A in Test is compiled into Test$A .class in Figure 14.6(b).
An inner class can reference the data and methods defined in the outer class in which it nests , so you do not need to pass the reference of an object of the outer class to the constructor of the inner class. For this reason, inner classes can make programs simple and concise .
An inner class can be declared with a visibility modifier subject to the same visibility rules applied to a member of the class.
An inner class can be declared static . A static inner class can be accessed using the outer class name . A static inner class cannot access non-static members of the outer class.
Objects of an inner class are often created in the outer class. But you can also create an object of an inner class from another class. If the inner class is non-static, you must first create an instance of the outer class, then use the following syntax to create an object for the inner class:
OuterClass.InnerClass innerObject = outerObject. new InnerClass();
If the inner class is static, use the following syntax to create an object for it:
OuterClass.InnerClass innerObject = new OuterClass.InnerClass();
Tip
A simple use of inner classes is to combine dependent classes into a primary class. This reduces the number of source files. It also makes class files easy to organize, since all the class files are named with the primary class as the prefix. For example, rather than creating two source files, Test.java and A.java, in Figure 14.6(a), you can combine class A into class Test and create just one source file, Test.java, in Figure 14.6(b). The resulting class files are Test.class and Test$A.class. |
Listing 14.1 can now be modified using an inner class.
Listing 14.2 is the same as Listing 14.1 except that the OKListener class is now an inner class. As an inner class, it can conveniently reference data and methods in the frame class. Since the listener class will not be used by applications outside the frame class, it is declared private .
The interaction between the source and the listener is shown in Figure 14.7.
jbtOK registers listener by invoking addActionListener(listener) .
jbtOK invokes listener 's actionPerformed method to process an ActionEvent .
Inner class listeners can be shortened using anonymous inner classes. An anonymous inner class is an inner class without a name. It combines declaring an inner class and creating an instance of the class in one step. An anonymous inner class is declared as follows:
new SuperClassName/InterfaceName() { // Implement or override methods in superclass or interface // Other methods if necessary }
Since an anonymous inner class is a special kind of inner class, it is treated like an inner class in many ways. In addition, it has the following features:
An anonymous inner class must always extend a superclass or implement an interface, but it cannot have an explicit extends or implements clause.
An anonymous inner class must implement all the abstract methods in the superclass or in the interface.
An anonymous inner class always uses the no-arg constructor from its superclass to create an instance. If an anonymous inner class implements an interface, the constructor is Object() .
An anonymous inner class is compiled into a class named OuterClassName$ n .class. For example, if the outer class Test has two anonymous inner classes, they are compiled into Test$1.class and Test$2.class.
Listing 14.3 is an example of rewriting the preceding example using anonymous inner classes.
1 import javax.swing.*; 2 import java.awt.event.*; 3 import java.awt.*; 4 5 public class SimpleEventDemoAnonymousInnerClass extends JFrame { 6 public SimpleEventDemoAnonymousInnerClass() { 7 JButton jbtOK = new JButton( "OK" ); 8 setLayout( new FlowLayout()); 9 add(jbtOK); 10 11 // Create and register anonymous inner class listener 12 jbtOK.addActionListener( new ActionListener() { 13 public void actionPerformed(ActionEvent e) { 14 System.out.println( "It is OK" ); 15 } 16 } ); 17 } 18 19 /** Main method */ 20 public static void main(String[] args) { 21 JFrame frame = new SimpleEventDemoAnonymousInnerClass(); 22 frame.setTitle( "SimpleEventDemoAnonymousInnerClass" ); 23 frame.setLocationRelativeTo( null ); // Center the frame 24 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 25 frame.setSize( 220 , 80 ); 26 frame.setVisible( true ); 27 } 28 } |
The anonymous listener (lines 12 “16) in this example works the same way as the inner class listener in the preceding example. The program is condensed using an anonymous inner class.
Anonymous inner classes are compiled into OuterClassName$#.class , where # starts at 1 and is incremented for each anonymous class encountered by the compiler. In this example, the anonymous inner class is compiled into SimpleEventDemoAnonymousInnerClass$1.class .
Pedagogical Note
Anonymous adapters make programs simple and concise, and enable handlers to access data and methods in the outer class. From here on, this book will use anonymous inner class event adapters to implement listeners. |
This example writes a program that displays two buttons , OK and Cancel, in a frame. A message is displayed on the console to indicate which button and when it is clicked, as shown in Figure 14.8. The program is given in Listing 14.4.
1 import javax.swing.*; 2 import java.awt.*; 3 import java.awt.event.*; 4 5 public class TestActionEvent extends JFrame { 6 private JButton jbtOK = new JButton( "OK" ); 7 private JButton jbtCancel = new JButton( "Cancel" ); 8 9 public TestActionEvent() { 10 setLayout( new FlowLayout()); 11 12 add(jbtOK); 13 add(jbtCancel); 14 15 jbtOK.addActionListener( new ActionListener() { 16 public void actionPerformed(ActionEvent e) { 17 System.out.println( "The " + e.getActionCommand() + " button " 18 + "is clicked at\n " + new java.util.Date(e.getWhen())); 19 } 20 }); 21 22 jbtCancel.addActionListener( new ActionListener() { 23 public void actionPerformed(ActionEvent e) { 24 System.out.println( "The " + e.getActionCommand() + " button " 25 + "is clicked at\n " + new java.util.Date(e.getWhen())); 26 } 27 }); 28 } 29 30 /** Main method */ 31 public static void main(String[] args) { 32 TestActionEvent frame = new TestActionEvent(); 33 frame.setTitle( "TestActionEvent" ); 34 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 35 frame.setSize( 220 , 80 ); 36 frame.setVisible( true ); 37 } 38 } |
The button objects jbtOK and jbtCancel are the source of ActionEvent . Anonymous inner class listeners are used to create listeners for jbtOK and jbtCancel in lines 15 and 22.
Clicking a button causes the actionPerformed method in the listener to be invoked. The e.getActionCommand() method returns the action command from the button (line 17). By default, a button's action command is the text of the button.
The e.getWhen() method returns the time of the action in milliseconds since January 1, 1970, 00:00:00 GMT. The Date class converts the time to year, month, date, hours, minutes, and seconds (line 18).
This example writes a program that demonstrates handling window events. Any subclass of the Window class can fire the following window events: window opened, closing, closed, activated, deactivated, iconified , and deiconified. The program in Listing 14.5 creates a frame, listens to the window events, and displays a message to indicate the occurring event. Figure 14.9 shows a sample run of the program.
1 import java.awt.event.*; 2 import javax.swing.JFrame; 3 4 public class TestWindowEvent extends JFrame { 5 public static void main(String[] args) { 6 TestWindowEvent frame = new TestWindowEvent(); 7 frame.setLocationRelativeTo( null ); // Center the frame 8 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 9 frame.setTitle( "TestWindowEvent" ); 10 frame.setSize( 220 , 80 ); 11 frame.setVisible( true ); 12 } 13 14 public TestWindowEvent() { 15 addWindowListener( new WindowListener() { 16 /** 17 * Handler for window deiconified event 18 * Invoked when a window is changed from a minimized 19 * to a normal state. 20 */ 21 public void windowDeiconified(WindowEvent event) { 22 System.out.println( "Window deiconified" ); 23 } 24 25 /** 26 * Handler for window iconified event 27 * Invoked when a window is changed from a normal to a 28 * minimized state. For many platforms, a minimized window 29 * is displayed as the icon specified in the window's 30 * iconImage property. 31 */ 32 public void windowIconified(WindowEvent event) { 33 System.out.println( "Window iconified" ); 34 } 35 36 /** 37 * Handler for window activated event 38 * Invoked when the window is set to be the user 's 39 * active window, which means the window (or one of its 40 * subcomponents) will receive keyboard events. 41 */ 42 public void windowActivated(WindowEvent event) { 43 System.out.println( "Window activated" ); 44 } 45 46 /** 47 * Handler for window deactivated event 48 * Invoked when a window is no longer the user's active 49 * window, which means that keyboard events will no longer 50 * be delivered to the window or its subcomponents. 51 */ 52 public void windowDeactivated(WindowEvent event) { 53 System.out.println( "Window deactivated" ); 54 } 55 56 /** 57 * Handler for window opened event 58 * Invoked the first time a window is made visible. 59 */ 60 public void windowOpened(WindowEvent event) { 61 System.out.println( "Window opened" ); 62 } 63 64 /** 65 * Handler for window closing event 66 * Invoked when the user attempts to close the window 67 * from the window's system menu. If the program does not 68 * explicitly hide or dispose the window while processing 69 * this event, the window close operation will be cancelled. 70 */ 71 public void windowClosing(WindowEvent event) { 72 System.out.println( "Window closing" ); 73 } 74 75 /** 76 * Handler for window closed event 77 * Invoked when a window has been closed as the result 78 * of calling dispose on the window. 79 */ 80 public void windowClosed(WindowEvent event) { 81 System.out.println( "Window closed" ); 82 } 83 } ); 84 } 85 } |
The WindowEvent can be fired by the Window class or by any subclass of Window . Since JFrame is a subclass of Window , it can fire WindowEvent .
TestWindowEvent extends JFrame and implements WindowListener . The WindowListener interface defines several abstract methods ( windowActivated , windowClosed , windowClosing , windowDeactivated , windowDeiconified , windowIconified , windowOpened ) for handling window events when the window is activated, closed, closing, deactivated, deiconified, iconified, or opened.
When a window event, such as activation, occurs, the windowActivated method is triggered. Implement the windowActivated method with a concrete response if you want the event to be processed .
Because the methods in the WindowListener interface are abstract, you must implement all of them even if your program does not care about some of the events. For convenience, Java provides support classes, called convenience adapters , which provide default implementations for all the methods in the listener interface. The default implementation is simply an empty body. Java provides convenience listener adapters for every AWT listener interface with multiple handlers. A convenience listener adapter is named X Adapter for X Listener. For example, WindowAdapter is a convenience listener adapter for WindowListener . Table 14.3 lists the convenience adapters.
Adapter | Interface |
---|---|
WindowAdapter | WindowListener |
MouseAdapter | MouseListener |
MouseMotionAdapter | MouseMotionListener |
KeyAdapter | KeyListener |
ContainerAdapter | ContainerListener |
ComponentAdapter | ComponentListener |
FocusAdapter | FocusListener |
Using WindowAdapter , the preceding example can be simplified as shown in Listing 14.6 if you are only interested in the window activated event. The WindowAdapter class is used to create an anonymous listener instead of WindowListener (line 15). The windowActivated handler is implemented in line 16.
1 import java.awt.event.*; 2 import javax.swing.JFrame; 3 4 public class AdapterDemo extends JFrame { 5 public static void main(String[] args) { 6 AdapterDemo frame = new AdapterDemo(); 7 frame.setLocationRelativeTo( null ); // Center the frame 8 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 9 frame.setTitle( "AdapterDemo" ); 10 frame.setSize( 220 , 80 ); 11 frame.setVisible( true ); 12 } 13 14 public AdapterDemo() { 15 addWindowListener( new WindowAdapter() { 16 public void windowActivated(WindowEvent event) { 17 System.out.println( "Window activated" ); 18 } 19 }); 20 } 21 } |