這個答案解釋和演示瞭如何使用鍵綁定而不是鍵監聽器用於教育目的。它不是
- 如何用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