The JavaTM Tutorial

| Start of Tutorial > Start of Trail > Start of Lesson | Search Feedback Form

Trail: Creating a GUI with JFC/Swing
Lesson: Writing Event Listeners

## General Information about Writing Event Listeners

> This section discusses several design considerations to keep in mind when implementing event handlers in your application. We then introduce you to event objects — small objects that describe each event. In particular, we talk about `EventObject`, the superclass for all AWT and Swing events. Next, we introduce the concepts of low-level events and semantic events, recommending that you prefer semantic events when possible. The remainder of this section discusses implementation techniques you might use in some event listeners or see in event listeners created by other people or by GUI builders.
> - Design Considerations
> - Getting Event Information: Event Objects
> - Concepts: Low-Level Events and Semantic Events
> - Event Adapters
> - Inner Classes and Anonymous Inner Classes
> - The EventHandler Class

### Design Considerations

> The most important rule to keep in mind about event listeners that they should execute very quickly. Because all drawing and event-listening methods are executed in the same thread, a slow event-listener method can make the program seem unresponsive and slow to repaint itself. If you need to perform some lengthy operation as the result of an event, do it by starting up another thread (or somehow sending a request to another thread) to perform the operation. For help on using threads, see How to Use Threads.

> You have many choices on how to implement an event listener. We can't recommend a specific approach because one solution won't suit all situations. However, we can give you some hints and show you some techniques that you might see, even if you don't use the same solution in your program.

> For example, you might choose to implement separate classes for different kinds of event listeners. This can be an easy architecture to maintain, but many classes can also mean reduced performance.

> When designing your program, you might want to implement your event listeners in a class that is not public, but somewhere more hidden. A private implementation is a more secure implementation.

> If you have a very specific kind of simple event listener, you might be able to avoid creating a class at all by using the `EventHandler` class.

### Getting Event Information: Event Objects

> Every event-listener method has a single argument — an object that inherits from the `EventObject` class. Although the argument always descends from `EventObject`, its type is generally specified more precisely. For example, the argument for methods that handle mouse events is an instance of `MouseEvent`, where `MouseEvent` is an indirect subclass of `EventObject`.

> The `EventObject` class defines one very useful method:

> `Object getSource()`
>   Returns the object that fired the event.

> Note that the `getSource` method returns an `Object`. Event classes sometimes define methods similar to `getSource`, but that have more restricted return types. For example, the `ComponentEvent` class defines a `getComponent` method that — just like `getSource` — returns the object that fired the event. The difference is that `getComponent` always returns a `Component`. Each how-to page for event listeners mentions whether you should use `getSource` or another method to get the event source.

> Often, an event class defines methods that return information about the event. For example, you can query a `MouseEvent` object for information about where the event occurred, how many clicks the user made, which modifier keys were pressed, and so on.

### Concepts: Low-Level Events and Semantic Events

> Events can be divided into two groups: low-level events and semantic events. Low-level events represent window-system occurrences or low-level input. Everything else is a semantic event.

> Examples of low-level events include mouse and key events — both of which result directly from user input. Examples of semantic events include action and item events. A semantic event might be triggered by user input; for example, a button customarily fires an action event when the user clicks it, and a text field fires an action event when the user presses Enter. However, some semantic events aren't triggered by low-level events, at all. For example, a table-model event might be fired when a table model receives new data from a database.

> Whenever possible, you should listen for semantic events rather than low-level events. That way, you can make your code as robust and portable as possible. For example, listening for action events on buttons, rather than mouse events, means that the button will react appropriately when the user tries to activate the button using a keyboard alternative or a look-and-feel-specific gesture. When dealing with a compound component such as a combo box, it's imperative that you stick to semantic events, since you have no reliable way of registering listeners on all the look-and-feel-specific components that might be used to form the compound component.

### Event Adapters

> Some listener interfaces 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 that implements a listener interface directly.
> >     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 listener implementation goes here...
> >         }
> >     }

> The resulting collection of empty method bodies can make code harder to read and maintain. To help you avoid implementing empty method bodies, the API generally includes an adapter class for each listener interface with more than one method. (The Listener API Table 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 and override only the methods of interest, rather than directly implementing all methods of the listener interface. Here is an example of modifying the preceding code to extend `MouseAdapter`. By extending `MouseAdapter`, it 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 listener implementation goes here...
> >         }
> >     }

### Inner Classes and Anonymous Inner Classes

> What if you want to use an adapter class, but don't want your public 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. A solution is to define an inner class — a class inside of your `Applet` subclass — that extends the `MouseAdapter` class.

> Inner classes can also be useful for event listeners that implement one or more interfaces directly.

> >     //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 listener implementation goes here...
> >             }
> >         }
> >     }

> > Performance note: When considering whether to use an inner class, keep in mind that application startup time and memory footprint are typically directly proportional to the number of classes you load. The more classes you create, the longer your program takes to start up and the more memory it will take. As an application developer you have to balance this with other design constraints you may have. We are not suggesting you turn your application into a single monolithic class in hopes of cutting down startup time and memory footprint — this would lead to unnecessary headaches and maintenance burdens.

> You can create an inner class without specifying a name — this is known as an anonymous inner class. While it might look strange at first glance, anonymous inner classes can make your code easier to read because the class is defined where it is referenced. However, you need to weigh the convenience against possible performance implications of increasing the number of classes.

> Here's an example of using an anonymous 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 listener implementation goes here...
> >                 }
> >     	});
> >         ...
> >         }
> >     }

> > Note: One drawback of anonymous inner classes is that they can't be seen by the long-term persistence mechanism. For more information see the API documentation for the JavaBeansTM package and the Bean Persistence lesson in the JavaBeans trail.

> Inner classes work even if your event listener 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.

> To refer to the enclosing instance, you can use `EnclosingClass.this`. For more information about inner classes, see Implementing Nested Classes.

### The EventHandler Class

> Release 1.4 introduced an `EventHandler` class that supports dynamically generating simple, one-statement event listeners. Although `EventHandler` is only useful for a certain class of extremely simple event listeners, it's worth mentioning for two reasons. It is useful for:
> - Making an event listener that persistence can see and yet doesn't clog up your own classes with event listener interfaces and methods.
> - Not adding to the number of classes defined in an application — this can help performance.

> Creating an `EventHandler` by hand is difficult. An `EventHandler` must be carefully constructed. If you make an mistake, you won't be notified at compile time — it will throw an obscure exception at runtime. For this reason, `EventHandler`s are best created by a GUI builder. `EventHandler`s should be carefully documented. Otherwise you run the risk of producing hard-to-read code.

> An `EventHandler` can only be used in a situation where you need to set a property on an object (any `Object` will work) that has a `set` method for the property, as specified by the JavaBeans component architecture. The value that you set the property to has to be reachable using a `get/is` method, from the event that the `EventHandler` handles. For example, all events have the event source (available via `getSource`). If the event source is the only object you can get to from the event, then whatever value you're getting has to be reachable from the event source. Also, you can't do any "if"s or any other kind of checking in the event listener. It directly assigns a value to a property and nothing more.

> The ColorChooserDemo example in How to Use Color Choosers can be modified to use an event listener to dynamically create a `ChangeListener`. The change listener class defines only one method — `stateChanged`. If it had more than one method and handled those methods differently, the method name would need to be specified as one of the parameters. For example, a `MouseListener` would probably want to treat mouse click and mouse down events differently.

> Here is how `ColorChooserDemo` would look if it utilized an `EventHandler`:

> >     import java.beans.EventHandler;
> >     ...

> >     //Note that the class no longer implements the
> >     //ChangeListener interface.
> >     public class ColorChooserDemo extends JPanel {
> >         ...

> >         //The following replaces the line:
> >         //tcc.getSelectionModel().addChangeListener(this);
> >         tcc.getSelectionModel().addChangeListener(
> >             (ChangeListener)EventHandler.create(
> >                 ChangeListener.class,
> >                 //banner.
> >                 banner,
> >                 //       setForeground(
> >                 "foreground",
> >                 //                     e.getSource().getSelectedColor());
> >                 "source.selectedColor"));
> >         )

> >         ...

> >         //This method is no longer necessary.
> >         //public void stateChanged(ChangeEvent e) {
> >         //    Color newColor = tcc.getColor();
> >         //    banner.setForeground(newColor);
> >         //}

> >         ...
> >     }

| Start of Tutorial > Start of Trail > Start of Lesson | Search Feedback Form

Copyright 1995-2004 Sun Microsystems, Inc. All rights reserved.