Leepoint Java Reference Notes on the Java language and its standard library.

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.