2

I have JButton performing some action via ActionListener. After I try to use Action to bind a keyboard shortcut (following this), mouse click on button works, but no reaction to keyboard.

Code Before

Button created within a panel, actionListener added.

private FooActionListener actionListener = new FooActionListener();

buttonLeft = new JButton("Left");
up.addActionListener(actionListener);

Then, actionPerformed method within FooActionListener class outside the main class:

public void actionPerformed(ActionEvent e) {
    Object source = e.getSource();
    if (source == buttonLeft) { thing.move(Direction.LEFT); }
}

Code After

final String leftText = "Left";
final Action left = new AbstractAction() {

    @Override
    public void actionPerformed(ActionEvent e) {
        thing.move(Direction.LEFT);
    }
};

buttonLeft = new JButton(left);
buttonLeft.setText(leftText);
KeyStroke keyLeft = KeyStroke.getKeyStroke(KeyEvent.VK_A, 0);
buttonLeft.getInputMap(buttonLeft.WHEN_IN_FOCUSED_WINDOW).put(keyLeft,
    "Left");
buttonLeft.getActionMap().put("Left", left );

Update: I am not quite sure that new code actually performs with the mouse as it should. Let’s say the object supposed to move 25 pixels by one click, and it does in the original code. But with the new action it seems that it moves twice or even trice with each click, which suggests some weird behavior of an action.

Community
  • 1
  • 1
theUg
  • 1,284
  • 2
  • 9
  • 14

1 Answers1

2

It's possible that the button is absorbing you mappings, I would, however, do it slightly differently.

Because you've used an Action (correctly), you movement logic is mostly centralised already.

I would simply apply the mappings to the main container instead.

public class TestKeybindings01 {

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

    public TestKeybindings01() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JLabel label;
        private JButton left;
        private JButton right;

        public TestPane() {

            label = new JLabel("Make a choice");
            label.setHorizontalAlignment(JLabel.CENTER);

            LeftAction leftAction = new LeftAction(label);
            RightAction rightAction = new RightAction(label);

            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), "left");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0), "right");

            ActionMap am = getActionMap();
            am.put("left", leftAction);
            am.put("right", rightAction);

            left = new JButton(leftAction);
            right = new JButton(rightAction);

            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.gridwidth = 2;
            gbc.anchor = GridBagConstraints.CENTER;
            gbc.fill = GridBagConstraints.HORIZONTAL;
            add(label, gbc);

            gbc.gridy++;
            gbc.gridwidth = 1;
            add(left, gbc);
            gbc.gridx++;
            add(right, gbc);


        }

    }

    public abstract class AbstractDirectionAction extends AbstractAction {

        private JLabel label;

        public AbstractDirectionAction(JLabel label) {
            this.label = label;
        }

        public JLabel getLabel() {
            return label;
        }

        public void setDirection(String text) {
            getLabel().setText(text);
        }

    }

    public class LeftAction extends AbstractDirectionAction {

        public LeftAction(JLabel label) {
            super(label);
            putValue(NAME, "<<");
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            setDirection("Left");
        }

    }

    public class RightAction extends AbstractDirectionAction {

        public RightAction(JLabel label) {
            super(label);
            putValue(NAME, ">>");
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            setDirection("Right");
        }

    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Can you please elaborate as to what absorbing the mappings mean? Also, check the update to the question, see if that hints at anything. And what is this EventQueue.invokeLater business about? – theUg Dec 15 '12 at 08:59
  • It's possible that the button implementation is "absorbing" key events, possible as part of it's support for mnemonics support, preventing it from raising events for the key bindings. The only reason I can think for a repeating event like this, is you're holding the key down. The `EventQueue.invokeLater` is because Swing is a single threaded API and all updates to the UI MUST be made from within the Event Dispatching Thread. When you `main` is executed, it is guaranteed that you are not withing the EDT – MadProgrammer Dec 15 '12 at 09:35
  • As for repeating, I am talking about mouse clicks at this point. I do exact same mouse clicks as with original code, but it seems like it calls the thing.move method twice on the same click. Is there a way to debug it? – theUg Dec 15 '12 at 09:41
  • Add some diagnostics to your and determine if the action is being executed more then once. – MadProgrammer Dec 15 '12 at 10:53
  • @theUg [example](http://stackoverflow.com/questions/7940173/how-do-i-use-keyeventdispatcher/7940227#7940227) – mKorbel Dec 15 '12 at 16:21