gpt4 book ai didi

java - 如何使用 Key Bindings 而不是 Key Listeners

转载 作者:太空狗 更新时间:2023-10-29 22:42:01 26 4
gpt4 key购买 nike

我正在使用 KeyListener s 在我的代码(游戏或其他)中作为我的屏幕对象对用户键输入使用react的方式。这是我的代码:

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);
}
}
}

我的响应速度有问题:

  • 我需要单击该对象才能使其工作。
  • 我按下其中一个键得到的响应不是我希望它的工作方式 - 响应太快或响应太慢。

为什么会发生这种情况,我该如何解决?

最佳答案

此答案解释并演示了如何使用键绑定(bind)而不是键监听器来达到教育目的。这不是

  • 如何用 Java 编写游戏。
  • 好的代码应该是什么样子(例如可见性)。
  • 实现键绑定(bind)的最有效(性能或代码)方式。

  • 我将张贴的内容作为对与关键听众有困难的任何人的答复

回答;阅读 Swing tutorial on key bindings .

I don't want to read manuals, tell me why I would want to use key bindings instead of the beautiful code I have already!

好吧,Swing 教程 对此进行了解释

  • 键绑定(bind)不需要您单击组件(使其获得焦点):
    • 从用户的角度消除意外行为。
    • 如果您有 2 个对象,它们不能同时移动,因为在给定时间只有 1 个对象可以拥有焦点(即使您将它们绑定(bind)到不同的键)。
  • 键绑定(bind)更易于维护和操作:
    • 禁用、重新绑定(bind)、重新分配用户操作要容易得多。
    • 代码更易于阅读。

OK, you convinced me to try it out. How does it work?

tutorial 有一个很好的部分。键绑定(bind)涉及 2 个对象 InputMapActionMapInputMap 将用户输入映射到操作名称,ActionMap 将操作名称映射到Action。当用户按下一个键时,在输入映射中搜索该键并找到一个 Action 名称,然后在 Action 映射中搜索 Action 名称并执行该 Action 。

Looks cumbersome. Why not bind the user input to directly to the action and get rid of the action name? Then you need only one map and not two.

好问题!您会发现这是使键绑定(bind)更易于管理(禁用、重新绑定(bind)等)的原因之一。

I want you to give me a full working code of this.

否(Swing 教程working examples)。

You suck! I hate you!

这里是如何进行单键绑定(bind):

myComponent.getInputMap().put("userInput", "myAction");
myComponent.getActionMap().put("myAction", action);

请注意,有 3 个 InputMap 对不同的焦点状态作出 react :

myComponent.getInputMap(JComponent.WHEN_FOCUSED);
myComponent.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
myComponent.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
  • WHEN_FOCUSED,也是在没有提供参数时使用的,在组件获得焦点时使用。这类似于关键监听器案例。
  • WHEN_ANCESTOR_OF_FOCUSED_COMPONENT 在焦点组件位于注册接收操作的组件内部时使用。如果宇宙飞船内有许多船员,并且您希望宇宙飞船在任何船员处于焦点状态时继续接收输入,请使用此选项。
  • WHEN_IN_FOCUSED_WINDOW 当注册接收操作的组件位于焦点组件内时使用。如果您在聚焦窗口中有很多坦克,并且您希望所有坦克同时接收输入,请使用此选项。

假设要同时控制两个对象,问题中提供的代码看起来像这样:

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.
}
}
}

您可以看到,将输入映射与操作映射分开可以实现可重用代码和更好地控制绑定(bind)。此外,如果您需要该功能,您还可以直接控制一个 Action。例如:

FireAction p1Fire = new FireAction(1);
p1Fire.setEnabled(false); // Disable the action (for both players in this case).

参见 Action tutorial获取更多信息。

I see that you used 1 action, move, for 4 keys (directions) and 1 action, fire, for 1 key. Why not give each key its own action, or give all keys the same action and sort out what to do inside the action (like in the move case)?

好点。从技术上讲,您可以两者都做,但您必须考虑什么是有意义的,什么允许轻松管理和可重用代码。在这里,我假设所有方向的移动都是相似的,而射击是不同的,所以我选择了这种方法。

I see a lot of KeyStrokes used, what are those? Are they like a KeyEvent?

是的,它们具有类似的功能,但更适合在这里使用。查看他们的 API有关信息以及如何创建它们的信息。


有问题吗?改进?建议?发表评论。有更好的答案吗?发布它。

关于java - 如何使用 Key Bindings 而不是 Key Listeners,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22741215/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com