In the last section of this chapter, we show you how to implement a class that generates its own events and notifies interested listeners. This is occasionally necessary when you use advanced Swing components. It is also interesting to see what goes on behind the scenes when you add a listener to a component. Our event source will be a PaintCountPanel that counts how often the paintComponent method was called. Every time the count is incremented, the PaintCountPanel notifies all listeners. In our sample program, we will attach just one listener that updates the frame title see Figure 8-11. Figure 8-11. Counting how often the panel is paintedWhenever you define an event source, you need three ingredients:
How do we make sure that events are sent to interested parties? This is the responsibility of the event source. It must construct an event object and pass it to the registered listeners whenever an event occurs. 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 addPropertyChangeListener(PropertyChangeListener listener) { listenerList.add(PropertyChangeListener.class, listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { listenerList.remove(PropertyChangeListener.class, listener); } NOTE
Whenever the paintComponent method is called, the PaintCountPanel class constructs a PropertyChangeEvent object, specifying the event source, the property name, and the old and new property values. It then calls the firePropertyChangeEvent helper method. public void paintComponent(Graphics g) { int oldPaintCount = paintCount; paintCount++; firePropertyChangeEvent(new PropertyChangeEvent(this, "paintCount", oldPaintCount, paintCount)); super.paintComponent(g); } The firePropertyChangeEvent method locates all registered listeners and calls their propertyChange methods. public void firePropertyChangeEvent(PropertyChangeEvent event) { EventListener[] listeners = listenerList.getListeners(PropertyChangeListener.class); for (EventListener l : listeners) ((PropertyChangeListener) l).propertyChange(event); } Example 8-7 shows the source code of a sample program that listens to a PaintCountPanel. The frame constructor adds a property change listener to the panel that updates the frame title: panel.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent event) { setTitle("EventSourceTest - " + event.getNewValue()); } }); 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. EventSourceTest.java1. import java.awt.*; 2. import java.awt.event.*; 3. import java.util.*; 4. import javax.swing.*; 5. import java.beans.*; 6. 7. public class EventSourceTest 8. { 9. public static void main(String[] args) 10. { 11. EventSourceFrame frame = new EventSourceFrame(); 12. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 13. frame.setVisible(true); 14. } 15. } 16. 17. /** 18. A frame that contains a panel with drawings 19. */ 20. class EventSourceFrame extends JFrame 21. { 22. public EventSourceFrame() 23. { 24. setTitle("EventSourceTest"); 25. setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 26. 27. // add panel to frame 28. 29. final PaintCountPanel panel = new PaintCountPanel(); 30. add(panel); 31. 32. panel.addPropertyChangeListener(new 33. PropertyChangeListener() 34. { 35. public void propertyChange(PropertyChangeEvent event) 36. { 37. setTitle("EventSourceTest - " + event.getNewValue()); 38. } 39. }); 40. } 41. 42. public static final int DEFAULT_WIDTH = 400; 43. public static final int DEFAULT_HEIGHT = 200; 44. } 45. 46. /** 47. A panel that counts how often it is painted. 48. */ 49. class PaintCountPanel extends JPanel 50. { 51. public void paintComponent(Graphics g) 52. { 53. int oldPaintCount = paintCount; 54. paintCount++; 55. firePropertyChangeEvent(new PropertyChangeEvent(this, 56. "paintCount", oldPaintCount, paintCount)); 57. super.paintComponent(g); 58. } 59. 60. /** 61. Adds a change listener 62. @param listener the listener to add 63. */ 64. public void addPropertyChangeListener(PropertyChangeListener listener) 65. { 66. listenerList.add(PropertyChangeListener.class, listener); 67. } 68. 69. /** 70. Removes a change listener 71. @param listener the listener to remove 72. */ 73. public void removePropertyChangeListener(PropertyChangeListener listener) 74. { 75. listenerList.remove(PropertyChangeListener.class, listener); 76. } 77. 78. public void firePropertyChangeEvent(PropertyChangeEvent event) 79. { 80. EventListener[] listeners = listenerList.getListeners(PropertyChangeListener.class); 81. for (EventListener l : listeners) 82. ((PropertyChangeListener) l).propertyChange(event); 83. } 84. 85. public int getPaintCount() 86. { 87. return paintCount; 88. } 89. 90. private int paintCount; 91. } javax.swing.event.EventListenerList 1.2
java.beans.PropertyChangeEvent 1.1
java.beans.PropertyChangeListener 1.1
|