Introduction
In Java version 1.6 or later,
Events are handled quite differently from JDK
1.0.2. This essay will follow a single Event from creationto cremation to give you an idea of how it all fits together. For details,
and practical coding concerns, I suggest you read some other essays on Events such as Richard Baldwin’s instructional essays.
I have written a similar essay for the older JDK (Java Development Kit)
1.0.2 Events.
I’m a guy who can’t use a tool until I have some model of how it works
under the hood. If you are that way, you will like this essay. If you are the kind of
programmer who simply wants to use the tool without any interest of how it works
inside, all you need pay attention to in this essay is how EventListeners work.
Overview
Events are:
- A way one object can call a method in another, but with a delay. Instead of
doing the work right away, the work is postponed by putting a note to do it later
into a queue, so that the caller can get on with more important work.
- A flexible way of deciding at run time precisely which objects should have
their methods called when something interesting happens.
- A way for letting the callee decide when it wants to be called rather than
leaving that decision totally up to the caller.
- A way of scheduling work for other Threads to do
when they have time.
Events are hard to understand because there are three
different mechanism meshing.
- A callback delegate mechanisms so that Sun-written Components can call code you write when something interesting
happens via the various listener interfaces. The listener
mechanism is more complex than your usual callback. The callee, remembers multiple
callers, then calls them back later, asynchronously when something interesting
happens.
- A queuing mechanism to procrastinate work. Basically it is a to-do list. High
priority work gets done first. If the system gets behind, it can skip some of the
validates or repaints since the later ones will cover the earlier ones. The queue
mechanism also coordinates many Threads all making
changes to the screen at once. They put their requests into a queue and a single
Thread does all the work without any worries of other
Threads interfering.
- It is also an Event delivery mechanism so that an
Event (notification of something interesting happening)
can be easily forwarded to other possibly interested parties.
Instead of Events percolating up to parents as they
did in JDK 1.0.2, any object or component can register itself as a listener,
interested in hearing about a type of Events originating
in some other component. When the Event arises, the source
component processes the Event by
dispatching it to each of the registered listeners.
Dispatching is synchronous, i.e. the listener handler routines are called directly
while the calling dispatcher waits for the handler to complete. According to the
specification, the listeners may be informed in any order, but the usual
implementation is a queue, with listeners informed in the same order they were added.
If you want to be absolutely sure of a fixed order of handling listeners, make your
own chain and pass the Event between them. listeners
pretty much only hear Events they are interested in. They
are not responsible for passing the Event on to anyone
else.
The new Event model gives you three places to hook in
your code to process Events.
- via listeners. This is by far the most common way and the easiest to set up.
The listener can be any object, e.g. the enclosing frame, the component itself, a
non-GUI code object, a mom object to mother a set of
components. A given listener can service several sources and a given source can
notify several target listeners. listener methods have names like actionPerformed, windowClosed, mouseMoved. Listeners are often
implemented with Adapter classes so you don’t have to write dummy methods for
Events you are not interested in. Adapter classes have
names like: java.awt.event.FocusAdapter, KeyAdapter and
WindowAdapter. Note, there is no such thing as
ActionAdapter since it has only one method. Often you
use inner classes or anonymous classes to handle fielding Events by extending an adapter class. Be very careful in extending
adapter classes to get your method names and signatures exactly right. Otherwise
you won’t override the corresponding dummy method and your code won’t
ever be executed, leaving you wondering where all the Events went.
- via processEvent which receives all raw
Events for the component. It does not see Events coming in via listeners. If you override it, be sure to call
super.processEvent to ensure all the finer Event handlers, like processKeyEvent,
ProcessActionEvent etc. also get called and the listeners alerted. If you
attempt to restrict the Events coming to processEvent with enableEvents(mask),
if there are listeners registered for Events not in the
mask, those Event classes will arrive anyway.
- via processXXXX(SomeEventClass e), e.g.
processActionEvent, processItemEvent. Every raw
Event for the component of that class comes through
here. It does not see Events coming in via listeners. Be
sure to call super.processXXXX to ensure the listeners
are alerted.
A component might quite reasonably register itself as a listener for
Events arising in itself or for Events arising from the associated native GUI. There is another
deprecated technique to process Events arising in itself.
A component could override its processEvent routine, do some action, then call
super.processEvent (which would inform all the
listeners), then do some other action.
The Cast of Players
Methods for registering a target as a listener of Events
arising in some source include: source.addActionListener(target), addAdjustmentListener,
addComponentListener, addContainerListener, addFocusListener, addItemListener,
addKeyListener, addMouseListener, addMouseMotionListener, addPropertyChangeListener,
addTextListener, addVetoableChangeListener, addWindowListener… Normally
you would invoke these methods but not override them.
The AWT informs the list of registered listeners that an Event has occurred by calling the generic source.processEvent which in turn calls the more specific Event handler: source.processActionEvent, processAdjustmentEvent, processComponentEvent,
processContainerEvent, processItemEvent, processKeyEvent, processMouseEvent,
processMouseMotionEvent, firePropertyChange, processTextEvent, fireVetoableChange,
processWindowEvent… These routines would in turn use the AWTEventMulticaster to inform all the listeners and invoke their
actionPerformed, keyPressed, windowOpened etc. methods in
turn. These processXXX routines typically work by calling the relevant dispatching
routines on the listener targets. It is unlikely you will ever deal with these
methods directly.
Your listener object must implement one or more interfaces such as: ActionListener,
ComponentListener, ContainerListener, FocusListener, KeyListener, ItemListener,
MouseListener, MouseMotionListener, TextListener, WindowListener…
The easiest way to implement those interfaces is to extend one of the related
adapter classes. The adapter classes provide dummy
methods to deal with the subcategories of Events that are
of no interest to you. ComponentAdapter, ContainerAdapter,
FocusAdapter, KeyListener, ItemAdapter, MouseAdapter, MouseMotionAdapter,
TextAdapter, WindowAdapter…
The Event is dispatched to each target Listener by invoking its listening method.
These have names like: target.actionPerformed,
adjustmentValueChanged, componentHidden, componentMoved, componentResized,
componentShown, componentAdded, componentRemoved, focusGained, focusLost,
itemStateChanged, keyPressed, keyReleased, keyTyped, mouseClicked, mouseEntered,
mouseExited, mousePressed, mouseReleased, textValueChanged, windowActivated,
windowClosed, windowClosing, windowDeactivated, windowDecionified, WindowIconified,
windowOpened… You need to write custom versions of these routines.
If there is a listener for an Event type registered,
then Events from the GUI for that Event type will be delivered to that component’s processEvent. The component can control which additional types are
delivered to processEvent by calling enableEvents with a mask. This way events that no one is interested
in are not even delivered to processEvent. It is unlikely
you will be directly involved with enableEvents.
A key point to understand is that a component has two kinds of acceptor routines
for Events.
- The generic processEvent routine primarily accepts
Events indirectly coming from the native GUI.
Application programmers do not normally use these methods.
- The listener methods, e.g. actionPerformed, accept
Events from other components. This is the main tool for
application programmers to deal with Events. Component
writers can also use the EventListener interface by
registering a custom component as a listener to Events
arising in itself.
The Life Cycle of An Event
Where do Events Come From
Daddy?
You synthesize an AWTEvent and post to the
SystemEventQueue for
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent( Event e );
In this Alice in Wonderland world, you set the source field
of the Event to the component or peer where you want the
Event delivered. Later, when the
source delivers the Event to the various listeners, the
term source for this field will make more sense. A
potentially separate thread will service the queue asynchronously. When the
Event percolates to the head of the queue, the dispatcher
will call event.getSource().processEvent(event);
Most Events arise in the native GUI. They can enter the
Java system in one of two ways:
- Notification of interesting happenings in the GUI arrive as a single stream of
messages mentioning native GUI components. This is the traditional native GUI
Event loop handled by hidden code, that calls
getMessage() in a loop. The AWT uses a hash table to
convert references to native GUI components into the corresponding Java components
and peers and composes corresponding Java Events and
enqueues them in the SystemEventQueue.
- Java peer components generate Events based on
information they glean from the native GUI. These too are enqueued in the
SystemEventQueue.
Lightweight components generate Events and post them
directly to themselves, without going through the SystemEventQueue.
Where is the Event Loop?
People who cut their teeth in the
Windows or Mac native low level GUI API are used to writing application-specific code
to handle dispatching using an Event processing loop. The
AWT handles this all automatically and invisibly in EventDispatchThread.run. It reads the stream of native GUI Event messages and translates them, creating Java Events, which it puts into the SystemEventQueue. The thread that processes Events, when it finishing processing one Event, goes and picks up another Event off
the head of the Event queue to dispatch. This is the very
thread that executes most of your application code.
The loop that processes Events does not even start
until your main program has finished executing!! This
Event loop code is not visible to the application
programmer.
You might naïvely imagine there is some sort of mail delivery service that
delivers Events to their destinations. It is much simpler
than that. The Event object contains a reference to the
target object. To deliver/dispatch the Event, all the
system has to do is call a method on that reference. It is a little more complicated
than that since an Event handler may call a chain of
Event handlers. The target object is confusingly called
the source. It makes sense if you consider that
Events usually come back to the same object that generated
them. From the point of view of listeners, the Events
appear to come from these source objects.
You can think of Events as a way of enqueing work to be
done later, usually by other objects.
Creation
Consider what happens behind the scenes when the user
clicks an OK button on the screen. The underlying native GUI notices the mouse click.
It could then work one of two ways:
- The native GUI indirectly notifies the Java Button peer object which creates a
new Event using the ActionEvent constructor.
- The GUI creates a native format message that a button has been clicked. Hidden
code in the AWT reads this stream of messages and translates this one into a new
Event using the ActionEvent
constructor. It has a Hashtable to convert references to native peer components to
the corresponding Java components.
-
- event source
- A Java component closely associated with this event, namely myButton. At first this field acts as the destination for the
Event. Later it informs listeners where the
Event came from.
- event id
- the type of event, in this case ActionEvent.ACTION_PERFORMED.
- event command
- the command string for this action event. In this case it would be the
string OK.
- event modifiers
- the modifiers held down during this click, e.g. CTRL_MASK if the Ctrl key
is held down
Enqueuing
The
EventQueue q = Toolkit.getDefaultToolkit().getSystemEventQueue();
Now it can enqueue the Event for delivery to the source (
myButton) with the system’s postEvent method. We don’t use
q.postEvent( e );
The Event hibernates in the queue waiting its turn to be
dispatched. postEvent will return immediately. It
won’t wait until the new Event has been delivered.
Dispatching
There is a single system thread that grabs the
next item off the queue and dispatches it. You can see the code for it in
EventDispatchThread.run When all the processing for that
Event is complete, it gets the next Event. No other Event processing, not even
a mouse move, can happen until the previous Event has been
processed. I repeat, only one Event is processed at a
time.
Having said that, this single-thread behaviour is not part of the specification.
There are some JVMs (Java Virtual Machines)
where you have several threads processing Events
simultaneously.
It looks at the source field in the Event object and
delivers the Event to that source object.
First it checks the java.awt.Component.eventMask to be
sure the myButton component is willing to accept that flavour of Event.
The button could conceivably have used java.awt.Component.disableEvents to filter out Events such as ours. disableEvents takes
bit masks for groups of Events such as java.awt.AWTEvent.MOUSE_MOTION_EVENT_MASK. You cannot feed it
individual Events such as java.awt.event.MouseEvent.MOUSE_ENTERED because these constants are
not in bit mask form. One my main bitches about Java is this bit map / enumeration
ambiguity.
It also checks if there are listeners for our type of
Event. If there are, even if our Event type is blocked, our Event will still
be delivered.
It delivers the Event to the button component by
calling myButton’s generic processEvent
Event handler:
myButton.processEvent( e );
or e.getSource().processEvent( e );
This dispatching mechanism is efficient because it does not require any sort of table
lookup or nested switch statements.
Classification
processEvent
could in theory do some special processing on the Event,
but usually all it does is classify the Event and call a
more specialised Event handler such as: processActionEvent. (If you override processEvent, make sure you call super.processEvent to ensure the standard Event processing also occurs.) Had we been processing a mouse
Event, we would have called processMouseEvent instead.
Notifying Listeners
A long time ago, before this Event was born, parties interested in this type of Event registered their interest by using myButton. addActionListener(myListener). It is important to remember that
addXXXXListener methods are implemented by the sources, not the listeners. In theory, the
addXXXXListener methods could be invoked by the source or
the listener, but usually they are invoked by a third party, usually an enclosing
frame.
Raw Events arrive via processEvent. They are classified and redirected to more specialised
Event processors like processActionEvent.
processActionEvent can further classify the incoming
Events and can do whatever special processing it wants.
Then it notifies any listeners, one after the other. (If you override processActionEvent, make sure you call super.processActionEvent to ensure the listeners are notified.)
Each listener effectively gets a fresh cloned copy of the original Event. Unfortunately, this wastes cpu cycles and creates a ton of run
time garbage. The intent is to make Events effectively
read-only and to render harmless any malicious component that stored a reference to
an Event. Once an Event enters
Component.processKeyEvent, it isn’t replicated
anymore so presumably some of its fields could be modified and used for
communication. Ideally Event objects would be read-only
and recyclable.
There is no guarantee that the listeners will be notified in any particular order,
however, they will be notified one at a time, never in parallel.
To notify each registered listener, the speaker’s processActionEvent calls each listener’s actionPerformed(e) method by chasing the AWTEventMultiCaster listener chain.
AWTEventMulticaster contains generic code that most
components use for implementing addXXXListener and the
notification of listeners. Component has various add/remove
listener methods that use AWTEventMulticaster that all
Components inherit.
If we had been processing a mouse motion, we would have called the
listener’s mouseMoved method instead. processActionEvent methods are designed to be called by processEvent; methods like actionPerformed and mouseMoved are part
of listener interfaces. Most application coding uses only listeners. Only if you were
inventing a new sort of component would you get involved with processEvent and processActionEvent.
What Are Listeners?
A Listener is any object that:
- implements one of the EventListener interfaces
such as ActionListener, e. g. An ActionListener must implement an actionPerformed method to be called when an Action Event occurs.
- Has hooked itself (or been hooked by a third party) onto one or more
Event-generating components with a method such as
addActionListener.
Listeners are written mostly by application programmers to field notifications
of interesting things that happen to Sun-written widgets such as JButtons.
A listener might be a component, an ordinary object, a special Mom object to
mother a set of components or an anonymous object created from an anonymous class to
simply provide a hook to some other routine.
A listener might be created by extending one of the adapter shortcut classes. The adapter classes give you some dummy
classes to start with in defining a listener where you only want to handle a few
types of Event.
A component may even register itself as a listener for
Events originating it itself. This is the preferred method
for application programmers since there is less possibility of mucking up standard
Event processing.
A listener can field Events from many different
sources. Similarly a source may notify many different listeners. Alternatively you
could have a separate listener object for every source, or anything in between.
Components like JButtons implement interfaces like
ItemSelectable. The provide methods like addItemListener so that classes that implement the ItemListener interface can arrange to be notified of interesting
events in the button, such as it being pressed by first hooking themselves up through
JButton.addItemListener.
The JButton uses EventListenList to maintain the list of interested listeners. When
something interesting happen its calls fireActionPerformed which calls each of the actionPerformed methods of the register listeners. Even though an
event gets passed to the actionPerformed method, the
listener mechanism does not necessarily mean the event spent any time in a queue. It
could be created and passed immediately to the listeners when the interesting event
happened.
You could even use the listener mechanism in your own code, for both the notifier
and listener end, for situations where it is hard to keep track of who should notify
whom when some interesting thing happens. You don’t need to modify the
callee’s code to add more interested parties. You don’t need an event
queue.
You can also set up a listener to monitor all the events. You might use this to
monitor keyboard or mouse activity for a timeout to shutdown after a period of
inactivity.
Listener Actions
What does the listener do when it’s
actionPerformed (or other mouseMoved etc…) method is called? It may classify the
incoming Event based on source, id or class (via
instanceof). Then it would perform some action, do a calculation, change the look of
the screen, delete a file… If it wants, it can use the consume() method to mark the Event
consumedto discourage further processing on it.
It had better be quick! The entire system is at a
standstill, unable to process any more Events until your
listener completes its action. If the action is going to take more than a few
milliseconds you should either:
- Spin off a separate thread to do the work and immediately return from the
Event handler. With Swing you must be careful since
Swing components are not thread safe. You must only access them with the original
thread-dispatching thread.
- Do a little of the work and enqueue a synthetic Event to remind yourself to do a little more of the work later.
- Create a second thread to dispatch Events, popping
them off the SystemEventQueue.
In a similar vein, if it is very important that some action not start until
after the physical paint is finished, the simplest way to arrange that is to have
paint kick it off, perhaps by examining some flag to see
if kickoff is necessary.
How Does consume() Work?
I have not found a precise definition
of what is supposed to happen to a consumed Event. By
experiment, I discovered it seems to stop further processing by the base classes of
the current component. It does not seem to stop notification of others listeners. I
found for example that in TextField, consuming a keystroke
Event would prevent it being displayed. However, this does
not work with the KL group JCTextField or the Swing
JTextField. There you must override processKeyEvent.
After all the listeners have been called, the Event is
checked to see if it has been consumed. If so, then it is not passed onto the next
stage. For e.g. KeyEvents for a TextArea, this means that it is not passed back to the peer and so
does not reach the underlying window/widget and so does not appear.
consume() in KeyListeners is
effectively broken in Swing 0.7 since it is ignored. It looks like the Event is drawn in JTextArea
before being given to the listeners.
For TextFields, you can change the value of the
keystroke in the Event in a KeyListener before passing it on, e.g. convert it to lower or upper
case. However, under Motif, converting to lower case is broken and the entire
technique is broken for Swing. You would have to trap processKeyEvent instead. In a TextField in
the AWT (Advanced Windowing Toolkit), you get the Event before
the character has been painted on the screen. Therefore if you do a getText or setText in your Event handler, very likely will thoroughly confuse the
AWT. The
AWT was not
designed to do anything useful, just to look good on paper. To save your sanity, it
is perhaps best to look on the AWT
as a mystery/adventure game. Your task is to figure out by experiment what the
methods do. Their names give you the barest of clues. You then McGuyver-like must
cobble the odd bits together to create something practical.
How Do You Compose a Listener?
Java version 1.1
added inner classes and anonymous classes to make it easier to hook up listener code.
Here is code that could appear in the middle of a frame
initialisation method that:
- defines a new anonymous class that extends the WindowAdapter listener.
- defines a method in that class to deal with window closing Events.
- allocates an object of that class.
- hooks that object up as a listener to the current frame component.
- throws away the reference to the class. (The object won’t die since it is
on the list of listeners.)
Cremation
The various listeners eventually complete their work
and return to actionPerformed. The return stack unwinds:
actionPerformed returns. processActionEvent returns. processEvent
returns. We are now back at system the code that dispatches Events from the queue. Our Event is
forgotten and the next Event takes the stage. With no more
references to our old Event, garbage collection soon
recycles the RAM (Random Access Memory) that the Event occupied.
Table of Event Classes and Methods
Now we need to generalise from
those specific examples.
Instead of creating an ActionEvent with id ActionEvent.ACTION_PERFORMED, that is dispatched to processEvent, then passed on to processActionEvent, then passed on to an ActionListener (that registered its interest via addActionListener) via its actionPerformed method, there are many other possibilities you can
generate from the following table:
Event Classes and Methods
When |
Event Class |
id |
Specific
Event
Handler |
Listener
Interface |
Listener
Notification
Method |
Listener
registration |
Adapter shortcut |
Typical Components |
Button clicked, MenuItem clicked, but not Choice selected. |
ActionEvent |
ACTION_PERFORMED |
processActionEvent |
ActionListener |
actionPerformed |
addActionListener |
|
Button, JButton,
MenuItem, JMenuItem |
Slider moved |
AdjustmentEvent |
ADJUSTMENT_VALUE_CHANGED |
processAdjustmentEvent |
AdjustmentListener
ScrollPane |
adjustmentValueChanged |
addAdjustmentListener |
|
JSlider |
Component hidden, moved, resized, revealed. |
ComponentEvent |
COMPONENT_HIDDEN
COMPONENT_MOVED
COMPONENT_RESIZED
COMPONENT_SHOWN |
processComponentEvent |
ComponentListener |
componentHidden
componentMoved
componentResized
componentShown |
addComponentListener |
ComponentAdapter |
Component |
something added or removed to this container. |
ContainerEvent |
COMPONENT_ADDED
COMPONENT_REMOVED |
processContainerEvent |
ContainerListener |
componentAdded
componentRemoved |
addContainerListener |
ContainerAdapter |
Container, Panel,
Jpanel, Frame,
JFrame |
Focus changed, either by mouse or tab |
FocusEvent |
FOCUS_GAINED
FOCUS_LOST |
processFocusEvent |
FocusListener |
focusGained
focusLost |
addFocusListener |
FocusAdapter |
Component |
Ancestor added or removed |
HierarchyEvent |
HIERARCHY_CHANGED |
processHierarchyEvent |
HierarchyListener |
hierarchyChanged |
addHierarchyListener |
|
Component |
Ancestor moved or resized |
HierarchyBoundsListener |
ANCESTOR_MOVED ANCESTOR_RESIZED |
processsHierarchyBoundsEvent |
HierarchyBoundsListener |
ancestorMoved
ancestorResized |
addHierarchyBoundsListener |
|
Component |
user keyed something into a text box |
InputMethodEvent |
INPUT_METHOD_TEXT_CHANGED
CARET_POSITION_CHANGED |
processInputMethodEvent |
InputMethodListener |
inputMethodTextChanged
caretPositionChanged |
addInputMethodListener |
|
Component |
user selected a different Choice |
ItemEvent |
DESELECTED
ITEM_STATE_CHANGED
SELECTED |
processItemEvent |
ItemListener |
itemStateChanged |
addItemListener |
|
Choice, JComboBox |
keystroke |
KeyEvent |
KEY_PRESSED (e.g. shift, F1)
KEY_RELEASED
KEY_TYPED (e.g. a A) |
processKeyEvent |
KeyListener |
keyPressed
keyReleased
keyTyped |
addKeyListener |
KeyAdapter |
Panel, JPanel,
Frame, JFrame
not Button, JButton, TextArea, JTextArea, Choice, ComboBox, Checkbox, JCheckbox |
Mouse clicked, entered or exited the region. See also ActionEvent and Item
event. |
MouseEvent |
MOUSE_CLICKED
MOUSE_ENTERED
MOUSE_EXITED
MOUSE_PRESSED
MOUSE_RELEASED |
processMouseEvent |
MouseListener |
mouseClicked
mouseEntered
mouseExited
mousePressed
mouseReleased |
addMouseListener |
MouseAdapter |
Component, Canvas,
Panel, JPanel,
not Button, JButton, TextArea, JTextArea, Choice, ComboBox, Checkbox, JCheckbox |
Mouse moved. You normally don’t want to track these since there are
so many of them. |
MouseMotionEvent |
MOUSE_DRAGGED
MOUSE_MOVED |
processMouseMotionEvent |
MouseMotionListener |
mouseDragged
mouseMoved |
addMouseMotionListener |
MouseMotionAdapter |
Component, Canvas,
Panel, JPanel,
not Button, JButton, TextArea, JTextArea, Choice, ComboBox, Checkbox, JCheckbox |
Mousewheel moved. |
MouseWheelEvent |
|
processMouseWheelEvent |
MouseWheelListener |
mouseWheelMoved |
addMouseWheelListener |
|
Component
Canvas
JPanel |
repaint |
PaintEvent |
PAINT
UPDATE |
paint
update |
|
|
|
|
Component |
value in a TextField changed. |
TextEvent |
TEXT_VALUE_CHANGED |
processTextEvent |
TextListener |
textValueChanged |
addTextListener |
|
TextField, TextArea,
JTextField, JTextArea |
Window state change |
WindowEvent |
WINDOW_ACTIVATED
WINDOW_CLOSED
WINDOW_CLOSING
WINDOW_DEACTIVATED
WINDOW_DEICONIFIED
WINDOW_ICONIFIED
WINDOW_OPENED |
processWindowEvent |
WindowListener |
windowActivated
windowClosed
windowClosing
windowDeactivated
windowDecionified
WindowIconified
windowOpened |
addWindowListener |
WindowAdapter |
Frame, JFrame,
Window, JWindow,
Dialog, JDialog |
For a complete list of the various Listener interfaces and methods see the
JDK API/Package-java.awt.event.html if you want to study the whole
java.awt.event package.
Censoring or Remapping Keystrokes
Let us say you wanted to
write your own version of TextField-like component that
disallowed certain keystrokes, or that processed certain keystrokes specially. You
may have tried the easy Listener/consume() method and
discovered it did not work for your base component. Here is
If you want the underlying base class not to see the Event, you consume it in one of your custom filter routines such as
unicodeKeyPressed(e) and that suppresses the call to
super.processKeyEvent. Something to baffle you. Hitting
the Enter key under NT generates two keystrokes a \r and a \n. On other platforms you
get just a \n. Best just to look for VK_ENTER.
In Swing you can use the Keymap and KeyStroke classes to control the actions of various keystrokes.
Activating
To activate your Listener, you need to create a
callback delegate Listener object and pass it to the Component. Then when something interesting happens, it can call one of
the methods you wrote (to its interface specifications), perhaps indirectly via the
Event queue.
Styles of Activating
There are several styles for activating:
compact
You do it all in one very complicated line, where you define an
anonymous class, create an instance of it and feed it to the addListener method.
The puts everything in one place. The code is an unreadable forest of parentheses
braces and semicolons. They limit you to one component per listener. They
generate one extra inner class per use. They don’t have to deal with the
problem of figuring out which component the incoming event to the listener is
relevant too. There is only one possibility. Java beautifiers tend to over-indent this style. Inner classes
get total and easy access to all the mother class’s fields. The examples
that follow are all of this style.
anonymous class reference
You create a reference to an anonymous inner
class object then in a separate step, feed that to the addListener method. I
prefer this to (1) because it is easier to proofread and SlickEdit beautifier
renders it without excessive indentation. You have the option of reusing a
Listener on several components. You can also collect all your listener logic in
one place separate from the GUI (Graphic User Interface) logic which makes it
easier to maintain. This is the style I most commonly use personally.
named inner class reference
Like (2), but you name the inner classes.
Perhaps a little easier for novices to understand.
named free standing classes
like (3) but with ordinary classes that
implement the Listener interfaces. They don’t have easy access to the
parent class variable. You can use designs where the mother class itself
implements the Listener interface, or where the Listener classes have additional
functionality. The Listener classes can have parameters in their constructors,
which anonymous inner classes cannot. This is the most flexible approach, but
also requires the most typing.
Stomp Style
These are mechanically generated. One master hookListeners method calls a separate method to hook up each
component. Those methods in turn build an anonymous listener and link it to the
widget. On activation they call one line of code, which is yet another method
that actually does the work. It is verbose, but easy to proofread because the
important stuff is separated from the bubblegum. Here is
I would be happy to share my Stomp cookie cutter
classes for you.
Activating a Button
Hooking up some code to be executed when a button is hit
is very similar to the earlier WindowClosing example. There
is no such thing as the ActionAdapter class, so
Activating a FocusListener
Activating a KeyListener
Activating a Choice or JCheckbox ItemListener
JMenuItems
Just a few simple menus will explode into gobs of code if you
provide an anonymous ActionListener for each
JMenuItem. Instead, use a common ActionListener for all JMenuItems or
all JMenuItems in a JMenu. Give
each JMenuItem a one-letter setActionCommand string. In your ActionListener, extract the command letter with getActionCommand().charAt(0) and use it to index a switch to select the piece of code that needs to execute. Remember
to add your ActionListener to every individual
JMenuItem. Best look into Actions for hooking up your JButtons and
JMenuItems if you have more than a handful.
TextListener.textValueChanged
This method receives an
event any time the text value of the component changes including when you
change it programmatically with setText! This
can cause your program to go into heart fibrillation, if your event handler does any
setTexts that in turn will trigger further Events in a Disney-style ping-pong-balls-on-the-mousetraps
fission-style chain reaction. To add to the confusion, some versions of the
JDK don’t do
this.
Missing Events
The newsgroups are full of plaintive
messages from newbies claiming the AWT
is broken because their listeners are not receiving any Events. Here are some things to check:
- When you override a listener method of an Adapter, did you spell the name of
the listener method correctly with exactly the right signature? If you did not, the
compiler will give no error message and all the Events
will continue to be delivered to the Adapter’s dummy listener method. Be
especially careful when you are experimenting with a variety of places to tap in.
You must change the type of the Event parameter.
- Are any Events arriving? Perhaps
the Events arriving are slightly different from those
expected. Put in some debug code to dump out whatever is arriving
to any of the
- Are you sure your component is supposed to be generating the events you expect?
Often components don’t pass on low-level mouseMoved or mouseClicked events, but
instead pass on higher level ItemStateChanged or
actionPerformed events.
- Are you sure you remembered to hook up your component to your listener with
theComponent.addXXXListener(theListener)? Did you use
the right add method?
- You will never get any keystrokes out of a component that does not have
focus.
- Don’t expect keystroke events to appear at a Panel. They will appear at the component in the Panel that has focus.
- Don’t expect keystrokes from a ListBox. You
will get ItemStateChanged events instead.
Mouse Events
There are three different listeners for the
mouse, MouseListener, MouseMotionListener and MouseWheelListener.
If you click the mouse, you will get three events which will show up at
mousePressed, then mouseReleased then mouseClicked.
If you move the mouse onto your component you will first get an event at
mouseEntered. If you move if off the component, you will
get an event at mouseExited.
Every time you move the mouse even a tad, you will get an event at mouseMoved.
If you press a mouse button and drag you will get an event at mousePressed followed by multiple events at mouseDragged reporting the current drag location. When you let go of
the button, you get an event at mouseReleased. You
won’t get an event at mouseClicked
If you twirl the wheelmouse on a Component in a ScrollPane, you won’t see events at the Component’s mouseWheelmoved. Instead
you will see events at mouseMoved, as if there were no
ScrollPane and user had moved the mouse instead. Events
are mouseMoved are about relative motion over the virtual
panned Component in the ScrollPane.
Detecting Shift, Alt and Ctrl, Left and Right Mouse
Buttons
When the user hits Shift, Alt or Ctrl (or a combination) in conjunction
with some ordinary key, you can determine that by looking at the KeyEvent.getModifiers, masking with ORed combinations of inputEvent.SHIFT_MASK, ALT_MASK, CTRL_MASK and META_MASK, in your keyTyped listener.
You can detect the separate pressing of Shift, Alt or Ctrl at the keyPressed() listener and the releasing at the keyReleased listener, by testing KeyEvent.getKeyCode() for a match
with KeyEvent.VK_SHIFT, VK_CONTROL, VK_ALT and KeyEvent.VK_META.
With the advent of Java version 1.3, you can detect and set
the state of the various CapsLock-like states with: public
boolean getLockingKeyState(int keyCode) and public void
setLockingKeyState(int keyCode, boolean on). Valid key codes are: VK_CAPS_LOCK, VK_NUM_LOCK, VK_SCROLL_LOCK, VK_KANA_LOCK.
Dealing with mouse clicks is similar. In your mouseClicked listener, MouseEvent.getModifiers() returns the state of all the modifier keys
and mouse buttons when the event was fired. You can use this method to determine
which mouse button was pressed (or newly released) when a mouse Event was fired. You mask the result with an ORed combination of
InputEvent.ALT_MASK, BUTTON1_MASK, BUTTON2__MASK, BUTTON3_MASK,
CTRL_MASK, META_MASK and SHIFT_MASK. For example,
the following expression is true if the right button was pressed:
The various combinations of ID, keyCode() and keyChar() in the
KeyEvent are quite complicated.
getID() tells you the type of event, basically which
listener was used. KeyCode() gets you a raw Keyboard code
e.g. A. keyChar() gets you
the cooked character e.g. a. See KeyListener in the Java glossary for more discussion.
I suggest downloading my little KeyPlay application. You can play with it, clicking
the mouse and hitting keystrokes. A description of the Events generated is dumped to the console. With it, you can quickly
learn about the ordering of Events and the use of the
fields. You will discover most JVMs
work the same way, with the exception of Microsoft’s which is out in left
field. Check out Oracle’s java.awt. Robot class for generating simulated keystrokes and mouse moves.
Keystroke Events
Whenever you hit a key on the keyboard,
three events will show up at your KeyListener, first
an event at keyPressed, then at keyTyped, then at keyReleased. If you
hold a key down and it repeats, you will see that triple repeated over and over. If
you hit the ctrl key and hold it down you will see repeated events at keyPressed and finally one at keyReleased
when you let go. There is no event at keyTyped. When you
hit Ctrl+C you see keyCode 03, ^C, ETX (End of Text)
not the letter C. Java combines the keys for you. It is not your problem to track the
ctrl or shift state and modify the meanings of other keystrokes.
Keystroke Names
You should write your code using these
KeyChar and KeyCode names from
java.awt.event.KeyEvent rather than hard-coding the
numeric literals. These are the set available in JDK
1.6. The list has been gradually expanding with every new release. You may find some
of these will not work is older JDKs (Java Development Kits).
Most notably, they added
VK_SEPARATOR to correct the spelling mistake in
VK_SEPARATER.
VK keystroke codes numerically
Numeric Ordering |
name |
hex |
decimal |
VK_UNDEFINED |
0x0000 |
0 |
VK_CANCEL |
0x0003 |
3 |
VK_BACK_SPACE (\b) |
0x0008 |
8 |
VK_TAB (\t) |
0x0009 |
9 |
VK_ENTER (\n) |
0x000a |
10 |
VK_CLEAR |
0x000c |
12 |
VK_SHIFT |
0x0010 |
16 |
VK_CONTROL |
0x0011 |
17 |
VK_ALT |
0x0012 |
18 |
VK_PAUSE |
0x0013 |
19 |
VK_CAPS_LOCK |
0x0014 |
20 |
VK_KANA |
0x0015 |
21 |
VK_FINAL |
0x0018 |
24 |
VK_KANJI |
0x0019 |
25 |
VK_ESCAPE |
0x001b |
27 |
VK_CONVERT |
0x001c |
28 |
VK_NONCONVERT |
0x001d |
29 |
VK_ACCEPT |
0x001e |
30 |
VK_MODECHANGE |
0x001f |
31 |
VK_SPACE |
0x0020 |
32 |
VK_PAGE_UP |
0x0021 |
33 |
VK_PAGE_DOWN |
0x0022 |
34 |
VK_END |
0x0023 |
35 |
VK_HOME |
0x0024 |
36 |
VK_LEFT |
0x0025 |
37 |
VK_UP |
0x0026 |
38 |
VK_RIGHT |
0x0027 |
39 |
VK_DOWN |
0x0028 |
40 |
VK_COMMA |
0x002c |
44 |
VK_MINUS |
0x002d |
45 |
VK_PERIOD |
0x002e |
46 |
VK_SLASH |
0x002f |
47 |
VK_0 |
0x0030 |
48 |
VK_1 |
0x0031 |
49 |
VK_2 |
0x0032 |
50 |
VK_3 |
0x0033 |
51 |
VK_4 |
0x0034 |
52 |
VK_5 |
0x0035 |
53 |
VK_6 |
0x0036 |
54 |
VK_7 |
0x0037 |
55 |
VK_8 |
0x0038 |
56 |
VK_9 |
0x0039 |
57 |
VK_SEMICOLON |
0x003b |
59 |
VK_EQUALS |
0x003d |
61 |
VK_A |
0x0041 |
65 |
VK_B |
0x0042 |
66 |
VK_C |
0x0043 |
67 |
VK_D |
0x0044 |
68 |
VK_E |
0x0045 |
69 |
VK_F |
0x0046 |
70 |
VK_G |
0x0047 |
71 |
VK_H |
0x0048 |
72 |
VK_I |
0x0049 |
73 |
VK_J |
0x004a |
74 |
VK_K |
0x004b |
75 |
VK_L |
0x004c |
76 |
VK_M |
0x004d |
77 |
VK_N |
0x004e |
78 |
VK_O |
0x004f |
79 |
VK_P |
0x0050 |
80 |
VK_Q |
0x0051 |
81 |
VK_R |
0x0052 |
82 |
VK_S |
0x0053 |
83 |
VK_T |
0x0054 |
84 |
VK_U |
0x0055 |
85 |
VK_V |
0x0056 |
86 |
VK_W |
0x0057 |
87 |
VK_X |
0x0058 |
88 |
VK_Y |
0x0059 |
89 |
VK_Z |
0x005a |
90 |
VK_OPEN_BRACKET |
0x005b |
91 |
VK_BACK_SLASH |
0x005c |
92 |
VK_CLOSE_BRACKET |
0x005d |
93 |
VK_NUMPAD0 |
0x0060 |
96 |
VK_NUMPAD1 |
0x0061 |
97 |
VK_NUMPAD2 |
0x0062 |
98 |
VK_NUMPAD3 |
0x0063 |
99 |
VK_NUMPAD4 |
0x0064 |
100 |
VK_NUMPAD5 |
0x0065 |
101 |
VK_NUMPAD6 |
0x0066 |
102 |
VK_NUMPAD7 |
0x0067 |
103 |
VK_NUMPAD8 |
0x0068 |
104 |
VK_NUMPAD9 |
0x0069 |
105 |
VK_MULTIPLY |
0x006a |
106 |
VK_ADD |
0x006b |
107 |
VK_SEPARATER |
0x006c |
108 |
VK_SEPARATOR |
0x006c |
108 |
VK_SUBTRACT |
0x006d |
109 |
VK_DECIMAL |
0x006e |
110 |
VK_DIVIDE |
0x006f |
111 |
VK_F1 |
0x0070 |
112 |
VK_F2 |
0x0071 |
113 |
VK_F3 |
0x0072 |
114 |
VK_F4 |
0x0073 |
115 |
VK_F5 |
0x0074 |
116 |
VK_F6 |
0x0075 |
117 |
VK_F7 |
0x0076 |
118 |
VK_F8 |
0x0077 |
119 |
VK_F9 |
0x0078 |
120 |
VK_F10 |
0x0079 |
121 |
VK_F11 |
0x007a |
122 |
VK_F12 |
0x007b |
123 |
VK_DELETE (ASCII (American Standard Code for Information Interchange) del) |
0x007f |
127 |
VK_DEAD_GRAVE |
0x0080 |
128 |
VK_DEAD_ACUTE |
0x0081 |
129 |
VK_DEAD_CIRCUMFLEX |
0x0082 |
130 |
VK_DEAD_TILDE |
0x0083 |
131 |
VK_DEAD_MACRON |
0x0084 |
132 |
VK_DEAD_BREVE |
0x0085 |
133 |
VK_DEAD_ABOVEDOT |
0x0086 |
134 |
VK_DEAD_DIAERESIS |
0x0087 |
135 |
VK_DEAD_ABOVERING |
0x0088 |
136 |
VK_DEAD_DOUBLEACUTE |
0x0089 |
137 |
VK_DEAD_CARON |
0x008a |
138 |
VK_DEAD_CEDILLA |
0x008b |
139 |
VK_DEAD_OGONEK |
0x008c |
140 |
VK_DEAD_IOTA |
0x008d |
141 |
VK_DEAD_VOICED_SOUND |
0x008e |
142 |
VK_DEAD_SEMIVOICED_SOUND |
0x008f |
143 |
VK_NUM_LOCK |
0x0090 |
144 |
VK_SCROLL_LOCK |
0x0091 |
145 |
VK_AMPERSAND |
0x0096 |
150 |
VK_ASTERISK |
0x0097 |
151 |
VK_QUOTEDBL |
0x0098 |
152 |
VK_LESS |
0x0099 |
153 |
VK_PRINTSCREEN |
0x009a |
154 |
VK_INSERT |
0x009b |
155 |
VK_HELP |
0x009c |
156 |
VK_META |
0x009d |
157 |
VK_GREATER |
0x00a0 |
160 |
VK_BRACELEFT |
0x00a1 |
161 |
VK_BRACERIGHT |
0x00a2 |
162 |
VK_BACK_QUOTE |
0x00c0 |
192 |
VK_QUOTE |
0x00de |
222 |
VK_KP_UP |
0x00e0 |
224 |
VK_KP_DOWN |
0x00e1 |
225 |
VK_KP_LEFT |
0x00e2 |
226 |
VK_KP_RIGHT |
0x00e3 |
227 |
VK_ALPHANUMERIC |
0x00f0 |
240 |
VK_KATAKANA |
0x00f1 |
241 |
VK_HIRAGANA |
0x00f2 |
242 |
VK_FULL_WIDTH |
0x00f3 |
243 |
VK_HALF_WIDTH |
0x00f4 |
244 |
VK_ROMAN_CHARACTERS |
0x00f5 |
245 |
VK_ALL_CANDIDATES |
0x0100 |
256 |
VK_PREVIOUS_CANDIDATE |
0x0101 |
257 |
VK_CODE_INPUT |
0x0102 |
258 |
VK_JAPANESE_KATAKANA |
0x0103 |
259 |
VK_JAPANESE_HIRAGANA |
0x0104 |
260 |
VK_JAPANESE_ROMAN |
0x0105 |
261 |
VK_KANA_LOCK |
0x0106 |
262 |
VK_INPUT_METHOD_ON_OFF |
0x0107 |
263 |
VK_AT |
0x0200 |
512 |
VK_COLON |
0x0201 |
513 |
VK_CIRCUMFLEX |
0x0202 |
514 |
VK_DOLLAR |
0x0203 |
515 |
VK_EURO_SIGN |
0x0204 |
516 |
VK_EXCLAMATION_MARK |
0x0205 |
517 |
VK_INVERTED_EXCLAMATION_MARK |
0x0206 |
518 |
VK_LEFT_PARENTHESIS |
0x0207 |
519 |
VK_NUMBER_SIGN |
0x0208 |
520 |
VK_PLUS |
0x0209 |
521 |
VK_RIGHT_PARENTHESIS |
0x020a |
522 |
VK_UNDERSCORE |
0x020b |
523 |
VK_WINDOWS |
0x020c |
524 |
VK_CONTEXT_MENU |
0x020d |
525 |
VK_F13 |
0xf000 |
61440 |
VK_F14 |
0xf001 |
61441 |
VK_F15 |
0xf002 |
61442 |
VK_F16 |
0xf003 |
61443 |
VK_F17 |
0xf004 |
61444 |
VK_F18 |
0xf005 |
61445 |
VK_F19 |
0xf006 |
61446 |
VK_F20 |
0xf007 |
61447 |
VK_F21 |
0xf008 |
61448 |
VK_F22 |
0xf009 |
61449 |
VK_F23 |
0xf00a |
61450 |
VK_F24 |
0xf00b |
61451 |
VK_COMPOSE |
0xff20 |
65312 |
VK_BEGIN |
0xff58 |
65368 |
VK_ALT_GRAPH |
0xff7e |
65406 |
VK_STOP |
0xffc8 |
65480 |
VK_AGAIN |
0xffc9 |
65481 |
VK_PROPS |
0xffca |
65482 |
VK_UNDO |
0xffcb |
65483 |
VK_COPY |
0xffcd |
65485 |
VK_PASTE |
0xffcf |
65487 |
VK_FIND |
0xffd0 |
65488 |
VK_CUT |
0xffd1 |
65489 | |
VK keyboard codes alphabetically
Alpha Ordering |
name |
hex |
decimal |
VK_0 |
0x0030 |
48 |
VK_1 |
0x0031 |
49 |
VK_2 |
0x0032 |
50 |
VK_3 |
0x0033 |
51 |
VK_4 |
0x0034 |
52 |
VK_5 |
0x0035 |
53 |
VK_6 |
0x0036 |
54 |
VK_7 |
0x0037 |
55 |
VK_8 |
0x0038 |
56 |
VK_9 |
0x0039 |
57 |
VK_A |
0x0041 |
65 |
VK_ACCEPT |
0x001e |
30 |
VK_ADD |
0x006b |
107 |
VK_AGAIN |
0xffc9 |
65481 |
VK_ALL_CANDIDATES |
0x0100 |
256 |
VK_ALPHANUMERIC |
0x00f0 |
240 |
VK_ALT |
0x0012 |
18 |
VK_ALT_GRAPH |
0xff7e |
65406 |
VK_AMPERSAND |
0x0096 |
150 |
VK_ASTERISK |
0x0097 |
151 |
VK_AT |
0x0200 |
512 |
VK_B |
0x0042 |
66 |
VK_BACK_QUOTE |
0x00c0 |
192 |
VK_BACK_SLASH |
0x005c |
92 |
VK_BACK_SPACE (\b) |
0x0008 |
8 |
VK_BEGIN |
0xff58 |
65368 |
VK_BRACELEFT |
0x00a1 |
161 |
VK_BRACERIGHT |
0x00a2 |
162 |
VK_C |
0x0043 |
67 |
VK_CANCEL |
0x0003 |
3 |
VK_CAPS_LOCK |
0x0014 |
20 |
VK_CIRCUMFLEX |
0x0202 |
514 |
VK_CLEAR |
0x000c |
12 |
VK_CLOSE_BRACKET |
0x005d |
93 |
VK_CODE_INPUT |
0x0102 |
258 |
VK_COLON |
0x0201 |
513 |
VK_COMMA |
0x002c |
44 |
VK_COMPOSE |
0xff20 |
65312 |
VK_CONTEXT_MENU |
0x020d |
525 |
VK_CONTROL |
0x0011 |
17 |
VK_CONVERT |
0x001c |
28 |
VK_COPY |
0xffcd |
65485 |
VK_CUT |
0xffd1 |
65489 |
VK_D |
0x0044 |
68 |
VK_DEAD_ABOVEDOT |
0x0086 |
134 |
VK_DEAD_ABOVERING |
0x0088 |
136 |
VK_DEAD_ACUTE |
0x0081 |
129 |
VK_DEAD_BREVE |
0x0085 |
133 |
VK_DEAD_CARON |
0x008a |
138 |
VK_DEAD_CEDILLA |
0x008b |
139 |
VK_DEAD_CIRCUMFLEX |
0x0082 |
130 |
VK_DEAD_DIAERESIS |
0x0087 |
135 |
VK_DEAD_DOUBLEACUTE |
0x0089 |
137 |
VK_DEAD_GRAVE |
0x0080 |
128 |
VK_DEAD_IOTA |
0x008d |
141 |
VK_DEAD_MACRON |
0x0084 |
132 |
VK_DEAD_OGONEK |
0x008c |
140 |
VK_DEAD_SEMIVOICED_SOUND |
0x008f |
143 |
VK_DEAD_TILDE |
0x0083 |
131 |
VK_DEAD_VOICED_SOUND |
0x008e |
142 |
VK_DECIMAL |
0x006e |
110 |
VK_DELETE (ASCII
del) |
0x007f |
127 |
VK_DIVIDE |
0x006f |
111 |
VK_DOLLAR |
0x0203 |
515 |
VK_DOWN |
0x0028 |
40 |
VK_E |
0x0045 |
69 |
VK_END |
0x0023 |
35 |
VK_ENTER (\n) |
0x000a |
10 |
VK_EQUALS |
0x003d |
61 |
VK_ESCAPE |
0x001b |
27 |
VK_EURO_SIGN |
0x0204 |
516 |
VK_EXCLAMATION_MARK |
0x0205 |
517 |
VK_F |
0x0046 |
70 |
VK_F1 |
0x0070 |
112 |
VK_F10 |
0x0079 |
121 |
VK_F11 |
0x007a |
122 |
VK_F12 |
0x007b |
123 |
VK_F13 |
0xf000 |
61440 |
VK_F14 |
0xf001 |
61441 |
VK_F15 |
0xf002 |
61442 |
VK_F16 |
0xf003 |
61443 |
VK_F17 |
0xf004 |
61444 |
VK_F18 |
0xf005 |
61445 |
VK_F19 |
0xf006 |
61446 |
VK_F2 |
0x0071 |
113 |
VK_F20 |
0xf007 |
61447 |
VK_F21 |
0xf008 |
61448 |
VK_F22 |
0xf009 |
61449 |
VK_F23 |
0xf00a |
61450 |
VK_F24 |
0xf00b |
61451 |
VK_F3 |
0x0072 |
114 |
VK_F4 |
0x0073 |
115 |
VK_F5 |
0x0074 |
116 |
VK_F6 |
0x0075 |
117 |
VK_F7 |
0x0076 |
118 |
VK_F8 |
0x0077 |
119 |
VK_F9 |
0x0078 |
120 |
VK_FINAL |
0x0018 |
24 |
VK_FIND |
0xffd0 |
65488 |
VK_FULL_WIDTH |
0x00f3 |
243 |
VK_G |
0x0047 |
71 |
VK_GREATER |
0x00a0 |
160 |
VK_H |
0x0048 |
72 |
VK_HALF_WIDTH |
0x00f4 |
244 |
VK_HELP |
0x009c |
156 |
VK_HIRAGANA |
0x00f2 |
242 |
VK_HOME |
0x0024 |
36 |
VK_I |
0x0049 |
73 |
VK_INPUT_METHOD_ON_OFF |
0x0107 |
263 |
VK_INSERT |
0x009b |
155 |
VK_INVERTED_EXCLAMATION_MARK |
0x0206 |
518 |
VK_J |
0x004a |
74 |
VK_JAPANESE_HIRAGANA |
0x0104 |
260 |
VK_JAPANESE_KATAKANA |
0x0103 |
259 |
VK_JAPANESE_ROMAN |
0x0105 |
261 |
VK_K |
0x004b |
75 |
VK_KANA |
0x0015 |
21 |
VK_KANA_LOCK |
0x0106 |
262 |
VK_KANJI |
0x0019 |
25 |
VK_KATAKANA |
0x00f1 |
241 |
VK_KP_DOWN |
0x00e1 |
225 |
VK_KP_LEFT |
0x00e2 |
226 |
VK_KP_RIGHT |
0x00e3 |
227 |
VK_KP_UP |
0x00e0 |
224 |
VK_L |
0x004c |
76 |
VK_LEFT |
0x0025 |
37 |
VK_LEFT_PARENTHESIS |
0x0207 |
519 |
VK_LESS |
0x0099 |
153 |
VK_M |
0x004d |
77 |
VK_META |
0x009d |
157 |
VK_MINUS |
0x002d |
45 |
VK_MODECHANGE |
0x001f |
31 |
VK_MULTIPLY |
0x006a |
106 |
VK_N |
0x004e |
78 |
VK_NONCONVERT |
0x001d |
29 |
VK_NUM_LOCK |
0x0090 |
144 |
VK_NUMBER_SIGN |
0x0208 |
520 |
VK_NUMPAD0 |
0x0060 |
96 |
VK_NUMPAD1 |
0x0061 |
97 |
VK_NUMPAD2 |
0x0062 |
98 |
VK_NUMPAD3 |
0x0063 |
99 |
VK_NUMPAD4 |
0x0064 |
100 |
VK_NUMPAD5 |
0x0065 |
101 |
VK_NUMPAD6 |
0x0066 |
102 |
VK_NUMPAD7 |
0x0067 |
103 |
VK_NUMPAD8 |
0x0068 |
104 |
VK_NUMPAD9 |
0x0069 |
105 |
VK_O |
0x004f |
79 |
VK_OPEN_BRACKET |
0x005b |
91 |
VK_P |
0x0050 |
80 |
VK_PAGE_DOWN |
0x0022 |
34 |
VK_PAGE_UP |
0x0021 |
33 |
VK_PASTE |
0xffcf |
65487 |
VK_PAUSE |
0x0013 |
19 |
VK_PERIOD |
0x002e |
46 |
VK_PLUS |
0x0209 |
521 |
VK_PREVIOUS_CANDIDATE |
0x0101 |
257 |
VK_PRINTSCREEN |
0x009a |
154 |
VK_PROPS |
0xffca |
65482 |
VK_Q |
0x0051 |
81 |
VK_QUOTE |
0x00de |
222 |
VK_QUOTEDBL |
0x0098 |
152 |
VK_R |
0x0052 |
82 |
VK_RIGHT |
0x0027 |
39 |
VK_RIGHT_PARENTHESIS |
0x020a |
522 |
VK_ROMAN_CHARACTERS |
0x00f5 |
245 |
VK_S |
0x0053 |
83 |
VK_SCROLL_LOCK |
0x0091 |
145 |
VK_SEMICOLON |
0x003b |
59 |
VK_SEPARATER |
0x006c |
108 |
VK_SEPARATOR |
0x006c |
108 |
VK_SHIFT |
0x0010 |
16 |
VK_SLASH |
0x002f |
47 |
VK_SPACE |
0x0020 |
32 |
VK_STOP |
0xffc8 |
65480 |
VK_SUBTRACT |
0x006d |
109 |
VK_T |
0x0054 |
84 |
VK_TAB (\t) |
0x0009 |
9 |
VK_U |
0x0055 |
85 |
VK_UNDEFINED |
0x0000 |
0 |
VK_UNDERSCORE |
0x020b |
523 |
VK_UNDO |
0xffcb |
65483 |
VK_UP |
0x0026 |
38 |
VK_V |
0x0056 |
86 |
VK_W |
0x0057 |
87 |
VK_WINDOWS |
0x020c |
524 |
VK_X |
0x0058 |
88 |
VK_Y |
0x0059 |
89 |
VK_Z |
0x005a |
90 | |
Event vs. AWTEvent
In JDK
1.0.2 there was basically only one kind of Event, called the java.awt.Event. In
Java version 1.6 or later there is a base Event called a java.awt.event.AWTEvent.
All the other types of Event such as java.awt.event.actionEvent and java.awt.event.WindowEvent are derived from it. It is a bit confusing
since the Event package for Java version 1.6 or later
is called java.awt.event instead of java.awt.AWTEvent as you might expect.
Synthetic Events
There are a number of ways to fake an
event.
- Check out Oracle’s java.awt.Robot class for generating simulated keystrokes and mouse moves.
- The simplest is just to call a Listener method directly with a dummy
Event object, filled in with just enough data to keep
the method happy.
- Create an Event and introduce it to the Component that will handle it at the processEvent method. with:
- Create an Event and
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent( Event e );
- Generating MouseMoved Events has no effect on the screen mouse cursor. To make the
underlying native GUI see your generated Events, use the Robot class to generate
move clicks, moves etc.
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.Robot;
...
Robot r = new Robot();
r.mouseMove( 100, 100 );
r.keyPress( KeyEvent.VK_A );
r.mousePress( InputEvent.BUTTON1_MASK );
Changes from JDK 1.0
In addition to
the complete revamping of how it works inside, some of the interfacing details have
changed too. The names of the various Events no longer
live in the Event class. They live in the various
subclasses of AWTEvent e.g. WindowEvent.WINDOW_CLOSED. Some Events have
been renamed. WINDOW_DESTROY is now WINDOW_CLOSED. You can no longer access the source and id fields of an Event directly. You must call getSource
and getID. handleEvent is now
called processEvent. See my essay on JDK 1.0 events, primarily now of
historical interest.
Changes with Java version 1.3
Starting with
Java version 1.3 it is
possible to insert custom Events into the system
Event queue with awt.EventQueue.invokeLater( Runnable r ) (or invokeAndWait). It also appears to be possible to take direct control
of the Event queue by sub-classing it using getToolkit().getSystemEventQueue().push( subclass of awt.EventQueue).
You could then add some monitoring debug code for example.
The catch is MouseEvents you create this way
don’t move the mouse cursor. To insert native events that will, see the
Robot class. It can also be used to generate synthetic
keystroke events. The Robot class helps you create self
running demos.
Repaint
You may have seen mention of PaintEvent.PAINT and PaintEvent.UPDATE
events. These are used to control how components are repainted. The repaint mechanism
partly uses the SystemEventQueue and partly the queue
inside the native GUI. See repaint
in the Java & Internet Glossary for details on how it works.
Learning More
Oracle’s Javadoc on
EventQueue class : available:
Credits
This article would not have been possible without the
patient assistance of Jan Newmarch who researched the material.
Book referral for Tricks of The Java Programing Gurus
|
recommend book⇒Tricks of The Java Programing Gurus |
by |
Glenn L. Vanderburg |
978-1-57521-102-2 |
paperback |
publisher |
Sams |
published |
1996-07 |
Chapter by Jan Newmarch |
|
Greyed out stores probably do not have the item in stock. Try looking for it with a bookfinder. |
Richard Baldwin and Peter Mehlitz helped explain the Java version 1.1 or later
event processing. Steve Odendahl pointed out the
mismatched signature problem in Adapters. Tuyen Tran pointed out the problems with
Swing and threading.
Summary
The key point to understand is that a component has two
kinds of acceptor routines for Events. The generic
processEvent routine primarily accepts events coming indirectly from the native
GUI which it
then fobs off on the more specific processXXXXEvent
methods. The listener methods, e.g. actionPerformed,
accept Events from other components. The listener methods
are primarily for application programmers and those who customise existing
components; the processXXXXEvent methods are primarily
for the writers of radically new components.