Developer

Managing Swinging events

Swing is elegant, flexible, and complicated. In the second installment of his Swing programming series, Lamont Adams takes a look at Swing's event model.


To my mind, the event model in Java’s Swing GUI library is at once extremely elegant and frightfully complicated. Unless you’ve dabbled in some high-end C++, you’ll be unlikely to have ever seen anything like it before. In this article, I’ll introduce you to event handling in Swing and try to take some of the complications out of it for you.

Miss something?
This is the second installment in a series of articles introducing Swing programming. Did you miss the first one?

I’ll assume you already have an understanding of event-driven programming, like what is meant by the generic term “event” and object-oriented concepts such as interfaces, implementations, instances, and classes. Throughout this article, I’ll be referring you to EventDemoApp, a simple JFrame application that listens for mouse movement, click, and key press events and displays messages about these events in a JList when they occur.EventDemoApp’s UI is shown in Figure A, and the code is in Listing A.

Figure A: EventDemoApp
Main window of EventDemoApp


In theory, handling a particular event is a simple, three-step process:
  1. 1.      Create an event listener that knows about the event you are interested in.
  2. 2.      Write the code that handles the event.
  3. 3.      Tell a component to notify the event listener when the event occurs.

In practice, step one can become a little complicated, since there are quite a few event listeners to choose from and several ways of using them. So I’ll gloss over step one for now and return to it after covering steps two and three.

Now, listen
First, though, what’s an event listener? Event listeners are classes that implement a standard interface, allowing them to be notified of certain groups of events. You write your event-handling code by implementing the methods of the event listener interface that represents the particular event or events you are interested in handling.

For example, the ActionListener class is a simple event listener that is designed to respond to mouse clicks and the pressing of the [Enter]/[Return] key—basically anything that would constitute a button click on your platform. EventDemoApp uses an instance of ActionListener called ActionEventListener to add a message to the EventListBox list box when a user clicks ClickMeButton. The code for ActionEventListener looks like this:
 
private class ActionEventListener
implements ActionListener {
 
   public void actionPerformed(ActionEvent e){
      EventList.addElement("Action Event from "
      + e.getSource().toString());
   }
}

When a user clicks ClickMeButton, Java calls the ActionEventListener.actionPerformed event method, which adds a message to EventListBox. The ActionEvent input parameter’s properties will hold useful information about the event, like getSource(), which returns a reference to the component that fired the event. All event methods in an event listener’s interface have a similar parameter (descended from EventObject) you can use to get information about the event.

Of course, we still have to tell ClickMeButton to notify ActionEventListener when an action event occurs. We do that by adding ActionEventListener as an action listener to ClickMeButton:
 
ActionEventListener ael =
new ActionEventListener(EventList);
ClickMeButton.addActionListener(ael);

Don’t worry about the EventList parameter I pass to ActionEventListener’s constructor; it’s simply a reference to EventListBox’s DefaultListModel, an object that manages the contents of a Jlist. (For more information, check out Sun’s JList tutorial.)

Since part of the message that ActionEventListener.actionPerformed adds to EventListBox is the name of the control that received the event, you might guess that multiple components can share the same event listener. Well, you’d be right; this is very much the case. In EventDemoApp, both ClickMeButton and the TextFieldTextEventField share the same action listener:
 
ClickMeButton.addActionListener(ael);
TextEventField.addActionListener(ael);

Sharing event listeners is an important feature of Swing, which can dramatically simplify your code. It’s also possible to have multiple event listeners of the same type attached to the same component, which can give your applications a lot of flexibility.

There are a great many event listener types supported by Swing. The hard part is often figuring out which one to use. I’ve listed some of the commonly used ones in Table A. The links in the table will take you to Sun’s tutorial page for that event listener:
Table A
Listener name Uses Important event methods
ActionListener
Reports actions: mouse clicks, [Enter]/[Return] key presses, etc.
actionPerformed(ActionEvent e)
FocusListener
Reports focus events on components that can receive the keyboard focus.
focusGained(FocusEvent e)
focusLost(FocusEvent e)
KeyListener
Reports key press events; properties of the KeyEvent object will contain information on which key was pressed.
keyTyped(KeyEvent e)
keyPressed(KeyEvent e)
keyReleased(KeyEvent e)
ListDataListener
Reports changes to the data in a list, like additions (intervalAdded) or deletions (intervalRemoved).
intervalAdded(ListDataEvent e)
intervalRemoved(ListDataEvent e)
MouseListener
Reports mouse clicks, button presses, and releases and reports when the mouse pointer enters and exits the bounds of a component.
mousePressed(MouseEvent e)
mouseReleased(MouseEvent e)
mouseEntered(MouseEvent e)
mouseExited(MouseEvent e)
mouseClicked(MouseEvent e)
MouseMotionListener
Reports mouse movement and dragging.
mouseMoved(MouseEvent e )
mouseDragged(MouseEvent e)
WindowListener
Reports changes in a window's state and open/close events. Call System.exit() from windowClosing() to make your app end when the window is closed. Use the getWindow() property of WindowEvent to get an instance of the window that fired the event.
windowOpened(WindowEvent e)
windowClosing(WindowEvent e)
windowIconified(WindowEvent e)
windowDeiconified(WindowEvent e)
Commonly used event listener types and their uses

Creating listeners
Now, as promised, I’ll return to step one. Here’s where our story gets a little complicated. As with most things Java, there are several ways of creating an event listener. Just remember, lots of choices mean lots of flexibility. You basically have three options for creating event listeners.

Implement the interface
Event listeners are essentially just interfaces, so the simplest way to create one would be to just implement the interface and supply code for all its methods. EventDemoApp uses implementation for ClickMeButton’s and TextEventField’s action event listeners, as you can see inActionEventListener’s declaration:
 
private class ActionEventListener implements ActionListener

Since any class can implement an interface, your main class could even implement all the event listeners needed by your application.

The downside to implementation is that Java’s compiler requires you to implement all methods of an interface (and rightly so), which means that you could wind up being forced to implement methods for events that your application doesn’t care about. For example, the WindowListener event listener has seven event methods. Even if you were concerned only with the windowClose() event, you’d still have to “stub in” the other six event methods to keep the compiler happy.

Using an adapter class
Implementation works best if there are only a few event methods in an interface or if you really need to have access to all event methods in an interface. For those times that you don’t want to bother with implementing the whole interface, Swing provides a set of “adapter classes,” which are basically abstract classes that have all event methods of an interface already stubbed in. Creating your event handler classes by extending an adapter leaves you free to pick and choose which events you want to handle.

EventDemoApp’s mouse event listener, MouseEventListener, extends the MouseAdapter class so that it can ignore the mouse events that it is not concerned about (button presses and releases):
 
private class MouseEventListener extends MouseAdapter

Table B  lists some common event listeners and their corresponding adapter classes:
TableB
Listener name Adapter class name
ActionListener N/A—Only one method to implement
FocusListener FoucsAdapter
KeyListener KeyAdapter
ListDataListener N/A
MouseListener MouseAdapter
MouseMotionListener MouseMotionAdapter
WindowListener WindowAdapter
Event listeners and their adapter classes

Using an anonymous inner class
If you are simply handling a single event on a single component, you can also create an event listener using an anonymous inner class by writing your event handler code “in-line” when you declare the event listener instance. EventDemoApp handles window events using an anonymous inner WindowListener class that, among other things, exits the application when the window is closed:
 
this.addWindowListener(new WindowListener(){
 
public void windowActivated(WindowEvent e){
EventList.addElement("Window Activated");
}
//more event methods
});

Anonymous inner class event listeners work best in situations where you have a single-use event that you can’t easily (or don’t want to) share. Since there is only one window in EventDemoApp, I implemented its window listener in this fashion.

Pop quiz
Now, here’s a quick test. In the code above, I’m using the WindowListener class instead of WindowAdapter class as the window event listener. How many of the methods provided by the interface does the class I create have to implement?

If you answered “all of them” to the above question, you’re on your way to getting the hang of events and are ready to start Swinging. Until next time, happy coding.

 

Editor's Picks