部分手機不支持原生按鍵重複事件 (keyRepeated 不觸發)。需自行實現「按下 → 延遲 → 以固定間隔重複觸發」邏輯。
public class KeyRepeater implements Runnable {
private final Canvas canvas;
private final byte[] pressedKeys = new byte[2]; // 支持最多 2 鍵同時長按
private byte idx = 0;
private volatile boolean running = true;
public KeyRepeater(Canvas canvas) { this.canvas = canvas; }
// keyPressed 註冊按下的鍵
public synchronized void keyPressed(int keyCode) {
pressedKeys[idx] = (byte) keyCode;
idx = (byte) ((idx + 1) % pressedKeys.length);
}
// keyReleased 移除釋放的鍵
public synchronized void keyReleased(int keyCode) {
for (int i = 0; i < pressedKeys.length; i++) {
if (pressedKeys[i] == keyCode) {
pressedKeys[i] = 0;
break;
}
}
}
// 處理線程主循環
public void run() {
while (running) {
byte[] keys = getActiveKeys(); // 獲取當前按下的鍵碼快照
for (int keyCode : keys) {
if (keyCode != 0) {
int action = canvas.getGameAction(keyCode);
// 分發邏輯動作 (示例:移動像素點)
switch (action) {
case Canvas.UP: canvas.py--; break;
case Canvas.DOWN: canvas.py++; break;
case Canvas.LEFT: canvas.px--; break;
case Canvas.RIGHT: canvas.px++; break;
}
}
}
if (hasActiveKey(keys)) canvas.repaint();
sleep(100); // 重複間隔 100ms,可配置
}
}
private synchronized byte[] getActiveKeys() {
return pressedKeys.clone();
}
private boolean hasActiveKey(byte[] keys) {
for (byte k : keys) if (k != 0) return true;
return false;
}
private void sleep(long ms) {
try { Thread.sleep(ms); } catch (InterruptedException ignored) {}
}
public void stop() { running = false; }
}
public class GameCanvas extends Canvas {
public int px = 50, py = 50;
private KeyRepeater repeater;
public GameCanvas() {
repeater = new KeyRepeater(this);
new Thread(repeater).start();
}
protected void keyPressed(int keyCode) {
repeater.keyPressed(keyCode);
}
protected void keyReleased(int keyCode) {
repeater.keyReleased(keyCode);
}
protected void paint(Graphics g) {
g.setColor(0xFFFFFF); g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(0xFF0000); g.fillRect(px, py, 8, 8); // 繪製移動方塊
}
}
keyPressed/Released) 僅維護「哪些鍵被按下」集合,不包含邏輯。run() 循環定期檢查集合,將物理鍵碼轉邏輯動作 (getGameAction) 並執行遊戲邏輯。synchronized,避免併發修改異常。sleep(100) 控制重複頻率,可根據遊戲類型調整 (動作遊戲 50-100ms,策略遊戲 200-500ms)。firstDelay 狀態機。destroyApp 中調用 repeater.stop() 優雅關閉線程。通過「事件註冊狀態 + 獨立線程輪詢分發」模式,可在不支持原生重複事件的設備上完美模擬按鍵長按重複,代碼結構清晰、擴展性強,適合各類即時性遊戲。