Previous | Next | Trail Map | Creating a GUI with JFC/Swing | Writing Event Listeners

General Rules for Writing Event Listeners

[PENDING: update for Swing]

When the AWT calls an event-listener method, that method executes in the AWT event thread. Because all other event-handling and drawing methods are executed in that same thread, a slow event-listener method can make the program seem unresponsive and slow to repaint itself.


Important: Make sure your event-listener methods execute quickly!
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 the lesson Doing Two or More Tasks at Once: Threads (page 327). [PENDING: link to threads and swing information instead]

The next subsection introduces you to the ancestor of all AWT event classes, the AWTEvent class. After that, Using Adapters and Inner Classes to Handle AWT Events gives you hints for avoiding clutter in your code.

The AWTEvent Class

Each method in each AWT event listener interface has a single argument: an instance of a class that descends from the java.awt.AWTEvent class. The AWTEvent class doesn't define any methods or API that you ordinarily need to use. However, it does inherit a useful method from the java.util.EventObject class:
Object getSource()
Returns the object that generated the event.
Note that the getSource method returns an Object. Whenever possible, AWTEvent subclasses define similar methods with more restricted return types. For example, the ComponentEvent class defines a getComponent method that returns the Component that generated the event.

Figure 68 shows the class hierarchy for the AWT event classes. As it shows, many but not all of the AWT event classes inherit from the ComponentEvent class.

The class hierarchy of the AWT event classes.

Standard AWT Events

The AWT defines nearly a dozen kinds of event listeners. You can tell what kinds of events a component can generate by looking at the kinds of event listeners you can register on it. For example, the Component class defines these listener registration methods: Thus, every class that inherits from Component supports component, focus, key, mouse, and mouse-motion listeners. However, most Component instances don't generate these events; a component generates only those events for which listeners have registered on it. For example, if a mouse listener is registered on a particular component, but the component has no other listeners, then the component will generate only mouse events--no component, focus, key, or mouse-motion events.

Using Adapters and Inner Classes to Handle Events

This section tells you how to use adapters and inner classes to reduce clutter in your code. If you don't care about this subject, feel free to skip to the next section.

Most listener interfaces, unlike ActionListener, contain more than one method. For example, the MouseListener interface contains five methods: mousePressed, mouseReleased, mouseEntered, mouseExited, and mouseClicked. Even if you care only about mouse clicks, if your class directly implements MouseListener, then you must implement all five MouseListener methods. Methods for those events you don't care about can have empty bodies. Here's an example:

//An example with cluttered but valid code.
public class MyClass implements MouseListener {
    ...
	someObject.addMouseListener(this);
    ...
    /* Empty method definition. */
    public void mousePressed(MouseEvent e) {
    }

    /* Empty method definition. */
    public void mouseReleased(MouseEvent e) {
    }

    /* Empty method definition. */
    public void mouseEntered(MouseEvent e) {
    }

    /* Empty method definition. */
    public void mouseExited(MouseEvent e) {
    }

    public void mouseClicked(MouseEvent e) {
	...//Event handler implementation goes here...
    }
}
Unfortunately, the resulting collection of empty method bodies can make code harder to read and maintain. To help you avoid cluttering your code with empty method bodies, the AWT and Swing provide an adapter class for each listener interface with more than one method. [PENDING: this is not entirely true for Swing.] (Handling Common Events lists all the listeners and their adapters.) For example, the MouseAdapter class implements the MouseListener interface. An adapter class implements empty versions of all its interface's methods.

To use an adapter, you create a subclass of it, instead of directly implementing a listener interface. For example, by extending MouseAdapter, your class inherits empty definitions of all five of the methods that MouseListener contains.

/*
 * An example of extending an adapter class instead of
 * directly implementing a listener interface.
 */
public class MyClass extends MouseAdapter {
    ... 
	someObject.addMouseListener(this);
    ... 
    public void mouseClicked(MouseEvent e) {
	...//Event handler implementation goes here...
    }
}

What if you don't want your event-handling class to inherit from an adapter class? For example, suppose you write an applet, and you want your Applet subclass to contain some code to handle mouse events. Since the Java language doesn't permit multiple inheritance, your class can't extend both the Applet and MouseAdapter classes. The solution is to define an inner class -- a class inside of your Applet subclass -- that extends the MouseAdapter class,

//An example of using an inner class.
public class MyClass extends Applet {
    ...
	someObject.addMouseListener(new MyAdapter());
    ...
    class MyAdapter extends MouseAdapter {
        public void mouseClicked(MouseEvent e) {
	    ...//Event handler implementation goes here...
        }
    }
}
Here's another example of using an inner class:
//An example of using an anonymous inner class.
public class MyClass extends Applet {
    ...
	someObject.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
	        ...//Event handler implementation goes here...
            }
	});
    ...
    }
}
Inner classes work well even if your event handler needs access to private instance variables from the enclosing class. As long as you don't declare an inner class to be static, an inner class can refer to instance variables and methods just as if its code is in the containing class. To make a local variable available to an inner class, just save a copy of the variable as a final local variable.

Note: Some 1.1 compilers don't let an inner class use private instance variables of the enclosing class. A workaround is to remove the private specifier from the instance variable's declaration.

For more information about inner classes, see Implementing Nested Classes(in the Learning the Java Language trail).


Previous | Next | Trail Map | Creating a GUI with JFC/Swing | Writing Event Listeners