[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();
}
}
這樣算是有完成本來的期待,但大部份東西都在外部再覆寫,弄的很冗長很不漂亮,期待未來技術精進可以再改良。
留言
張貼留言