Events and listeners

How do you create a custom event?

Q
How do you create a custom event, and how do you fire it so that a component can get an event?

A
Before looking at a custom event, lets look at a pre-existing event: the ActionEvent.

Components such as the Button and JButton fire off ActionEvents to indicate some kind of component-defined action. For example, the Button fires off an ActionEvent whenever the user presses it. The entire point of an event is to inform a listener that something has happened to a component in the GUI. An event includes all of the information that a listener needs to figure out what happened and to whom it happened (the what and who of the event). An event must give enough information to fully describe itself. That way, a listener can figure out what exactly happened and respond in a meaningful way.

The ActionEvent includes methods for learning the action's command string, modifiers, and identification string. The getActionCommand() method returns the command string that indicates the event's intended action, such as print or copy (the what). The getSource() method returns the object that generates the event (the who).

In order to receive an ActionEvent, a listener must implement the ActionListener interface and register itself with the component. Furthermore, a component must keep track of its listeners in order to notify them of an event.

By using the ActionEvent example as a model, we can easily see the pieces necessary for a component to generate an event and a listener to listen for an event. At a high level, there are three pieces:

  1. The component
  2. The event class
  3. The listener interface

Let's take a look at each one separately.

The component

Components generate events. An event is a component's way of letting a listener know that something has happened. Therefore, a component must provide a mechanism to register and deregister event listeners. The component must also track its listeners and pass on the events to those listeners.

The mechanics of registration/deregistration and tracking are left to the individual component. However, a component will normally have an addXXXListener and removeXXXListener for each type of event that it generates. Internally, the component may store a listener however it chooses; usually, however, components store listeners in a java.util.Vector or javax.swing.event.EventListenerList. To fire off an event to its listeners, the component simply loops through its list of listeners and passes the event to each listener by calling the listener's event dispatch method.

It's time for an example:

 ...
EventListenerList xxxListeners = new EventListnerList();
public void addXXXListener(XXXListener listener) 
{
     xxxListeners.add(XXXListener.class, listener);
}
public void removeXXXListener(XXXListener listener) 
{
     xxxListeners.remove(XXXListener.class, listener);
}
protected void fireXXX(XXXEvent xxxEvent) 
{
     Object[] listeners = xxxListeners.getListenerList();
     // loop through each listener and pass on the event if needed
     Int numListeners = listeners.length;
     for (int i = 0; i<numListeners; i+=2) 
     {
          if (listeners[i]==XXXListener.class) 
          {
               // pass the event to the listeners event dispatch method
                ((XXXListener)listeners[i+1]).dispatchXXX(xxxEvent);
          }            
     }
}

This example shows how to register, deregister, and fire events of type XXXEvent. Listeners can register and deregister themselves through the addXXXListener() and removeXXXListener() methods. When an event occurs, the component creates an event object and passes it to the fireXXX() method, where it is passed to the listeners.

The example defines a generic recipe that all components can follow. However, in order for the example to work, you must define an XXXEvent and an XXXListener interface.

The event class

The event holds all of the information necessary for a listener to figure out what happened. The information included is really event specific. Just think about the event carefully and design the event class to hold whatever information is necessary to fully describe the event to a listener. Events normally extend the java.awt.AWTEvent event class.

The listener interface

An event listener interface defines the methods used by a component to dispatch events. Each event type will have at least one corresponding dispatch method in a listener interface.

A listener interface takes the following generic format:

 public interface XXXListener      
     extends EventListener
{
     // event dispatch methods
     somethingHappened(XXXEvent e);
     somethingElseHappened(XXXEvent e);
     ...
}

To listen for an event, a listener must implement the XXXListener interface and register itself with the component. When an event occurs, the component will call the proper dispatch method. The methods are defined in an interface so that any object can receive the event. As long as the listener implements the interface, the component will know how to dispatch the event to the listener.

Wrap-up

As you can see, there are dependencies between some of the pieces. The listener interface corresponds directly to the event. The event is necessarily the dispatch method's argument.

The component corresponds directly with the event and listener. It needs to know about each so that it can create events, dispatch events, and register listeners.

Unlike the other two pieces, the event object is independent. As a result, many components are free to fire off the event type. Furthermore, multiple interfaces may define methods to dispatch the event.

Tony Sintes is a principal consultant at BroadVision. Tony, a Sun-certified Java 1.1 programmer and Java 2 developer, has worked with Java since 1997.
Recommended
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more