Moving to JDK 1.1: Using the delegation event model to create custom AWT components

Learn how a move to JDK 1.1 affects the way you create custom components

1 2 Page 2
Page 2 of 2

In practice, applets must use the dispatchEvent() method to dispatch an event because the security manager restricts access to the system event queue. For an application, which has the choice, the difference between using dispatchEvent() and the system event queue is fairly subtle: An event inserted in the queue will be delivered later by the AWT event thread; otherwise, the event will be delivered immediately by the caller. Among other things, using the queue protects the caller against RuntimeExceptions that may be thrown by a recipient.

In both cases, however, the dispatchEvent() method is called, which in turn calls processEvent(). We must override processEvent() to appropriately handle the new event type. We'll return to this method in a moment.

Under the new event delegation model, we must maintain a list of registered listeners to which events will be delivered:

/*
 * This code maintains a list of current ColorListeners.
 */
protected ColorListener colorListener;

We could use a Vector for this purpose, but instead we use the ColorEventMulticaster class to maintain a list in the colorListener variable.

Next we have the addColorListener() method, which adds the ColorListener l to the list colorListener using the static method add() of class ColorEventMulticaster. This listener will be notified when the user selects a color with this color picker component:

public synchronized void addColorListener (ColorListener l) {
  colorListener = ColorEventMulticaster.add (colorListener, l);
}

Likewise, the removeColorListener() method removes the ColorListener l from the list colorListener using the static method remove() of class ColorEventMulticaster:

public synchronized void removeColorListener (ColorListener l) {
  colorListener = ColorEventMulticaster.remove (colorListener, l);
}

The processEvent() method, shown next, is called by dispatchEvent() to distribute AWT events generated by this component to any registered listeners. As I indicated earlier, we override the method to correctly process color events: If the event is of class ColorEvent, we call processColorEvent(); otherwise, we call the superclass processEvent() method to appropriately handle normal AWT events:

/*
 * This code processes the dispatch of ColorEvents to the ColorListeners.
 */
protected void processEvent (AWTEvent e) {
  if (e instanceof ColorEvent) {
    processColorEvent ((ColorEvent) e);
  } else {
    super.processEvent (e);
  }
}

Next, we distribute a ColorEvent to the list of registered listeners in colorListener through the colorPicked() method of interface ColorListener. The ColorEventMulticaster class provides this simple interface to performing an event multicast; we call colorPicked() once and it is automatically distributed to every listener in the list:

protected synchronized void processColorEvent (ColorEvent e) {
  if (colorListener != null)
    colorListener.colorPicked (e);
}

Alternatively, we can implement a Vector of listeners that loop through the Vector, calling colorPicked() on each element to perform a multicast:

/*
 * Using a Vector to store the listeners
 */
protected Vector colorListeners = new Vector ();
 public void addColorListener (ColorListener l) {
   colorListeners.addElement (l);
 }
 public void removeColorListener (ColorListener l) {
   colorListeners.removeElement (l);
 }
 protected void processColorEvent (ColorEvent e) {
   synchronized (colorListeners) {
     for (int i = 0; i < colorListeners.size (); ++ i) {
       ((ColorListener) colorListeners.elementAt (i)).colorPicked (e);
     }
   }
 }

We use the addElement() and removeElement() methods of class Vector to maintain the list, and simply sequence through the list to multicast color events.

Class ColorEventMulticaster

The ColorEventMulticaster class maintains a list of ColorListeners. This class itself implements the ColorListener interface and maintains references to two other ColorListeners, as illustrated on the left side of the figure below. When the colorPicked() method is called, the event is passed onto both attached ColorListeners. By stringing together a sequence of ColorEventMulticasters, as indicated on the right side of the figure below, we can maintain a list of listeners.

An event multicaster
A chain of multicasters
An event multicasterA chain of multicasters

We implement the ColorListener interface and maintain references to two other ColorListeners a and b:

class ColorEventMulticaster implements ColorListener {
  protected ColorListener a, b;

The constructor accepts references to two ColorListeners a and b:

protected ColorEventMulticaster (ColorListener a, ColorListener b) {
  this.a = a;
  this.b = b;
}

The colorPicked() method then simply passes the supplied event onto both attached ColorListeners:

public void colorPicked (ColorEvent e) {
    a.colorPicked (e);
    b.colorPicked (e);
  }

To add a listener, we use the following static method. This method returns a ColorListener, which constitutes a list of the two supplied ColorListeners a and b. As indicated in the figure Adding a listener, if either a or b is null, we can simply return the other; otherwise, we return a ColorEventMulticaster that multicasts any call to colorPicked() onto both a and b:

static ColorListener add (ColorListener a, ColorListener b) {
    if (a == null)
      return b;
    else if (b == null)
      return a;
    else
      return new ColorEventMulticaster (a, b);
  }
Adding a listener
Adding a listener

To remove a listener, we use the following static method. This method returns a ColorListener, which constitutes the list of ColorListeners a with the ColorListener b removed. As indicated in the figure Removing a listener, if a is either null or b, we return null; however, if a is a ColorEventMulticaster list then we remove b from both of its subtrees and combine the result. Otherwise, we return a because it is a ColorListener that is neither a list nor b:

static ColorListener remove (ColorListener a, ColorListener b) {
    if ((a == null) || (a == b))
      return null;
    else if (a instanceof ColorEventMulticaster)
      return add (remove (((ColorEventMulticaster) a).a, b),
                  remove (((ColorEventMulticaster) a).b, b));
    else
      return a;
  }
Removing a listener
Removing a listener

Using the ColorPicker

We can use the ColorPicker as we would use any typical JDK 1.1 AWT component: We add it to a container and then register any interested listeners through the addColorListener() method. When the user selects a color, the listeners will be notified through their colorPicked() methods.

The following example demonstrates the picker's usage, opening a Frame with a color picker, and registering a listener that prints out any selections:

import java.awt.*;
public class PickerTester implements ColorListener {
  public void colorPicked (ColorEvent e) {
    System.out.println ("Picked: " + e.getColor ());
  }
  public static void main (String args[]) {
    Frame frame = new Frame ("Test");
    ColorPicker picker = new ColorPicker ();
    PickerTester tester = new PickerTester ();
    picker.addColorListener (tester);
    frame.add ("Center", picker);
    frame.pack ();
    frame.setVisible (true);
  }
}

We simply create a Frame, a ColorPicker, and a PickerTester. We register the PickerTester as a listener for the ColorPicker, which we add to the Frame, which we then pack and show.

Conclusion

The new event delegation model of the AWT is important because it enables us to develop more complex graphical applications more easily than under JDK 1.0.2. We no longer have to repeatedly subclass containers and components in order to process their events, and interaction between AWT and non-AWT systems is now possible.

I hope this example provides you with a solid background that you can draw on when you sit down to develop your own custom components. A few comments: A component that extends Panel and adds other components can simply register itself as a listener for their events and proceed in a similar manner. Before you start building a custom event class, find out if an existing AWT event class will suit your needs. You will cut down significantly on the coding you need to do (you won't need your own event or listener classes), because you can use the existing Component event dispatch mechanism.

You eagle-eyed folks may have noticed that we did not use adapters in this example. An adapter, such as java.awt.event.MouseAdapter, is a class that implements a listener interface with empty method bodies. A subclass can then implement only those methods in which it is interested and not bother with those methods that are not relevant and are supplied by the superclass. Because our ColorListener interface provides only one method, an adapter would not provide any added convenience.

Merlin Hughes is VP of Technology at Prominence Dot Com, lead author of Java Network Programming by Manning Publications Company, and co-author of JavaWorld's monthly Java Step By Step column. Merlin has studied Feng Shui for the past 20 years and has failed to attain any sense of balance, harmony, or order.

Learn more about this topic

  • Previous Step By Step articles

1 2 Page 2
Page 2 of 2