这个答案解释和演示了如何使用键绑定而不是键监听器用于教育目的。它不是
- 如何用Java编写游戏。
- 代码的书写效果应该如何(例如可见度)。
- 实现密钥绑定的最有效的(性能或代码方式)方法。
这是
答案;阅读Swing tutorial on key bindings。
我不想看手册,告诉我为什么我想用键绑定而不是美丽的代码我已经!
好了,Swing指南解释说,
- 键绑定不要求你点击组件(给它重点):
- 从用户的删除意外的行为观点看法。
- 如果您有2个对象,它们不能同时移动,因为只有1个对象可以在给定时间拥有焦点(即使您将它们绑定到不同的键)。
- 键绑定更易于维护和操作:
- 禁用,重新绑定,重新分配用户的操作更容易。
- 该代码更易于阅读。
OK,你说服我试试吧。它是如何工作的?
的tutorial有一个关于它良好的部分。密钥绑定涉及2个对象InputMap
和ActionMap
。 InputMap
将用户输入映射到动作名称,ActionMap
将动作名称映射到Action
。当用户按下某个键时,会在输入映射中搜索键并查找一个动作名称,然后在动作映射中搜索动作名称并执行动作。
看起来很麻烦。为什么不把用户输入直接绑定到动作并摆脱动作名称?那么你只需要一张地图而不是两张地图。
好问题!您会看到这是使键绑定更易于管理(禁用,重新绑定等)的一件事情。
我想让你给我一个完整的工作代码。
否(在Swing指南有working examples)。
你吮吸!我恨你!
下面是如何使一个按键绑定:
myComponent.getInputMap().put("userInput", "myAction");
myComponent.getActionMap().put("myAction", action);
注意,有3个InputMap
小号反应,不同的聚焦状态:
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.
}
}
}
你可以看到,从动作图分离输入映射允许重复使用代码和更好的绑定控制。另外,如果您需要该功能,还可以直接控制Action。例如:
FireAction p1Fire = new FireAction(1);
p1Fire.setEnabled(false); // Disable the action (for both players in this case).
查看Action tutorial了解更多信息。
我看到你用1分的动作,移动,4个键(方向)和1个动作,火,1个键。为什么不给每个关键点自己的行为,或给所有关键点采取相同的行动,并理清行动内部要做什么(就像在移动的情况下)?
好点。从技术上讲,你可以同时做这两件事,但你必须考虑什么是有意义的,什么允许简单的管理和可重用的代码。在这里,我假设所有方向的移动都是相似的,而且射击是不同的,所以我选择了这种方法。
我看到很多KeyStroke
的使用,那些是什么?他们喜欢KeyEvent
?
是的,他们有一个类似的功能,但更适合在这里使用。有关信息和如何创建它们,请参阅他们的API。
有问题?改进?建议?发表评论。 有更好的回答?发表它。
_“改进?”_ - 我会使用常量,而不是硬编码的字符串值。 –
@peeskillet谢谢,将操作名称更改为常量。为了清楚起见,我留下了关键笔划字符串。 – user1803551
[例如](http://stackoverflow.com/a/7940227/714968),使用的JPanel用正确的焦点(不知道你能指望什么),良柱(一束)的键绑定解释(原本应该是通过的KeyListener)由@Hovercraft全部鳗鱼标记,他的抽象的主人在这里与约 – mKorbel