Keyboard input
The obvious approach is the one that does not work, for a reason worth understanding.
The obvious approach
component.addKeyListener(new KeyAdapter() {
@Override public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) close();
}
});
This frequently does nothing, and the reason is focus: key events go to the focused component only. A JPanel is not focusable by default, so a listener on it never fires.
You can force it:
panel.setFocusable(true);
panel.requestFocusInWindow();
and it still breaks as soon as focus moves to a text field or a button, which is to say, as soon as the application does anything.
Key bindings
Swing’s actual mechanism. A key maps to an action name, and the name maps to an Action:
panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
.put(KeyStroke.getKeyStroke("ESCAPE"), "close");
panel.getActionMap()
.put("close", new AbstractAction() {
public void actionPerformed(ActionEvent e) { close(); }
});
Two maps: InputMap from keystroke to name, ActionMap from name to behaviour. The indirection means several keys can trigger one action, and the action can be shared with a button or a menu item.
The three conditions
Which input map you ask for decides when the binding applies:
| Condition | Fires when |
|---|---|
WHEN_FOCUSED |
this component has focus |
WHEN_ANCESTOR_OF_FOCUSED_COMPONENT |
focus is on it or anything inside it |
WHEN_IN_FOCUSED_WINDOW |
the window is active, wherever focus is |
The third is what you want for application-wide shortcuts, and it is why key bindings work where a listener does not.
KeyStroke
KeyStroke.getKeyStroke("ESCAPE");
KeyStroke.getKeyStroke("control S");
KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK);
For menu shortcuts use Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx() rather than hard-coding Control: it gives Command on macOS.
keyTyped versus keyPressed
If you do use a listener: keyPressed and keyReleased report physical keys and carry a key code. keyTyped reports a character produced, so it handles Shift and dead keys correctly but has no code for keys that produce nothing, arrows, function keys, Escape.
Use keyTyped for text, keyPressed for control keys.
Not for text input
If you are collecting typed characters, do not listen at all. Use a JTextField and read its document. Building text input from key events means reimplementing selection, undo, clipboard and input methods, and getting them wrong.