Java Swing使用箭头按钮发送导航命令

时间:2015-02-26 05:44:16

标签: java swing key-bindings

我需要使用JButton和键盘发送导航命令。如果按下“向上”按钮/向上键我需要发送“向北移动”命令,如果按下向上和向左按钮(从键盘),我需要发送“向西北移动”命令等。命令应该是定期发送(每1秒)。以下是我的代码。

要解释更多,

视图上有三个JButton。我们称他们为jUp,jLeft和jRight。当用户按下视图上的jUp按钮时,程序应定期发送moveNorth命令,直到用户释放jUp按钮。当用户按下键盘上的向上按钮时,应该发生同样的事情,并且应该按下jUp按钮,直到用户释放键盘向上按钮。当用户同时按下键盘和左键时,jUp和jLeft按钮应该按下,直到用户松开键盘按钮。在用户释放键盘按钮之前,应定期发送移动northWest命令。在代码中我刚刚使用System.out.println打印命令。

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;

import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;

public class ButtonDemo {
    private JPanel buttons;
    private Timer t;
    private JButton upButton;
    private JButton leftButton;
    private JButton rightButton;

    public static void main(String[] args) {
        new ButtonDemo().run();

    }

    public ButtonDemo() {
        buttons = new JPanel(new BorderLayout());
        this.t = new Timer(1000, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (upButton.isSelected() && leftButton.isSelected()) {
                    System.out.println("Move north west");
                } else if (upButton.isSelected() && rightButton.isSelected()) {
                    System.out.println("Move north east");
                } else if (upButton.isSelected()) {
                    System.out.println("Move north");
                } else {
                    t.stop();
                }
            }
        });
    }

    void run() {

        this.upButton = new JButton("Up");
        buttons.add(upButton, BorderLayout.NORTH);
        setupButton(upButton, "Up", KeyEvent.VK_UP);

        this.leftButton = new JButton("Left");
        buttons.add(leftButton, BorderLayout.WEST);
        setupButton(leftButton, "Left", KeyEvent.VK_LEFT);

        this.rightButton = new JButton("Right");
        buttons.add(rightButton, BorderLayout.EAST);
        setupButton(rightButton, "Right", KeyEvent.VK_RIGHT);

        JFrame frame = new JFrame("FrameDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(buttons, BorderLayout.CENTER);
        frame.pack();
        frame.setVisible(true);
    }

    private void setupButton(JButton button, String key, int vkUp) {
        buttons.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(vkUp, 0),
                key + " pressed");
        buttons.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(vkUp, 0, true),
                key + " released");
        buttons.getActionMap().put(key + " pressed", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                button.setSelected(true);
                pressed(key);
            }
        });

        buttons.getActionMap().put(key + " released", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                button.setSelected(false);
            }
        });
    }

    private void pressed(String key) {
        if (!t.isRunning()) {
            t.start();
        }
    }
}

现在提出问题。

a)即使我调用了setSelected方法,按钮状态也不会变为按下状态。 (视觉上它不会改变为按下状态)。我怎样才能做到这一点?

b)是否有更好/更标准的方法来实现此功能?使用助记符/ ExecutorService等..?我是否正确将操作添加到“buttons”元素的输入映射中。 (buttons.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)是否正确?)面板将位于选项卡中,选择该选项卡时按钮应该有效。

1 个答案:

答案 0 :(得分:2)

JButton没有selected州,它有armed。为了在释放鼠标或键时保持“按下”状态的按钮,您必须使用JToggleButton

我个人会亲自监视按钮状态,而是使用某种enum或其他可以在Set中添加和删除的常量。这使得实现国家的手段与作用于国家的过程脱钩。

从那里,您可以使用单个Action来通知某种观察者状态已经发生变化

Navigation

import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.Set;
import java.util.StringJoiner;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

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

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

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

    public static class TestPane extends JPanel {

        public enum Direction {

            UP, DOWN, LEFT, RIGHT;
        }

        private JToggleButton[] buttons;
        private Set keys;

        private Timer timer;

        private JLabel direction;

        public TestPane() {
            keys = new HashSet();
            direction = new JLabel("Stopped");

            timer = new Timer(1000, new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    if (keys.isEmpty()) {
                        ((Timer) e.getSource()).stop();
                        direction.setText("Stopped");
                    } else {
                        StringJoiner joiner = new StringJoiner("-");
                        if (keys.contains(Direction.UP)) {
                            joiner.add("North");
                        }
                        if (keys.contains(Direction.DOWN)) {
                            joiner.add("South");
                        } 
                        if (keys.contains(Direction.LEFT)) {
                            joiner.add("West");
                        } 
                        if (keys.contains(Direction.RIGHT)) {
                            joiner.add("East");
                        }
                        direction.setText(joiner.toString());
                    }
                }

            });

            Monitor monitor = new Monitor() {
                @Override
                public void pressed(Direction direction) {
                    keys.add(direction);
                    timer.restart();
                }

                @Override
                public void released(Direction direction) {
                    keys.remove(direction);
                }
            };

            MovementAction up = new MovementAction("Up", Direction.UP, monitor);
            MovementAction down = new MovementAction("Down", Direction.DOWN, monitor);
            MovementAction left = new MovementAction("Left", Direction.LEFT, monitor);
            MovementAction right = new MovementAction("Right", Direction.RIGHT, monitor);

            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridy = 0;
            gbc.gridx = 1;

            buttons = new JToggleButton[4];
            buttons[0] = new JToggleButton(up);
            buttons[1] = new JToggleButton(down);
            buttons[2] = new JToggleButton(left);
            buttons[3] = new JToggleButton(right);

            add(buttons[0], gbc);
            gbc.gridy = 2;
            add(buttons[1], gbc);

            gbc.gridy = 1;
            gbc.gridx = 0;
            add(buttons[2], gbc);
            gbc.gridx++;
            add(direction, gbc);
            gbc.gridx++;
            add(buttons[3], gbc);

            addTriggerKeyBindingTo(buttons[0], KeyEvent.VK_UP, KeyEvent.VK_W, KeyEvent.VK_NUMPAD8);
            addTriggerKeyBindingTo(buttons[1], KeyEvent.VK_DOWN, KeyEvent.VK_S, KeyEvent.VK_NUMPAD2);
            addTriggerKeyBindingTo(buttons[2], KeyEvent.VK_LEFT, KeyEvent.VK_A, KeyEvent.VK_NUMPAD6);
            addTriggerKeyBindingTo(buttons[3], KeyEvent.VK_RIGHT, KeyEvent.VK_D, KeyEvent.VK_NUMPAD4);

        }

        protected void addTriggerKeyBindingTo(JToggleButton comp, int... virtualKeys) {
            InputMap im = comp.getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap am = comp.getActionMap();

            for (int key : virtualKeys) {
                im.put(KeyStroke.getKeyStroke(key, 0), "trigger");
            }
            am.put("trigger", new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    JToggleButton button = (JToggleButton) e.getSource();
                    button.doClick();
                }
            });
        }

        protected class MovementAction extends AbstractAction {

            private Direction direction;
            private Monitor monitor;

            public MovementAction(String name, Direction direction, Monitor monitor) {
                putValue(NAME, name);
                this.direction = direction;
                this.monitor = monitor;
                putValue(SELECTED_KEY, false);
            }

            @Override
            public void actionPerformed(ActionEvent e) {
                boolean selected = (boolean) getValue(SELECTED_KEY);
                if (selected) {
                    monitor.pressed(direction);
                } else {
                    monitor.released(direction);
                }
            }

        }

        public interface Monitor {

            public void pressed(Direction direction);

            public void released(Direction direction);

        }

    }

}

现在,此示例并不关心,但您可以使用Monitor来控制在任何时间触发的键/按钮,可能是从boolean返回pressed值例如......