Java 如何使用键绑定而不是键侦听器
我在代码(游戏或其他)中使用s作为屏幕对象对用户键输入作出反应的方式。这是我的密码:Java 如何使用键绑定而不是键侦听器,java,swing,key-bindings,keyevent,key-events,Java,Swing,Key Bindings,Keyevent,Key Events,我在代码(游戏或其他)中使用s作为屏幕对象对用户键输入作出反应的方式。这是我的密码: public class MyGame extends JFrame { static int up = KeyEvent.VK_UP; static int right = KeyEvent.VK_RIGHT; static int down = KeyEvent.VK_DOWN; static int left = KeyEvent.VK_LEFT; static
public class MyGame extends JFrame {
static int up = KeyEvent.VK_UP;
static int right = KeyEvent.VK_RIGHT;
static int down = KeyEvent.VK_DOWN;
static int left = KeyEvent.VK_LEFT;
static int fire = KeyEvent.VK_Q;
public MyGame() {
// Do all the layout management and what not...
JLabel obj1 = new JLabel();
JLabel obj2 = new JLabel();
obj1.addKeyListener(new MyKeyListener());
obj2.addKeyListener(new MyKeyListener());
add(obj1);
add(obj2);
// Do other GUI things...
}
static void move(int direction, Object source) {
// do something
}
static void fire(Object source) {
// do something
}
static void rebindKey(int newKey, String oldKey) {
// Depends on your GUI implementation.
// Detecting the new key by a KeyListener is the way to go this time.
if (oldKey.equals("up"))
up = newKey;
if (oldKey.equals("down"))
down = newKey;
// ...
}
public static void main(String[] args) {
new MyGame();
}
private static class MyKeyListener extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
Object source = e.getSource();
int action = e.getExtendedKeyCode();
/* Will not work if you want to allow rebinding keys since case variables must be constants.
switch (action) {
case up:
move(1, source);
case right:
move(2, source);
case down:
move(3, source);
case left:
move(4, source);
case fire:
fire(source);
...
}
*/
if (action == up)
move(1, source);
else if (action == right)
move(2, source);
else if (action == down)
move(3, source);
else if (action == left)
move(4, source);
else if (action == fire)
fire(source);
}
}
}
我的反应能力有问题:
- 我需要点击这个对象,它才能工作
- 我按下其中一个键时得到的反应不是我想要它如何工作——反应太快或反应太迟钝
- 如何用Java编写游戏
- 代码编写的好坏(例如可见性)
- 实现键绑定的最有效(性能或代码方面)的方法
- 我想把这篇文章作为对那些在关键听众方面有困难的人的回答
答复;读这本书 我不想阅读手册,告诉我为什么我想使用键绑定而不是我已经拥有的漂亮代码强> Swing教程解释了这一点
- 键绑定不要求您单击组件(以使其具有焦点):
- 从用户的角度删除意外行为
- 如果有两个对象,它们不能同时移动,因为在给定时间只有一个对象可以具有焦点(即使将它们绑定到不同的关键点)
- 键绑定更易于维护和操作:
- 禁用、重新绑定和重新分配用户操作要容易得多
- 代码更容易阅读
InputMap
和ActionMap
InputMap
将用户输入映射到动作名称,ActionMap
将动作名称映射到动作。当用户按下一个键时,将在输入映射中搜索该键并找到一个动作名称,然后在动作映射中搜索动作名称并执行该动作
看起来很笨重。为什么不直接将用户输入绑定到操作并去掉操作名呢?那么您只需要一张地图,而不需要两张。
好问题!您将看到,这是使键绑定更易于管理(禁用、重新绑定等)的因素之一
我想让你给我一个完整的工作代码。
否(Swing教程中有)
你真烂!我恨你强>
以下是如何进行单键绑定:
myComponent.getInputMap().put("userInput", "myAction");
myComponent.getActionMap().put("myAction", action);
请注意,有3个InputMap
s对不同的焦点状态作出反应:
myComponent.getInputMap(JComponent.WHEN_FOCUSED);
myComponent.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
myComponent.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
,也就是在没有提供参数时使用的,在组件具有焦点时使用。这与密钥侦听器的情况类似WHEN_FOCUSED
。如果你在一艘宇宙飞船里有许多宇航员,并且你想让飞船在任何一名宇航员都有焦点的情况下继续接收输入,那么使用这个当聚焦组件位于注册以接收操作的组件内时,使用聚焦组件的祖先时
- 当注册接收动作的组件位于聚焦组件内部时,使用聚焦窗口中的
时。如果在一个聚焦窗口中有许多坦克,并且希望所有坦克同时接收输入,请使用此选项
public class MyGame extends JFrame {
private static final int IFW = JComponent.WHEN_IN_FOCUSED_WINDOW;
private static final String MOVE_UP = "move up";
private static final String MOVE_DOWN = "move down";
private static final String FIRE = "move fire";
static JLabel obj1 = new JLabel();
static JLabel obj2 = new JLabel();
public MyGame() {
// Do all the layout management and what not...
obj1.getInputMap(IFW).put(KeyStroke.getKeyStroke("UP"), MOVE_UP);
obj1.getInputMap(IFW).put(KeyStroke.getKeyStroke("DOWN"), MOVE_DOWN);
// ...
obj1.getInputMap(IFW).put(KeyStroke.getKeyStroke("control CONTROL"), FIRE);
obj2.getInputMap(IFW).put(KeyStroke.getKeyStroke("W"), MOVE_UP);
obj2.getInputMap(IFW).put(KeyStroke.getKeyStroke("S"), MOVE_DOWN);
// ...
obj2.getInputMap(IFW).put(KeyStroke.getKeyStroke("T"), FIRE);
obj1.getActionMap().put(MOVE_UP, new MoveAction(1, 1));
obj1.getActionMap().put(MOVE_DOWN, new MoveAction(2, 1));
// ...
obj1.getActionMap().put(FIRE, new FireAction(1));
obj2.getActionMap().put(MOVE_UP, new MoveAction(1, 2));
obj2.getActionMap().put(MOVE_DOWN, new MoveAction(2, 2));
// ...
obj2.getActionMap().put(FIRE, new FireAction(2));
// In practice you would probably create your own objects instead of the JLabels.
// Then you can create a convenience method obj.inputMapPut(String ks, String a)
// equivalent to obj.getInputMap(IFW).put(KeyStroke.getKeyStroke(ks), a);
// and something similar for the action map.
add(obj1);
add(obj2);
// Do other GUI things...
}
static void rebindKey(KeyEvent ke, String oldKey) {
// Depends on your GUI implementation.
// Detecting the new key by a KeyListener is the way to go this time.
obj1.getInputMap(IFW).remove(KeyStroke.getKeyStroke(oldKey));
// Removing can also be done by assigning the action name "none".
obj1.getInputMap(IFW).put(KeyStroke.getKeyStrokeForEvent(ke),
obj1.getInputMap(IFW).get(KeyStroke.getKeyStroke(oldKey)));
// You can drop the remove action if you want a secondary key for the action.
}
public static void main(String[] args) {
new MyGame();
}
private class MoveAction extends AbstractAction {
int direction;
int player;
MoveAction(int direction, int player) {
this.direction = direction;
this.player = player;
}
@Override
public void actionPerformed(ActionEvent e) {
// Same as the move method in the question code.
// Player can be detected by e.getSource() instead and call its own move method.
}
}
private class FireAction extends AbstractAction {
int player;
FireAction(int player) {
this.player = player;
}
@Override
public void actionPerformed(ActionEvent e) {
// Same as the fire method in the question code.
// Player can be detected by e.getSource() instead, and call its own fire method.
// If so then remove the constructor.
}
}
}
您可以看到,将输入映射与操作映射分离可以实现可重用代码和更好的绑定控制。此外,如果需要该功能,还可以直接控制操作。例如:
FireAction p1Fire = new FireAction(1);
p1Fire.setEnabled(false); // Disable the action (for both players in this case).
有关更多信息,请参阅
我看到您使用了1个动作,移动,4个键(方向),1个动作,开火,1个键。为什么不让每个键都有自己的动作,或者让所有键都有相同的动作,并在动作中找出要做的事情(如移动案例)?
说得好。从技术上讲,您可以同时做到这两个方面,但您必须考虑什么是有意义的,以及什么允许轻松管理和可重用代码。在这里,我假设所有方向的移动都是相似的,而射击是不同的,所以我选择了这种方法
我看到很多人用按键,这些是什么?它们是否像一个KeyEvent
?
是的,它们有类似的功能,但更适合在这里使用。有关信息以及如何创建它们的信息,请参见它们
有问题吗?改进?建议?留下评论。 有更好的答案吗?发帖。此答案解释并演示了如何使用键绑定而不是键侦听器进行教育。事实并非如此
- 如何用Java编写游戏
- 代码编写的好坏(例如可见性)
- 实现键绑定的最有效(性能或代码方面)的方法
- 我想把这篇文章作为对那些在关键听众方面有困难的人的回答
答复;读这本书 我不想阅读手册,告诉我为什么我想使用键绑定而不是我已经拥有的漂亮代码强> Swing教程解释了这一点
- 键绑定不要求您单击组件(以使其具有焦点):
- 从用户的角度删除意外行为
- 如果有两个对象,它们不能同时移动,因为在给定时间只有一个对象可以具有焦点(即使将它们绑定到不同的关键点)
- 键绑定更易于维护和操作:
- 禁用、重新绑定和重新分配用户操作要容易得多
- 代码更容易阅读
modifiers := shift | control | ctrl | meta | alt | altGraph
typedID := typed <typedKey>
typedKey := string of length 1 giving Unicode character.
pressedReleasedID := (pressed | released) key
key := KeyEvent key code name, i.e. the name following "VK_".
KeyStroke control = getKeyStroke("CONTROL");
KeyStroke control = getKeyStroke("ctrl CONTROL");
private static JLabel listener= new JLabel();
add(listener);
private void setKeyBinding(String keyString, AbstractAction action) {
listener.getInputMap().put(KeyStroke.getKeyStroke(keyString), keyString);
listener.getActionMap().put(keyString, action);
}
private void setKeyBinding(int keyCode, AbstractAction action) {
int modifier = 0;
switch (keyCode) {
case KeyEvent.VK_CONTROL:
modifier = InputEvent.CTRL_DOWN_MASK;
break;
case KeyEvent.VK_SHIFT:
modifier = InputEvent.SHIFT_DOWN_MASK;
break;
case KeyEvent.VK_ALT:
modifier = InputEvent.ALT_DOWN_MASK;
break;
}
listener.getInputMap().put(KeyStroke.getKeyStroke(keyCode, modifier), keyCode);
listener.getActionMap().put(keyCode, action);
}
setKeyBinding(KeyEvent.VK_CONTROL, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("ctrl pressed");
}
});
case KeyEvent.VK_ALT_GRAPH:
modifier = InputEvent.ALT_GRAPH_DOWN_MASK;
break;
// Create key bindings for controls
private void createKeyBindings(JPanel p) {
InputMap im = p.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
ActionMap am = p.getActionMap();
im.put(KeyStroke.getKeyStroke("W"), MoveAction.Action.MOVE_UP);
im.put(KeyStroke.getKeyStroke("S"), MoveAction.Action.MOVE_DOWN);
im.put(KeyStroke.getKeyStroke("A"), MoveAction.Action.MOVE_LEFT);
im.put(KeyStroke.getKeyStroke("D"), MoveAction.Action.MOVE_RIGHT);
am.put(MoveAction.Action.MOVE_UP, new MoveAction(this, MoveAction.Action.MOVE_UP));
am.put(MoveAction.Action.MOVE_DOWN, new MoveAction(this, MoveAction.Action.MOVE_DOWN));
am.put(MoveAction.Action.MOVE_LEFT, new MoveAction(this, MoveAction.Action.MOVE_LEFT));
am.put(MoveAction.Action.MOVE_RIGHT, new MoveAction(this, MoveAction.Action.MOVE_RIGHT));
}
// Handles the key bindings
class MoveAction extends AbstractAction {
enum Action {
MOVE_UP, MOVE_DOWN, MOVE_LEFT, MOVE_RIGHT;
}
private static final long serialVersionUID = /* Some ID */;
Window window;
Action action;
public MoveAction(Window window, Action action) {
this.window = window;
this.action = action;
}
@Override
public void actionPerformed(ActionEvent e) {
switch (action) {
case MOVE_UP:
/* ... */
break;
case MOVE_DOWN:
/* ... */
break;
case MOVE_LEFT:
/* ... */
break;
case MOVE_RIGHT:
/* ... */
break;
}
}
}