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:
| 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 Sun’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.
| 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 | |
| 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 Sun’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. |
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.