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.