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.