J2ME 規範提供 Canvas.keyRepeated(int keyCode) 回調,但大量機型(如 Nokia 6600、許多國產機、部分麼 S40 機型)不支持。用戶長按方向鍵瀏覽長文本或遊戲移動角色時無連續事件,體驗極差。本文給出通用的「按下標記 + 獨立線程輪詢」方案,在不支持原生 keyRepeated 的機型上完美模擬長按連發。
keyPressed:記錄鍵碼,設置 isKeyDown = true,記錄 timeOut = System.currentTimeMillis()。keyReleased:設置 isKeyDown = false。isKeyDown 且距上次觸發 ≥ 300ms,觸發邏輯(如 repaint()、角色移動),更新 timeOut。import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class KeyRepeatMIDlet extends MIDlet {
protected void startApp() {
Display.getDisplay(this).setCurrent(new PollingCanvas());
}
protected void pauseApp() {}
protected void destroyApp(boolean u) {}
}
class PollingCanvas extends Canvas {
int keyCode;
boolean isKeyDown;
long lastRepeatTime;
public PollingCanvas() {
new Thread(() -> {
while (true) {
try { Thread.sleep(50); } catch (InterruptedException ignored) {}
if (isKeyDown) {
long now = System.currentTimeMillis();
if (now - lastRepeatTime >= 300) { // 300ms 間隔 ≈ 3.3 次/秒
lastRepeatTime = now;
// 這裡放長按邏輯:移動、翻頁、自動射擊等
System.out.println("Simulated keyRepeated: " + keyCode);
repaint();
}
}
}
}).start();
}
protected void keyPressed(int k) {
keyCode = k; isKeyDown = true; lastRepeatTime = System.currentTimeMillis();
repaint();
}
protected void keyRepeated(int k) { // 原生支持的機型會走這裡
keyCode = k; repaint();
}
protected void keyReleased(int k) {
isKeyDown = false; keyCode = k; repaint();
}
protected void paint(Graphics g) {
g.setColor(0); g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(0xFFFFFF);
g.drawString("Key: " + keyCode + (isKeyDown ? " [HOLD]" : ""), 10, 10, 0);
}
}
| 參數 | 建議值 | 說明 |
|---|---|---|
輪詢間隔 Thread.sleep() | 50 ms | 太小佔 CPU,太大響應延遲明顯 |
| 首次觸發延遲 | 300~500 ms | 模擬鍵盤重複率,防止誤觸 |
| 後續觸發間隔 | 100~150 ms | 可在代碼中區分首次/後續,實現「加速」手感 |
若機型支持原生 keyRepeated,輪詢線程會產生雙重觸發。解決方案:
boolean nativeRepeatSupported = false;
protected void keyRepeated(int k) {
nativeRepeatSupported = true;
// 原生邏輯
}
輪詢線程中檢查 if (!nativeRepeatSupported && isKeyDown) ...,僅在無原生支持時模擬。