The Event Queue

   

Core Java™ 2: Volume I - Fundamentals
By Cay S. Horstmann, Gary Cornell
Table of Contents
Chapter 8.  Event Handling


When the operating environment generates an event in response to a user action such as a mouse click, the part of the AWT that communicates with the operating environment receives a notification and turns it into an AWT event. The AWT then deposits the event into an event queue. The part of the AWT that dispatches events to listeners:

  • Fetches events from the event queue;

  • Locates the listener object for that event;

  • Invokes the appropriate listener procedure for that event.

An event queue is important for performance reasons. Events that occur frequently (such as mouse moves) or that are slow to carry out (such as painting) can be combined in the queue. If the program has not managed to extract mouse move or paint events and a new event is inserted, then the AWT can combine it with the existing event to make a single, new event. For example, we can have the new mouse position update the old one, or a new paint event can contain a request to repaint the combined areas of the old paint events.

Occasionally, it is useful to manipulate the event queue directly. For example, you can remove events from the queue, thereby bypassing how events would normally be delivered. Or, you can add new events into the queue, allowing a richer event handling than is possible in the basic Java event model.

You obtain an object representing the event queue by using the method call

 EventQueue queue    = Toolkit.getDefaultToolkit().getSystemEventQueue(); 

You insert a new event into the event queue with the postEvent method:

 queue.postEvent(new ActionEvent(this,    ActionEvent.ACTION_PERFORMED, "Blue")); 

You remove an event with the getNextEvent method. The peekEvent method returns the next event in the queue, but it does not remove it.

graphics/notes_icon.gif

Inserting or removing events is an advanced technique. If performed improperly or maliciously, it can wreak havoc with an application. For that reason, applets the Java applications that are downloaded from foreign computers and run inside your browser are not allowed access to the system event queue.

java.awt.EventQueue 1.1

graphics/api_icon.gif
  • AWTEvent peekEvent()

    returns a reference to the AWTEvent object that describes the next event.

  • AWTEvent getNextEvent()

    returns a reference to the AWTEvent object that describes the next event and removes it from the queue.

  • void postEvent(AWTEvent anEvent)

    places the event on the event queue.

    Parameters:

    anEvent

    The event you want to post

Adding Custom Events

In the last section of this chapter, we will do some fairly sophisticated programming. We want to show you how to build a custom event type that you can insert into the AWT event queue and then have it dispatched to a listener, just like regular AWT events. For the example in this section, we will implement our own timer. The timer sends an event to its listener whenever a certain time interval has elapsed. For this event, we make a new event type that we call TimerEvent. The associated listener will have one method, called timeElapsed.

Using our timer is simple. Construct a timer object and specify the interval (in milliseconds) in the constructor. Then, add a listener. The listener will be notified whenever the time interval has elapsed. Here is how you can put the timer to work:

 Timer t = new Timer(100);    // deliver timer clicks every 100 milliseconds TimerListener listener = . . .; t.addTimerListener(listener);    // notify the timeElapsed method of this class 

You need to define a class that implements the TimerListener interface:

 class TimerAction implements TimerListener {    public void timeElapsed(TimerEvent event)    {       // this code is executed every 100 milliseconds    } } 

graphics/notes_icon.gif

As you know from Chapter 6, the Swing package has its own Timer class, which is slightly different from ours. The Swing timer does not introduce a new event type but instead sends action events to the listener. More importantly, the Swing timer does not smuggle its events inside the AWT queue but keeps its own separate queue for timer events. We are implementing our own class to show you how to add new event types to the AWT, not to build a better timer. If you need a timer in your own code, you should simply use the Swing timer, not ours.

Now, let us see how to implement a custom event. Whenever you define a custom event, you need three ingredients:

  • an event type. We will define a TimerEvent class.

  • an event listener interface. We will use:

     public interface TimerListener extends EventListener {    void timeElapsed(TimerEvent event); } 
  • an event source (that is, our Timer). Listeners are attached to the event source.

The TimerEvent class is pretty simple:

  • It extends the AWTEvent superclass since all events in the AWT event queue must have type AWTEvent.

  • The constructor for the timer event receives the object that is the source of the event (that is, the timer object).

We also need to give an event ID number to the superclass. It does not matter what positive integer we choose, as long as we stay outside the range that the AWT uses for its own events.

How to find an unused ID? To quote the SDK documentation: "Programs should choose event ID values which are greater than the integer constant java.awt.AWTEvent.RESERVED_ID_MAX."

 class TimerEvent extends AWTEvent {    public TimerEvent(Timer t) { super(t, TIMER_EVENT); }    public static final int TIMER_EVENT =       AWTEvent.RESERVED_ID_MAX  + 5555; } 

Finally, we need to implement the Timer class itself. The AWT event mechanism requires that event sources extend the class Component. Normally, components are user interface elements that are placed inside a window. We will simply take the attitude that a timer is an invisible component and have it extend the JComponent class.

To write the code that constructs the interval that the timer "ticks," we need to use threads. (Threads are discussed in the second volume of this book, so you will need to take the thread handling code on faith for now.) Whenever the specified time interval has elapsed, we make a new timer event and insert it into the event queue. Here's the code for this, with the pieces that are needed to post the event in bold:

 class Timer extends JComponent implements Runnable {    public Timer(int i)    {       interval = i;       Thread t = new Thread(this);       t.start();    }    public void run()    {       while (true)       {          try { Thread.sleep(interval); }          catch(InterruptedException e) {}          EventQueue queue             = Toolkit.getDefaultToolkit().getSystemEventQueue();          TimerEvent event = new TimerEvent(this);           queue.postEvent(event);       }    }    . . .    private int interval; 

After this code is processed, our custom timer events are inserted into the queue. Event delivery is not automatic however, so our custom timer event will not be sent to anyone without additional code.

How do we make sure our custom event is sent to interested parties? The answer is that is the responsibility of the event source to:

  • Manage the listeners for the events that it generates;

  • Dispatch the events to the listeners that are registered for them.

Event management is a common task, and the Swing designers provide a convenience class EventListenerList to make it easy to implement the methods for adding and removing listeners and for firing events. The class takes care of the tricky details that can arise when multiple threads attempt to add, remove, or dispatch events at the same time.

Because some event sources accept listeners of multiple types, each listener in the event listener list is associated with a particular class. The add and remove methods are intended for the implementation of addXxxListener methods. For example,

 public void addTimerListener(TimerListener listener) {    listenerList.add(TimerListener.class, listener); } public void removeTimerListener(TimerListener listener) {    listenerList.remove(TimerListener.class, listener); } 

graphics/notes_icon.gif

You may wonder why the EventListenerList doesn't simply check which interface the listener object implements. But it is possible for an object to implement multiple interfaces. For example, it is possible that listener happens to implement both the TimerListener and the ActionListener interface, but a programmer may choose only to add it as a TimerListener by calling the addTimerListener. The EventListenerList must respect that choice.

Whenever the AWT removes an event from the queue, it calls the processEvent method. For timers, we define that method to call the timeElapsed method on the single listener. We use the getListeners method of the EventListenerList class to obtain all timer listeners. Then we call the timeElapsed method for each of them:

 public void processEvent(AWTEvent event) {    if (event instanceof TimerEvent)    {       EventListener[] listeners = listenerList.getListeners(          TimerListener.class);       for (int i = 0; i < listeners.length; i++)          ((TimerListener)listeners[i]).timeElapsed(             (TimerEvent)event);    }    else super.processEvent(event); } 

graphics/notes_icon.gif

The getListeners method is a J2SE 1.3 feature. Use getListenerList if you use J2SE 1.2.

graphics/caution_icon.gif

Our timer extends the JComponent class. If you instead use the Component class as the superclass, you will run into a problem. The AWT code that removes events from the queue and dispatches them to the event source will deliver them only if it is convinced that the component supports the new event model. One way to convince it is to call the enableEvents method in the Component class. This method takes a parameter that gives a mask for the AWT events that you want to enable for this component. If you don't care about AWT events at all as is the case for a source of custom events you can pass a mask of 0. Therefore, if your event source extends the Component class, you should place a call enableEvents(0) into the constructor.

As you can see, it is possible to add custom events to the AWT mechanism using relatively little code.

Example 8-7 shows the complete source code of a sample program that uses the timer. For fun, we generate a new random circle and shift the screen display down a pixel with every timer tick. The effect is an animation that simulates rainfall. (See Figure 8-11.)

Figure 8-11. Using custom timer events to simulate rainfall

graphics/08fig11.gif

This ends our discussion of event handling. In the next chapter, you will learn more about user interface components. Of course, to program user interfaces, you will put your knowledge of event handling to work by capturing the events that the user interface components generate.

Example 8-7 CustomEventTest.java
   1. import java.awt.*;   2. import java.awt.geom.*;   3. import java.util.*;   4. import java.awt.event.*;   5. import javax.swing.*;   6. import javax.swing.event.*;   7.   8. public class CustomEventTest   9. {  10.    public static void main(String[] args)  11.    {  12.       CustomEventFrame frame = new CustomEventFrame();  13.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  14.       frame.show();  15.    }  16. }  17.  18. /**  19.    A frame with a panel that displays falling raindrops  20. */  21. class CustomEventFrame extends JFrame  22. {  23.    public CustomEventFrame()  24.    {  25.       setTitle("CustomEventTest");  26.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);  27.  28.       // add frame to panel  29.  30.       CustomEventPanel panel = new CustomEventPanel();  31.       Container contentPane = getContentPane();  32.       contentPane.add(panel);  33.    }  34.  35.    public static final int DEFAULT_WIDTH = 300;  36.    public static final int DEFAULT_HEIGHT = 200;  37. }  38.  39. /**  40.    A panel that displays falling rain drops  41. */  42. class CustomEventPanel extends JPanel  43. {  44.    public CustomEventPanel()  45.    {  46.       y = 0;  47.       circles = new ArrayList();  48.  49.       Timer t = new Timer(100);  50.       TimerAction listener = new TimerAction();  51.       t.addTimerListener(listener);  52.    }  53.  54.    public void paintComponent(Graphics g)  55.    {  56.       super.paintComponent(g);  57.       Graphics2D g2 = (Graphics2D)g;  58.  59.       // translate the origin to create illusion of falling drops  60.       g2.translate(0, y);  61.  62.       // draw all circles  63.       for (int i = 0; i < circles.size(); i++)  64.          g2.draw((Ellipse2D)circles.get(i));  65.    }  66.  67.    private ArrayList circles;  68.    private int y;  69.  70.    private class TimerAction implements TimerListener  71.    {  72.       public void timeElapsed(TimerEvent event)  73.       {  74.          if (getWidth() == 0) return; // panel not yet shown  75.  76.          // add another circle  77.          int x = generator.nextInt(getWidth());  78.          Ellipse2D circle = new Ellipse2D.Double(x, -y,  79.             SIZE, SIZE);  80.          circles.add(circle);  81.  82.          // shift up the origin  83.          y++;  84.  85.          repaint();  86.       }  87.  88.       private Random generator = new Random();  89.       private static final int SIZE = 9;  90.    }  91. }  92.  93. /**  94.    A custom event class.  95. */  96. class TimerEvent extends AWTEvent  97. {  98.    public TimerEvent(Timer t) { super(t, TIMER_EVENT); }  99.    public static final int TIMER_EVENT 100.       = AWTEvent.RESERVED_ID_MAX  + 5555; 101. } 102. 103. /** 104.    A custom event listener interface. 105. */ 106. interface TimerListener extends EventListener 107. { 108.    public void timeElapsed(TimerEvent event); 109. } 110. 111. /** 112.    A custom timer class that is the source of timer events. 113. */ 114. class Timer extends JComponent implements Runnable 115. { 116.    public Timer(int i) 117.    { 118.       listenerList = new EventListenerList(); 119.       interval = i; 120.       Thread t = new Thread(this); 121.       t.start(); 122.    } 123. 124.    /** 125.       Adds a timer listener 126.       @param listener the listener to add 127.    */ 128.    public void addTimerListener(TimerListener listener) 129.    { 130.       listenerList.add(TimerListener.class, listener); 131.    } 132. 133.    /** 134.       Removes a timer listener 135.       @param listener the listener to remove 136.    */ 137.    public void removeTimerListener(TimerListener listener) 138.    { 139.       listenerList.remove(TimerListener.class, listener); 140.    } 141. 142. 143.    /** 144.       Posts a new timer event every <code>interval</code> 145.       milliseconds. 146.    */ 147.    public void run() 148.    { 149.       while (true) 150.       { 151.          try { Thread.sleep(interval); } 152.          catch(InterruptedException e) {} 153. 154.          TimerEvent event = new TimerEvent(this); 155. 156.          EventQueue queue 157.             = Toolkit.getDefaultToolkit().getSystemEventQueue(); 158.          queue.postEvent(event); 159.       } 160.    } 161. 162.    public void processEvent(AWTEvent event) 163.    { 164.       if (event instanceof TimerEvent) 165.       { 166.          EventListener[] listeners = listenerList.getListeners( 167.             TimerListener.class); 168.          for (int i = 0; i < listeners.length; i++) 169.             ((TimerListener)listeners[i]).timeElapsed( 170.                (TimerEvent)event); 171.       } 172.       else 173.          super.processEvent(event); 174.    } 175. 176.    private int interval; 177.    private EventListenerList listeners; 178. } 

java.swing.event.EventListenerList 1.2

graphics/api_icon.gif
  • void add(Class t, EventListener l)

    adds an event listener and its class to the list. The class is stored so that event firing methods can selectively call events. Typical usage is in an addXxxListener method:

     public void addXxxListener(XxxListener l) {    listenerList.add(XxxListener.class, l); } 

    Parameters:

    t

    The listener type

     

    l

    The listener

  • void remove(Class t, EventListener l)

    removes an event listener and its class from the list. Typical usage is in a removeXxxListener method:

     public void removeXxxListener(XxxListener l) {    listenerList.remove(XxxListener.class, l); } 

    Parameters:

    t

    The listener type

     

    l

    The listener

  • EventListener[] getListeners(Class t) 1.3

    returns an array of all the listeners of the given type. The array is guaranteed to be non-null.

  • Object[] getListenerList()

    returns an array whose elements with even-numbered index are listener classes, and whose elements with odd-numbered index are listener objects. The array is guaranteed to be non-null.

java.awt.Component 1.0

graphics/api_icon.gif
  • void enableEvents(long maskForEvents) 1.1

    enables the component to insert events into the event queue even when there is no listener for a particular event type.

    Parameters:

    maskForEvents

    A mask of event types to enable, made up of constants, such as ACTION_EVENT_MASK, that are defined in the AWTEvent class.


       
    Top
     



    Core Java 2(c) Volume I - Fundamentals
    Building on Your AIX Investment: Moving Forward with IBM eServer pSeries in an On Demand World (MaxFacts Guidebook series)
    ISBN: 193164408X
    EAN: 2147483647
    Year: 2003
    Pages: 110
    Authors: Jim Hoskins

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net