[Java] 利用 getInputMap、getActionMap 和 KeyStroke 從外部新增 Action

承自上一篇 javax 中 KeyListener 的遲鈍問題 解決了聽取按鍵的延遲問題,終於看懂那個 StackOverflow 的解答,試著自己把他做出來,而且更貪心地我想做成一個類別,之後都能用,就像 pygame 裡 sprite 的 update() 一樣。但是 Java 寫起來超級長而且很不自由,非常不滿意,期待之後能改良:

首先這個類別 GameObject 是繼承自 JLabel ,他是 JComponent 的子類別就有 InputMap 和 ActionMap 可以使用鍵盤功能,簡單型式(不考慮聽取鍵盤的延遲問題的話的作法)如下:

GameObject.java

package game;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;

import javax.swing.JLabel;
import javax.swing.KeyStroke;


public class GameObject extends JLabel implements ActionListener{

  Action goLeft;       // 宣告一個 Action 準備給按下左鍵時動作,內容在下面重載

  public GameObject() {

    goLeft = new LeftAction();

    this.getInputMap().put(KeyStroke.getKeyStroke("pressed LEFT"), "leftAct");
    this.getActionMap().put("leftAct", goLeft);

    // 用 "pressed Left" 字串會得到一個鍵值,並對應到 "leftAct" 字串
    // 再用 ActionMap 將 "leftAct" 對應到動作 goLeft,這是一個 AbstractAction 抽象物件
  }		
    public class LeftAction extends AbstractAction {
      @Override                                      // 在此只能做 println ,沒辦法
      public void actionPerformed(ActionEvent e) {   // 控制 GameObject 的位置
        System.out.println("Go leftward");           // 此處用 this 會是指 LeftAction
      }
    };

    @Override
    public void actionPerformed(ActionEvent e) {
      // TODO Auto-generated method stub
    };
}

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

    myLabel.setSize(80,60);
    myLabel.setBackground(Color.green);
    myLabel.setOpaque(true);
    myLabel.setLocation(100,400);  // 如同 GameObject.java內的寫法,這次從外部來使用
                                   // 補上 InputMap ActionMap 還有要動作的內容直接 new
    myLabel.getInputMap().put(KeyStroke.getKeyStroke("pressed RIGHT"), "rightAct");
    myLabel.getActionMap().put("rightAct", new AbstractAction() {

      @Override                    // 按下右鍵時會動作,將 myLabel 向右移動 2px
      public void actionPerformed(ActionEvent e) {
        myLabel.setLocation(myLabel.getX() + 2, myLabel.getY());
      }
    });
    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();
  }
}

以上會得到一個 JFrame 裡的 JLabel,按鍵盤左鍵將在 console 印出 "Go leftward",而按鍵盤右鍵會直接將 JLabel 方塊向右移動,兩者可以做一個比較。

這個作法非常單純,缺點是按一個鍵為一個動作,如果把向右和向上的動作做出來,也沒辦法兩個一起按就往右上移動,要統合的話要將這些按鍵放入一個 List 並打開 timer 來考慮

留言