早期手機不支援 keyRepeated 重複按鍵事件,更無原生組合鍵偵測。本文展示如何以單一背景執行緒維護「目前被按下鍵碼集合」,實現組合鍵(同時按下多鍵)與長按重複功能。
byte[] pressedKeys = new byte[2] 環狀緩衝區,儲存目前按下的鍵碼(最多 2 鍵,足應付手機操作)pressedKeys 的方法加 synchronized,確保執行緒安全keyPressed → startAction(keyCode) 寫入緩衝區keyReleased → stopAction(keyCode) 從緩衝區移除run() 持續輪詢緩衝區,對每個被按下的鍵觸發遊戲邏輯
import javax.microedition.lcdui.*;
public class KeyEventManager implements Runnable {
private Canvas canvas; // 回呼目標
private byte[] pressedKeys = new byte[2]; // 環狀緩衝區
private int writeIndex = 0; // 寫入指標
private boolean hasPressedKey = false; // 快速檢查旗標
public KeyEventManager(Canvas canvas) {
this.canvas = canvas;
new Thread(this).start();
}
// 鍵按下:寫入環狀緩衝區
public synchronized void startAction(int keyCode) {
pressedKeys[writeIndex] = (byte) keyCode;
writeIndex = (writeIndex + 1) % pressedKeys.length;
hasPressedKey = true;
}
// 鍵釋放:從緩衝區移除
public synchronized void stopAction(int keyCode) {
for (int i = 0; i < pressedKeys.length; i++) {
if (pressedKeys[i] == keyCode) {
pressedKeys[i] = 0;
break;
}
}
// 更新旗標
hasPressedKey = false;
for (byte k : pressedKeys) if (k != 0) { hasPressedKey = true; break; }
}
// 檢查是否有按鍵被按住
public synchronized boolean hasPressedKeys() {
return hasPressedKey;
}
// 取得目前按鍵快照(供執行緒讀取)
public synchronized byte[] getPressedKeysSnapshot() {
return pressedKeys.clone();
}
// 背景執行緒主迴路
public void run() {
while (true) {
if (hasPressedKeys()) {
byte[] keys = getPressedKeysSnapshot();
for (byte keyCode : keys) {
if (keyCode != 0) {
handleGameAction(keyCode);
}
}
canvas.repaint();
}
try { Thread.sleep(10); } catch (InterruptedException e) {}
}
}
// 實際遊戲邏輯處理
private void handleGameAction(int keyCode) {
int action = canvas.getGameAction(keyCode);
// 假設 Canvas 有 x, y 座標屬性
switch (action) {
case Canvas.UP: canvas.y--; break;
case Canvas.DOWN: canvas.y++; break;
case Canvas.LEFT: canvas.x--; break;
case Canvas.RIGHT: canvas.x++; break;
case Canvas.FIRE: /* 射擊/確認 */ break;
}
}
}
public class GameCanvas extends Canvas {
public int x = 50, y = 50; // 角色座標
private KeyEventManager keyManager;
public GameCanvas() {
keyManager = new KeyEventManager(this);
}
protected void keyPressed(int keyCode) {
keyManager.startAction(keyCode);
}
protected void keyReleased(int keyCode) {
keyManager.stopAction(keyCode);
}
protected void paint(Graphics g) {
g.setColor(0xFFFFFF);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(0xFF0000);
g.fillRect(x, y, 8, 8); // 繪製角色方塊
}
}
// 在 handleGameAction 中檢測組合鍵
private void handleGameAction(int keyCode) {
byte[] keys = getPressedKeysSnapshot();
boolean up = false, left = false, fire = false;
for (byte k : keys) {
int a = canvas.getGameAction(k);
if (a == Canvas.UP) up = true;
else if (a == Canvas.LEFT) left = true;
else if (a == Canvas.FIRE) fire = true;
}
if (up && left) { /* 左上斜向移動 */ }
if (fire && up) { /* 跳躍射擊 */ }
// ... 其他組合
}
| 優點 | 缺點/限制 |
|---|---|
| 無需平台支援 keyRepeated | 手機鍵盤緊湊,三鍵以上極難操作 |
| 同時實現長按重複與組合鍵 | 輪詢間隔 (sleep 10ms) 影響回應速度 |
| 邏輯集中於單一執行緒,易維護 | 需妥善處理 synchronized 避免死鎖 |
| 跨裝置相容性極佳 (MIDP 1.0+) | 環狀緩衝區固定長度限制同時鍵數 |
Vector 儲存按鍵,支援任意數量同時鍵keyRepeated,優先使用原生事件這個模式在 MIDP 1.0 時代被廣泛採用,雖然現代裝置多支援原生重複事件,但其組合鍵偵測邏輯在觸控不普及的鍵盤手機上仍具實用價值。