27.5 Event Utilities


If you extend one of the Swing components to add functionality, or indeed, build your own component from scratch, you need to handle event listeners for any events you might generate. The EventListenerList class (from the javax.swing.event package) is designed to aid in that task. This class is similar in many ways to the AWTEventMulticaster; however, it supports any type of listener and assumes you'll use only the appropriate listeners for a given event type.

The KeyStroke class can also help handle keyboard events. Rather than listening to every key that gets pressed and throwing out the things you don't care about, you can use the KeyStroke class to register specific actions with specific keys. The MouseInputAdapter can help deal with the other common low-level event generator: the mouse. And last but not least, this section also covers the SwingPropertyChangeSupport class to show you a fast way of generating property change events.

27.5.1 The EventListenerList Class

If your component generates events, it must contain methods to add and remove interested listeners. Following the JavaBeans design patterns, these are the addTypeListener( ) and removeTypeListener( ) methods. Typically you store the listeners in a collection, and then use the vector as a rollcall for who to send events to when the time comes. This is a very common task for components that generate events, and the EventListenerList can help lift some (but certainly not all) of the burden of coding the event firing.

The EventListenerList stores listeners as pairs of objects: one object to hold the listener's type and one to hold the listener itself. At any time, you can retrieve all of the current listeners as an array of Objects and use that array to fire off any events you need.

Here is a SecretLabel class that extends JLabel and fires ActionEvent messages when clicked. The label does not give any indication it has been clicked; that's why it's called secret. The code for this label demonstrates how an EventListenerList is typically used. Figure 27-3 shows the SecretLabel up and running.

Figure 27-3. A JLabel that uses an EventListenerList to facilitate dispatching events
figs/swng2.2703.gif

We set up the standard addActionListener( ) and removeActionListener( ) methods, which delegate to the listener list, and pass in the type of listener we're attaching. (Recall that EventListenerList can store any type of listener.) When we actually fire an event, we search the listener list array, checking the even-numbered indices for a particular type of listener. If we find the right type (ActionListener, in this case) we use the next entry in the list as an event recipient. You can find other such examples throughout the Swing package in such models as DefaultTreeModel, DefaultTableModel, and DefaultButtonModel.

The EventListenerList provides a generic dispatcher that works in just about every situation. However, it is not the only way to dispatch events. For a particular application you may find a more efficient means.

// SecretLabel.java // An extension of the JLabel class that listens to mouse clicks and converts // them to ActionEvents, which in turn are reported via an EventListenersList object // import java.awt.*; import java.awt.event.*; import javax.swing.*; public class SecretLabel extends JLabel {   public SecretLabel(String msg) {     super(msg);     addMouseListener(new MouseAdapter( ) {       public void mouseClicked(MouseEvent me) {         fireActionPerformed(new ActionEvent(SecretLabel.this,                                              ActionEvent.ACTION_PERFORMED,                                              "SecretMessage"));       }     });   }   public void addActionListener(ActionListener l) {     // We'll just use the listenerList we inherit from JComponent.     listenerList.add(ActionListener.class, l);   }   public void removeActionListener(ActionListener l) {     listenerList.remove(ActionListener.class, l);   }   protected void fireActionPerformed(ActionEvent ae) {     Object[] listeners = listenerList.getListeners(ActionListener.class);     for (int i = 0; i < listeners.length; i++) {       ((ActionListener)listeners[i]).actionPerformed(ae);     }   } }

Our addActionListener( ) and removeActionListener( ) methods just defer to a listener list to register and unregister listeners. We don't have to do anything special to get an EventListenerList; we're extending JLabel and therefore inherit a listenerList field from JComponent. fireActionPerformed( ) does the actual work; it calls the actionPerformed( ) method of every action listener stored in the listener list. Note that we walk through the array of listeners two at a time; as we'll see, the elements in this array alternate between Class objects that tell us what kind of listener we have, and the actual listener objects themselves. Figure 27-4 shows how the array is set up.

Here's the application that creates the SecretLabel and hooks it up to the reporting label. We use the same addActionListener( ) that we would for things like buttons or lists:

// SecretTest.java // A demonstration framework for the EventListenerList-enabled SecretLabel class // import javax.swing.*; import java.awt.event.*; import java.awt.*; public class SecretTest extends JFrame {   public SecretTest( ) {     super("EventListenerList Demo");     setSize(200, 100);     setDefaultCloseOperation(EXIT_ON_CLOSE);     SecretLabel secret = new SecretLabel("Try Clicking Me");     final JLabel reporter = new JLabel("Event reports will show here...");     secret.addActionListener(new ActionListener( ) {       public void actionPerformed(ActionEvent ae) {         reporter.setText("Got it: " + ae.getActionCommand( ));       }     } );     getContentPane( ).add(secret, BorderLayout.NORTH);     getContentPane( ).add(reporter, BorderLayout.SOUTH);       }   public static void main(String args[]) {     SecretTest st = new SecretTest( );     st.setVisible(true);   } }

If you have set up your own event source components before, the EventListenerList class may not seem to provide much improvement. But, as the documentation points out, it provides a single access point for serializing your list of listeners.

27.5.1.1 Constructor
public EventListenerList( )

Create a new listener list.

27.5.1.2 Listener methods
public void add(Class t, EventListener l)

Add a new listener to the list.

public int getListenerCount( )
public int getListenerCount(Class t)

Return a count of the listeners contained in the list. If a Class argument is provided, you get the number of listeners of that type in the list.

public Object[] getListenerList( )

Return an array of objects organized as in Figure 27-4. The listener type entry is always a Class object. You typically work through this array in increments (or decrements) of 2.

Figure 27-4. The EventListenerList object array structure
figs/swng2.2704.gif
public EventListener[] getListeners(Class t)

Added in the 1.3 release of the SDK, this method returns an array containing only listeners matching the requested class, t. This eliminates the tedium of looping through all the registered listeners when you only want a particular type. Many classes that use the EventListenerList helper class provide a pass through a version of this method so you can access the listeners directly. (See the Timer class earlier in this chapter.)

public remove(Class t, EventListener l)

Remove a listener from the list.

27.5.2 The KeyStroke Class

Another convenient class for dealing with events is the KeyStroke class. This class allows you to associate actions with particular keys through the InputMap class. While not technically part of Swing, it plays an integral role in setting up the maps.

27.5.2.1 Properties

The KeyStroke class contains the properties shown in Table 27-5. The keyChar property is the character this keystroke represents, such as A or $. The keyCode property is the int value associated with a particular key on your keyboard. This might be the ASCII value of a letter, or some other value associated with a function key. The modifiers property contains information on whether or not any of the modifier keys (Control, Alt, Meta, Shift) are attached to this keystroke. The acceptable key codes come from the java.awt.event.KeyEvent class and are shown in Table 27-6. The onKeyRelease property determines when events associated with this keystroke should be triggered. A true value treats the keystroke as a keyTyped( ) event, while a false value behaves like a keyPressed( ) event.

Table 27-5. KeyStroke properties

Property

Data type

get

is

set

Default value

keyChar

char

·

   

'\0'

keyCode

int

·

   

0

modifiers

int

·

   

0

onKeyRelease

boolean

 

·

 

false

Table 27-6. Virtual key codes for KeyStroke

KeyEvent constant

Decimal value

Hexadecimal value

char equivalent

VK_0

48

0x30

'0'

VK_1

49

0x31

'1'

VK_2

50

0x32

'2'

VK_3

51

0x33

'3'

VK_4

52

0x34

'4'

VK_5

53

0x35

'5'

VK_6

54

0x36

'6'

VK_7

55

0x37

'7'

VK_8

56

0x38

'8'

VK_9

57

0x39

'9'

VK_A

65

0x41

'A'

VK_B

66

0x42

'B'

VK_C

67

0x43

'C'

VK_D

68

0x44

'D'

VK_E

69

0x45

'E'

VK_F

70

0x46

'F'

VK_G

71

0x47

'G'

VK_H

72

0x48

'H'

VK_I

73

0x49

'I'

VK_J

74

0x4a

'J'

VK_K

75

0x4b

'K'

VK_L

76

0x4c

'L'

VK_M

77

0x4d

'M'

VK_N

78

0x4e

'N'

VK_O

79

0x4f

'O'

VK_P

80

0x50

'P'

VK_Q

81

0x51

'Q'

VK_R

82

0x52

'R'

VK_S

83

0x53

'S'

VK_T

84

0x54

'T'

VK_U

85

0x55

'U'

VK_V

86

0x56

'V'

VK_W

87

0x57

'W'

VK_X

88

0x58

'X'

VK_Y

89

0x59

'Y'

VK_Z

90

0x5a

'Z'

VK_ACCEPT

30

0x1e

 

VK_ADD

107

0x6b

 

VK_AGAIN

65481

0xffc9

 

VK_ALL_CANDIDATES

256

0x0100

 

VK_ALPHANUMERIC

240

0x00f0

 

VK_ALT

18

0x12

 

VK_ALT_GRAPH

65406

0xff7e

 

VK_AMPERSAND

150

0x96

'&'

VK_ASTERISK

151

0x97

'*'

VK_AT

512

0x0200

'@'

VK_BACK_QUOTE

192

0xc0

 

VK_BACK_SLASH

92

0x5c

'\\'

VK_BACK_SPACE

8

0x08

'\b'

VK_BRACELEFT

161

0xa1

 

VK_BRACERIGHT

162

0x12

 

VK_CANCEL

3

0x03

 

VK_CAPS_LOCK

20

0x14

 

VK_CIRCUMFLEX

514

0x0202

 

VK_CLEAR

12

0x0c

 

VK_CLOSE_BRACKET

93

0x5d

']'

VK_CODE_INPUT

256

0x0100

 

VK_COLON

513

0x0201

':'

VK_COMMA

44

0x2c

','

VK_COMPOSE

65312

0xff20

 

VK_CONTROL

17

0x11

 

VK_CONVERT

28

0x1c

 

VK_COPY

65485

0xffcd

 

VK_CUT

65489

0xffd1

 

VK_DEAD_ABOVEDOT

134

0x86

 

VK_DEAD_ABOVERING

136

0x88

 

VK_DEAD_ACUTE

129

0x81

 

VK_DEAD_BREVE

133

0x85

 

VK_DEAD_CARON

138

0x90

 

VK_DEAD_CEDILLA

139

0x91

 

VK_DEAD_CIRCUMFLEX

130

0x82

 

VK_DEAD_DIAERESIS

135

0x87

 

VK_DEAD_DOUBLEACUTE

137

0x89

 

VK_DEAD_GRAVE

128

0x80

 

VK_DEAD_IOTA

141

0x93

 

VK_DEAD_MACRON

132

0x84

 

VK_DEAD_OGONEK

140

0x92

 

VK_DEAD_SEMIVOICED_SOUND

143

0x95

 

VK_DEAD_TILDE

131

0x83

 

VK_DEAD_VOICED_SOUND

142

0x94

 

VK_DECIMAL

110

0x6e

 

VK_DELETE

127

0x7f

 

VK_DIVIDE

111

0x6f

 

VK_DOLLAR

515

0x0203

'$'

VK_DOWN

40

0x28

 

VK_END

35

0x23

 

VK_ENTER

10

0x0a

'\n'

VK_EQUALS

61

0x3d

'='

VK_ESCAPE

27

0x1b

 

VK_EURO_SIGN

516

0x0204

 

VK_EXCLAMATION_MARK

517

0x0205

 

VK_F1

112

0x70

 

VK_F2

113

0x71

 

VK_F3

114

0x72

 

VK_F4

115

0x73

 

VK_F5

116

0x74

 

VK_F6

117

0x75

 

VK_F7

118

0x76

 

VK_F8

119

0x77

 

VK_F9

120

0x78

 

VK_F10

121

0x79

 

VK_F11

122

0x7a

 

VK_F12

123

0x7b

 

VK_F13

61440

0xf000

 

VK_F14

61441

0xf001

 

VK_F15

61442

0xf002

 

VK_F16

61443

0xf003

 

VK_F17

61444

0xf004

 

VK_F18

61445

0xf005

 

VK_F19

61446

0xf006

 

VK_F20

61447

0xf007

 

VK_F21

61448

0xf008

 

VK_F22

61449

0xf009

 

VK_F23

61450

0xf00a

 

VK_F24

61451

0xf00b

 

VK_FINAL

24

0x18

 

VK_FIND

65488

0xffd0

 

VK_FULL_WIDTH

243

0xf3

 

VK_GREATER

160

0xa0

'>'

VK_HALF_WIDTH

244

0xf4

 

VK_HELP

156

0x9c

 

VK_HIRAGANA

242

0xf2

 

VK_HOME

36

0x24

 

VK_INPUT_METHOD_ON_OFF1.3

263

0x0107

 

VK_INSERT

155

0x9b

 

VK_INVERTED_EXCLAMATION_MARK

518

0x0206

 

VK_JAPANESE_HIRAGANA

260

0x0104

 

VK_JAPANESE_KATAKANA

259

0x0103

 

VK_JAPANESE_ROMAN

261

0x0105

 

VK_KANA

21

0x15

 

VK_KANA_LOCK1.3

262

0x0106

 

VK_KANJI

25

0x19

 

VK_KATAKANA

241

0xf1

 

VK_KP_DOWN

225

0xe1

 

VK_KP_LEFT

226

0xe2

 

VK_KP_RIGHT

227

0xe3

 

VK_KP_UP

224

0xe0

 

VK_LEFT

37

0x25

 

VK_LEFT_PARENTHESIS

519

0x0207

'('

VK_LESS

153

0x99

'<'

VK_META

157

0x9d

 

VK_MINUS

45

0x2d

'-'

VK_MODECHANGE

31

0x1f

 

VK_MULTIPLY

106

0x6a

 

VK_NONCONVERT

29

0x1d

 

VK_NUMBER_SIGN

520

0x0208

'#'

VK_NUM_LOCK

144

0x90

 

VK_NUMPAD0

96

0x60

 

VK_NUMPAD1

97

0x61

 

VK_NUMPAD2

98

0x62

 

VK_NUMPAD3

99

0x63

 

VK_NUMPAD4

100

0x64

 

VK_NUMPAD5

101

0x65

 

VK_NUMPAD6

102

0x66

 

VK_NUMPAD7

103

0x67

 

VK_NUMPAD8

104

0x68

 

VK_NUMPAD9

105

0x69

 

VK_OPEN_BRACKET

91

0x5b

'['

VK_PAGE_DOWN

34

0x22

 

VK_PAGE_UP

33

0x21

 

VK_PASTE

65487

0xffcf

 

VK_PAUSE

19

0x13

 

VK_PERIOD

46

0x2e

'.'

VK_PLUS

521

0x0209

'+'

VK_PREVIOUS_CANDIDATE

257

0x0101

 

VK_PRINTSCREEN

154

0x9a

 

VK_PROPS

65482

0xffca

 

VK_QUOTE

222

0xde

 

VK_QUOTEDBL

152

0x98

'\"'

VK_RIGHT

39

0x27

 

VK_RIGHT_PARENTHESIS

522

0x020a

')'

VK_ROMAN_CHARACTERS

245

0xf5

 

VK_SCROLL_LOCK

145

0x91

 

VK_SEMICOLON

59

0x3b

';'

VK_SEPARATER*

108

0x6c

 

VK_SEPARATOR1.4

108

0x6c

 

VK_SHIFT

16

0x10

 

VK_SLASH

47

0x2f

'/'

VK_SPACE

32

0x20

' '

VK_STOP

65480

0ffc8

 

VK_SUBTRACT

109

0x6d

 

VK_TAB

9

0x09

'\t'

VK_UNDEFINED

0

0x00

 

VK_UNDERSCORE

523

0x020b

 

VK_UNDO

65483

0xffcb

 

VK_UP

38

0x26

 

1.3since 1.3, 1.4since 1.4

*Pseudo-deprecated in favor of VK_SEPARATOR.

27.5.2.2 Key codes

Just for your reference, Table 27-6 lists the key codes defined by the java.awt.event.KeyEvent class. You'll notice that many of the key code values correspond to the ASCII value of the character associated with the key. This facilitates coding for common keys. Of course, as a good programmer, you always use the constant, right? In fact, even though you can use, for example, 'R' rather than VK_R, if you try to use 'r', it won't work. This is a good argument for using the constant; if you make a capitalization error the compiler will tell you right away, and you won't have to wait for bug reports from your users. Note that in the table we left out character equivalents that were not conceptually related to the virtual keycode constant.

27.5.2.3 Factory methods

The KeyStroke class does not have a public (or even protected) constructor. All keystrokes are cached for you. You can use any of the static methods to retrieve the instance you're looking for.

public static KeyStroke getKeyStroke(char keyChar)
public static KeyStroke getKeyStroke(char keyChar, int modifiers)
public static KeyStroke getKeyStroke(char keyChar, boolean onKeyRelease)

Return the KeyStroke that represents a character, such as a. The modifiers argument allows you to look for keystrokes like Ctrl-A. The onKeyRelease argument determines whether actions associated with this keystroke are triggered when the key is pressed (false) or when it is released (true).

public static KeyStroke getKeyStroke(int keyCode, int modifiers)
public static KeyStroke getKeyStroke(int keyCode, int modifiers, boolean onKeyRelease)

Return the KeyStroke that represents a given key code (see Table 27-6) and modifier combination. As with the previous getKeyStroke( ) methods, the onKeyRelease argument determines when to fire associated actions.

public static KeyStroke getKeyStroke(String representation)

Return a KeyStroke from the given representation of the keystroke. Sometimes it's easier to think about keystrokes this way. For example, to retrieve Ctrl-A as a keystroke, you could call this method with "control A" as the argument. To get one of the function keys, representation would be "pressed F12" or "control alt released INSERT".

The syntax for a valid representation is shown in Figure 27-5. Note to Mac users: you'll need to use "alt" for the Option key and "meta" for the Command key when building keystrokes with this method.

Figure 27-5. The valid construction of a keystroke by string representation
figs/swng2.2705.gif
public static KeyStroke getKeyStrokeForEvent(KeyEvent anEvent)

Extract a KeyStroke from anEvent.

27.5.3 The MouseInputAdapter Class

This simple implementation of the MouseInputListener interface (which is itself just a conglomeration of the MouseListener and MouseMotionListener interfaces) provides empty methods for each of the mouse event handlers. You can use this abstract convenience class like any other adapter, extending it and overriding only the methods that interest you. This class simply has the benefit of handling both mouse and mouse motion events.

27.5.3.1 Methods
public void mouseClicked(MouseEvent e)
public void mousePressed(MouseEvent e)
public void mouseReleased(MouseEvent e)
public void mouseEntered(MouseEvent e)
public void mouseExited(MouseEvent e)

These methods come from the MouseListener interface. All have empty implementations.

public void mouseDragged(MouseEvent e)
public void mouseMoved(MouseEvent e)

These methods come from the MouseMotionListener interface. Both have empty implementations.

27.5.4 The SwingPropertyChangeSupport Class

Many Swing components support bound properties as defined by the JavaBeans specification. In the java.beans package, a utility class called PropertyChangeSupport is defined to help you register property change listeners and fire property change events. The PropertyChangeSupport class does this work in a thread-safe manner that consumes a good bit of memory. The SwingPropertyChangeSupport class provides exactly the same set of features but does so without thread-safety to reduce memory usage and increase performance. If you're building your own components, you can use this class instead of PropertyChangeSupport.

27.5.4.1 Constructor
public SwingPropertyChangeSupport(Object sourceBean)

This constructor creates a new SwingPropertyChangeSupport object with a reference to sourceBean kept for use in reporting events. The sourceBean is listed as the source for all property change events coming from this support object.

27.5.4.2 Methods
public void addPropertyChangeListener(PropertyChangeListener listener)
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener)
public void removePropertyChangeListener(PropertyChangeListener listener)
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener)

Add or remove PropertyChangeListener objects interested in receiving property change events. If you give a propertyName, only changes to the specified property are reported.

public void firePropertyChange(String propertyName, Object oldValue, Object newValue)

Create a new PropertyChangeEvent object from propertyName, oldValue and newValue, then fire it to any registered listeners.

public void firePropertyChange(PropertyChangeEvent evt)

Fire an existing PropertyChangeEvent, evt, to any registered listeners.

public boolean hasListeners(String propertyName)

Return true if there are any listeners registered for the given propertyName. If a generic listener is present, this method returns true, regardless of whether or not any specific property listeners exist.



Java Swing
Graphic Java 2: Mastering the Jfc, By Geary, 3Rd Edition, Volume 2: Swing
ISBN: 0130796670
EAN: 2147483647
Year: 2001
Pages: 289
Authors: David Geary

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