[Java] 用 InputMap ActionMap KeyStroke 並解決按鍵遲鈍問題
這篇等於是結合前面兩篇 利用 getInputMap、getActionMap 和 KeyStroke 從外部新增 Action 和 javax 中 KeyListener 的遲鈍問題 的結果
概念上是用 KeyStroke.getKeyStroke("pressed RIGHT") 得到按下右鍵時,讓 InputMap 對應到 "rightAct" 字串,再讓 ActionMap 對應到一個動作,而這個動作物件是在外部再描述,讓 myLabel 裡的物件成員 ArrayList<Character> pressedKey 來增減內容,當沒有 'r' 時增加 'r' 進去,而且若是第一次按鍵就啓動 timer;而 KeyStroke.getKeyStroke("released RIGHT") 放開右鍵時,對應到 "rightCancel" 字串,由 ActionMap 對應到動件,在外描描述此動作,當 pressKey 之中有 'r' 時刪去,如果完全沒有按鍵時則 timer 停止。timer 會讓 update() 不停的執行,而 update() 內容也由外部覆寫,處理目前按下了左鍵還是右鍵,讓 myLabel 更新他的位置來左右移動。
GameObject.java
package game; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import javax.swing.JLabel; import javax.swing.Timer; public class GameObject extends JLabel implements ActionListener{ private int delay; private Timer timer; private ArrayList<Character> pressedKeyList = new ArrayList<>(); public GameObject() { delay = 16; timer = new Timer(delay, this); timer.setInitialDelay(0); } public void update() { System.out.println(pressedKeyList); // 之後由外部覆寫此處內容 } public void setDelay(int delay) { this.delay = delay; } public void startTimer() { timer.start(); } public void stopTimer() { timer.stop(); } public boolean hasKey(char key) { // 串列中是否已有鍵 return pressedKeyList.contains(key); } public void addKey(char key) { // 在串列中增加 pressedKeyList.add(key); } public void delKey(char key) { // 從串列中拿掉 pressedKeyList.remove(pressedKeyList.indexOf(key)); } public boolean keyStart() { // 判斷是否初次按鍵用 return pressedKeyList.size() == 1; } public boolean keyEnd() { // 判斷是否不再有按鍵用 return pressedKeyList.size() == 0; } @Override public void actionPerformed(ActionEvent e) { update(); } }
MyFrame.java
package game; import java.awt.Color; import java.awt.event.ActionEvent; import javax.swing.AbstractAction; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.KeyStroke; public class MyFrame extends JFrame{ GameObject myLabel; JPanel myPanel; MyFrame(){ myLabel = new GameObject() { public void update() { // 覆寫要更新的部份 int deltaX = 0; // 也就是左右移動 if(this.hasKey('l')) { // 若增加上下移動的deltaY統計 deltaX -= 2; // 最後 setLocation 就可實現斜向移動 } if(this.hasKey('r')) { deltaX += 2; } this.setLocation( this.getX() + deltaX , this.getY() ); } }; myLabel.setSize(80,60); myLabel.setBackground(Color.green); myLabel.setOpaque(true); myLabel.setLocation(100,400); // 偵測到按下左鍵 myLabel.getInputMap().put(KeyStroke.getKeyStroke("pressed LEFT"), "leftAct"); myLabel.getActionMap().put("leftAct", new AbstractAction() { @Override // 則會運作此部份,由外部覆寫決定內容 public void actionPerformed(ActionEvent e) { if(!myLabel.hasKey('l')) { // 將在物件成員 pressedKey 中增加按鍵 myLabel.addKey('l'); } if(myLabel.keyStart()) { // 並判斷是否開啓計時器 myLabel.startTimer(); // 持續 update() } } }); // 偵測到放開左鍵 myLabel.getInputMap().put(KeyStroke.getKeyStroke("released LEFT"), "leftCancel"); myLabel.getActionMap().put("leftCancel", new AbstractAction() { @Override // 則會運作此部份 public void actionPerformed(ActionEvent e) { if(myLabel.hasKey('l')) { // 在 pressedKey 中拿掉按鍵 myLabel.delKey('l'); } if(myLabel.keyEnd()) { // 並判斷是否關閉計時器 myLabel.stopTimer(); // 不再 update() } } }); myLabel.getInputMap().put(KeyStroke.getKeyStroke("pressed RIGHT"), "rightAct"); myLabel.getActionMap().put("rightAct", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { if(!myLabel.hasKey('r')) { // 按下右鍵的部份,同前 myLabel.addKey('r'); } if(myLabel.keyStart()) { myLabel.startTimer(); } } }); myLabel.getInputMap().put(KeyStroke.getKeyStroke("released RIGHT"), "rightCancel"); myLabel.getActionMap().put("rightCancel", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { if(myLabel.hasKey('r')) { // 放開右鍵的部份,同前 myLabel.delKey('r'); } if(myLabel.keyEnd()) { myLabel.stopTimer(); } } }); myPanel = new JPanel(); myPanel.setLayout(null); myPanel.add(myLabel); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setSize(500,600); this.add(myPanel); this.setVisible(true); } }
Main.java
package game; public class Main { public static void main(String[] args) { new MyFrame(); } }
這樣算是有完成本來的期待,但大部份東西都在外部再覆寫,弄的很冗長很不漂亮,期待未來技術精進可以再改良。
留言
張貼留言