我需要使用 JButtons 和键盘发送导航命令。如果按下“向上”按钮/向上键,我需要发送“向北移动”命令,如果按下向上和向左按钮(从键盘),我需要发送“向西北移动”命令等。命令应该是定期发送(每 1 秒)。以下是我的代码。
为了解释更多,
View 上有三个 JButton。我们称它们为 jUp、jLeft 和 jRight。当用户按下 View 上的 jUp 按钮时,程序应定期发送 moveNorth 命令,直到用户释放 jUp 按钮。当用户按下键盘上的向上按钮时,也会发生同样的事情,并且 jUp 按钮应该看起来被按下,直到用户释放键盘上的向上按钮。当用户同时按下键盘的向上和向左按钮时,jUp 和 jLeft 按钮应显示为按下状态,直到用户释放键盘按钮。在用户释放键盘按钮之前,应定期发送向西北移动命令。在代码中,我刚刚使用 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.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
正确吗?)面板将位于选项卡中,并且在选择该选项卡时按钮应该可以工作。
最佳答案
JButton
没有selected
状态,它有一个armed
状态。为了让按钮在释放鼠标或按键时保持“按下”状态,您必须使用 JToggleButton
。
我个人会不再监视按钮状态,而是使用某种enum
或其他可以在Set中添加和删除的常量
>。这将实现状态的方式与作用于状态的过程解耦。
从那里,您可以使用单个Action
,该操作将用于通知某种观察者状态已更改
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
例如,来自按下
的值...
关于Java Swing 使用箭头按钮发送导航命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28735302/