Using Java “Key Bindings” To Handle Keyboard Input

slimwhimperΛογισμικό & κατασκευή λογ/κού

3 Νοε 2013 (πριν από 3 χρόνια και 10 μήνες)

77 εμφανίσεις


1
Using Java “Key Bindings” To Handle Keyboard Input
A conventional approach for handling keyboard input is to create an object which
implements the “KeyListener” interface, add that object as a listener to a GUI
component, and insure that the GUI component has “focus” when it is desired to catch
key presses.
While this is approach conceptually straightforward, it is complicated by two issues:
the fact that the “focus owner” can change unexpectedly (including a change for reasons
outside the control of the program), and the fact that in most circumstances what the
KeyListener needs to do is invoke some “command object” (e.g. a Java Action object)
corresponding to a given key – meaning that the program must arrange for a “connection”
between the listener and the appropriate command object.
An alternative mechanism for handling keyboard input with Java Swing components
is to use a Java mechanism called a “key binding”. Under this approach, the program
simply specifies, for a given component, the “binding” or “mapping” between any key of
interest and the Action (command) object to be invoked when that key is pressed (or
released). Key bindings are associated with a specific GUI component; however, the
binding can be specified as applying under one of several circumstances:
• when the component has focus;
• when the component is an ancestor of the component that has focus (for
example, a container such as a JPanel that contains the component that has
focus);
• when the component that has focus is contained anywhere in the “active
window”.

Every object of type JComponent (including, for example, a JPanel) has a set of
“maps”. There are two kinds of maps: one for input keys, and one for actions. Input
key maps hold objects of type “KeyStroke”, which describe specific keys. Action maps
hold objects of type “Action” (i.e., command objects). To create a key binding you store a
KeyStroke object in an input map under a chosen “name” (typically, for example, the
name of the command associated with the key, although the name can actually be any
String). You then store the corresponding Action object in the action map under the same
name.
There is only one action map per JComponent. However, there are three different
input key maps for each JComponent: one that applies when the component has focus;
one that applies when some ancestor of the component has focus, and one that applies
when the active window contains the component has focus.
When a key is pressed, the underlying system first sends the key to any registered
KeyListeners for the component that has focus (if there are any registered listeners),
meaning that the “key listener” approach has precedence.

2
However, the underlying system ALSO looks at all the input maps for the
components in the current window, starting with the component that has focus, and after
that at the input maps for the ancestor(s), and finally at the input map for the window
itself. If any of those maps (checked in that order) has a KeyStroke binding for the key
which was pressed, the “name” for that KeyStroke is used to do a lookup in the "action
map". If there is a Java "Action" object associated with that name, the "execute" method
("actionPerformed()") for that action is automatically invoked.
The effect of all this is that you don't have to implement key listeners to handle key
input; you can instead create "KeyStroke" objects representing each key you're interested
in, put them in the appropriate "input map" under some name, and then put an Action
object under the same name in the action map. If key bindings are placed in the “when
focused component is in the active window” map, then any time the window (frame) is
active, keystrokes will invoke the corresponding Action objects.
The snippet of code below shows how to use key bindings to cause the ‘f’ key to
invoke an Action object (command) named “fireCommand” associated with a JPanel
called “centerPanel”. Once this code is executed, pressing the ‘f’ key will invoke the
“fireCommand” object any time the focus lies anywhere in the window containing the
centerPanel.



//get the "focus is in the window" input map for the center panel
int mapName = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap imap = centerPanel.getInputMap(mapName);

//create a keystroke object to represent the "f" key
KeyStroke fKey = KeyStroke.getKeyStroke('f');

//put the "f-Key" keystroke object into the panel’s "when focus is
// in the window" input map under the identifier "fire"
imap.put(fKey, "fire");

//get the action map for the panel
ActionMap amap = centerPanel.getActionMap();

//put the "fireCommand" object (an instance of class "fireCommand";
// an Action object created elsewhere that performs the “fire” operation)
// into the panel's actionMap
amap.put("fire", fireCommand);

//have the frame request keyboard focus
this.requestFocus();