Keylistener not working for JPanel

41,446

Solution 1

If you search this problem, you'll see that it is asked and has been solved many times.

  • KeyListeners need to be on the focused component to work. One solution is to give your component the focus after first making it focusable.
  • Better by a long shot however is to use Key Bindings. Google the tutorial on this.

Please have a look at my answer to this question for more on this, including many of the gory details.

Solution 2

For reference, I've create an example using your approach; while it works, it also suggests a focus problem elsewhere in your code. Key Bindings avoid this, as shown here.

Addendum: Here's my working key binding.

private static class TestPanel extends JPanel {

    private static final String LEFT = "Left";
    private Action left = new AbstractAction(LEFT) {
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println(LEFT);
        }
    };
    private static final String RIGHT = "Right";
    private Action right = new AbstractAction(RIGHT) {
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println(RIGHT);
        }
    };

    public TestPanel() {
        this.getInputMap().put(
            KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), LEFT);
        this.getActionMap().put(LEFT, left);
        this.getInputMap().put(
            KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), RIGHT);
        this.getActionMap().put(RIGHT, right);
    }
}

Original SSCCE:

import java.awt.EventQueue;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;

/**
 * @see https://stackoverflow.com/a/16531380/230513
 */
public class Test {

    private void display() {
        JFrame f = new JFrame("Test");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(new TestPanel());
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private static class TestPanel extends JPanel implements KeyListener {

        public TestPanel() {

            this.addKeyListener(this);
            this.setFocusable(true);
            this.requestFocusInWindow();
        }

        @Override
        public void keyPressed(KeyEvent e) {
            if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
                System.out.println("Right");
            }

            if (e.getKeyCode() == KeyEvent.VK_LEFT) {
                System.out.println("Left");
            }
        }

        @Override
        public void keyTyped(KeyEvent e) {
        }

        @Override
        public void keyReleased(KeyEvent e) {
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Test().display();
            }
        });
    }
}

Solution 3

For receives key events on JPanel you must set focus:

setFocusable(true);
requestFocus(); 

the JPanel now has focus, so it receives key events

Share:
41,446
user2373733
Author by

user2373733

Updated on July 13, 2022

Comments

  • user2373733
    user2373733 almost 2 years

    I am trying to do something when one of the arrow keys are pressed using the KeyListener in my JPanel class. Here is my code:

    public class TestPanel extends JPanel implements KeyListener{
    
        public TestPanel(){
            this.addKeyListener(this);
            this.setFocusable(true);
            this.requestFocusInWindow();
        }
    
        public void keyPressed(KeyEvent e) {
            if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
                System.out.println("Right");
    
            }
    
            if (e.getKeyCode() == KeyEvent.VK_LEFT) {
                System.out.println("Left");
            }
    
        }
    
        public void keyTyped(KeyEvent e) {}
        public void keyReleased(KeyEvent e) {}
    }
    

    My main method adds a new instance of this panel to a frame and displays it. Do I need to add the keylistener to the JFrame? In my case, this would be difficult and inefficient, so I would like to make it work with this JPanel if possible. Anyone know what I am doing wrong?

    EDIT: Key Bindings code that does not work either:

    public class GamePanel extends JPanel implements ActionListener{
    
    //Constructor
    public GamePanel(){
    
        setupKeyBinding();
        this.setFocusable(true);
        this.requestFocusInWindow();
    
    
    }
    
    private void setupKeyBinding() {
        int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
        InputMap inMap = getInputMap(condition);
        ActionMap actMap = getActionMap();
    
        inMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "Left");
        actMap.put("Left", new leftAction());
    }
    
    private class leftAction extends AbstractAction {
    
           public void actionPerformed(ActionEvent e) {
              System.out.println("test");
           }
    }
    
    public void actionPerformed(ActionEvent e) {
        //some other game info
    }
    } 
    

    Can someone tell me why this doesnt work either? (my second action listener is for other stuff needed for my game)