How to Listen for Key Presses (Within Java Swing) Across All Components

How can I listen for key presses (within Java Swing) across all components?

It is possible.

KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addKeyEventDispatcher(new KeyEventDispatcher() {
@Override
public boolean dispatchKeyEvent(KeyEvent e) {
System.out.println("Got key event!");
return false;
}
});

That will grab all key events. Returning false allows the keyboard focus manager to resume normal key event dispatching to the various components.

If you want to catch key combos, you can keep a set of "pressed keys." Whenever a key is pressed, add it to the set and check what keys are already in the set. When a key is released, remove it from the set.

Java swing - Processing simultaneous key presses with key bindings

I personally would use the KeyListener interface to keep track of what the user has typed and then just register within a List the keys that have been pressed (without releasing) and then collect them once any key has been released.

Make sure though to add to the list only the keys that are not present yet because the keyPressed event is fired multiple times while the user is holding down a key.

I've also created a little sample to give you the idea.

public class MyClass extends JFrame implements KeyListener {

private JTextArea textArea;
private List<Character> listKeys;

public MyClass() {
setTitle("test");

listKeys = new ArrayList<>();
textArea = new JTextArea();
textArea.addKeyListener(this);

setLayout(new BorderLayout());
add(textArea, BorderLayout.CENTER);

setLocation(50, 50);
setSize(500, 500);
setVisible(true);
}

@Override
public void keyTyped(KeyEvent e) {
}

@Override
public void keyPressed(KeyEvent e) {
if (!listKeys.contains(e.getKeyChar())) {
listKeys.add(e.getKeyChar());
}
}

@Override
public void keyReleased(KeyEvent e) {
if (listKeys.isEmpty()) {
return;
}

if (listKeys.size() > 1) {
System.out.print("The key combination ");
} else {
System.out.print("The key ");
}
for (Character c : listKeys) {
System.out.print(c + " ");
}
System.out.println("has been entered");
listKeys.clear();
}

public static void main(String[] args) {
new MyClass();
}
}

Java: Add KeyListener to JFrame and its components

Do you know how to fix this?

Yes, use Key Bindings and not a KeyListener. The bindings can be set to work even if the bound component doesn't have the focus, one of their key advantages (no pun intended). The tutorial can be found here: Key Bindings Tutorial.

Note that when you get the InputMap from your bound component, be sure to use the right condition, i.e.,

InputMap inputMap = myComponent.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);

There are three input maps for each component, and the one above will be active when the component is held in a top-level window (such as a JFrame) that is currently active. This means that the binding will work even if the component itself doesn't have focus.


You can find some of my sample programs that use Key Bindings, often in conjunction with Swing animation, here:

  • Java Animate JLabel
  • MouseEntered and KeyPressed at Same Time Java Swing
  • Override VK_Tab Focus action
  • How to make an image move while listening to a keypress in Java.
  • Java Key Event won't execute once I press a button
  • Did I structure this key listener properly?

JPanel keylistener

In a typical fashion, your key event is not intercepted by the correct Swing component. You have to understand that the first component below the cursor will receive the keyboard event. Were you to select a button with your keyboard, it would be this JButton that would receive the key event.

To make sure you get all those events, you don't have to register on components, but rather by using a KeyboardFocusManager, which will receive key events wherever they occur.

Your code then require the following elements

KeyEventDispatcher myKeyEventDispatcher = new DefaultFocusManager();
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(myKeyEventDispatcher);

myKeyEventDispatcher will then receive calls to dispatchKeyEvent whenever a key is pressed, wherever it is in UI. This way, you can make sure your code is correctly called.

The alternative method of registering key listener would require you to use a HierarchyListener in order for your key listener to be added:removed to each and every swing component that appear to be added/removed as a child of your root component. This is not only cumbersome to write, but also really hard to debug and understand.

This is why I prefer the more brute-force, but although quite elegant way of adding application global key listener to a specific keyboard focus manager.

Swing's KeyListener and multiple keys pressed at the same time

Use a collection to remember which keys are currently pressed and check to see if more than one key is pressed every time a key is pressed.

class MultiKeyPressListener implements KeyListener {
// Set of currently pressed keys
private final Set<Integer> pressedKeys = new HashSet<>();

@Override
public synchronized void keyPressed(KeyEvent e) {
pressedKeys.add(e.getKeyCode());
Point offset = new Point();
if (!pressedKeys.isEmpty()) {
for (Iterator<Integer> it = pressedKeys.iterator(); it.hasNext();) {
switch (it.next()) {
case KeyEvent.VK_W:
case KeyEvent.VK_UP:
offset.y = -1;
break;
case KeyEvent.VK_A:
case KeyEvent.VK_LEFT:
offset.x = -1;
break;
case KeyEvent.VK_S:
case KeyEvent.VK_DOWN:
offset.y = 1;
break;
case KeyEvent.VK_D:
case KeyEvent.VK_RIGHT:
offset.x = 1;
break;
}
}
}
System.out.println(offset); // Do something with the offset.
}

@Override
public synchronized void keyReleased(KeyEvent e) {
pressedKeys.remove(e.getKeyCode());
}

@Override
public void keyTyped(KeyEvent e) { /* Event not used */ }
}

How to get a Key Event in a Java Swing JFrame instance which has many JTextFields?

KeyListener only works (in most cases) for components that are currently focused. It also won't work if you add it to a container of the focused component in case when that component consumes key events. Registering a keyboard action would be a better way to approach window-wide hotkeys.

Here is a small example of how it could be done:

public class FrameHotkey
{
public static void main ( final String[] args )
{
SwingUtilities.invokeLater ( new Runnable ()
{
@Override
public void run ()
{
final JFrame frame = new JFrame ();

frame.setLayout ( new FlowLayout ( FlowLayout.CENTER, 15, 15 ) );
frame.add ( new JLabel ( "Field 1:" ) );
frame.add ( new JTextField ( "Field 1", 15 ) );
frame.add ( new JLabel ( "Field 2:" ) );
frame.add ( new JTextField ( "Field 2", 15 ) );

// Hotkey for the F1 in window
frame.getRootPane ().registerKeyboardAction ( new ActionListener ()
{
@Override
public void actionPerformed ( final ActionEvent e )
{
JOptionPane.showMessageDialog ( frame, "F1 have been pressed!" );
}
}, KeyStroke.getKeyStroke ( KeyEvent.VK_F1, 0 ), JComponent.WHEN_IN_FOCUSED_WINDOW );

frame.setDefaultCloseOperation ( WindowConstants.EXIT_ON_CLOSE );
frame.pack ();
frame.setLocationRelativeTo ( null );
frame.setVisible ( true );
}
} );
}
}

Note that I'm registering hotkey on JRootPane of the JFrame here, but it generally does't matter because condition is JComponent.WHEN_IN_FOCUSED_WINDOW - meaning that you can register it on any component in the window and as long as the window is focused in the system you will receive the action event.

Alternatively, if your application has a JMenuBar with menu items that would perform the actions you desire - you can specify accelerators for those menu items and they will handle the action upon specified hotkey on their own.

I also recommend reading the article from Swing tutorial camickr offered in the other answer.

Add listener to all objects in a JPanel

JPanel extends JComponent, that inherits Container. You can use getComponents(). You get a Component[] array, which you can loop through and add for each component, that is a subclass of Component like Button, and add the same ActionListener for each component. See http://docs.oracle.com/javase/6/docs/api/java/awt/Component.html



Related Topics



Leave a reply



Submit