繼上篇高級事件,本文深入低級事件核心——Canvas,覆蓋鍵盤三態、游戲鍵映射、觸控筆三事件、能力檢測、繪圖基礎,並給出完整可運行的測試 MIDlet。
protected void keyPressed(int keyCode) // 按下瞬間,邏輯判斷多放這裡
protected void keyReleased(int keyCode) // 釋放瞬間,停止移動、觸發一次性動作
protected void keyRepeated(int keyCode) // 長按重複 (需 hasRepeatEvents()==true)
關鍵:keyCode 為設備物理鍵碼,**不可硬編碼邏輯**。必須用 getGameAction(keyCode) 轉換為邏輯方向:
int action = getGameAction(keyCode);
switch (action) {
case Canvas.LEFT: moveLeft(); break;
case Canvas.RIGHT: moveRight(); break;
case Canvas.UP: moveUp(); break;
case Canvas.DOWN: moveDown(); break;
case Canvas.FIRE: fire(); break;
}
| 類別 | 常量 | 說明 |
|---|---|---|
| 標準鍵 (所有設備必支持) | KEY_NUM0 ~ KEY_NUM9 | 數字鍵 0-9 |
| KEY_STAR, KEY_POUND | * 鍵、# 鍵 | |
| 游戲鍵 (邏輯動作) | UP, DOWN, LEFT, RIGHT, FIRE | 方向鍵、確認鍵 |
| GAME_A, GAME_B, GAME_C, GAME_D | 輔助功能鍵 (對應軟鍵/側鍵等) |
轉換雙向 API:getGameAction(int keyCode)、getKeyCode(int gameAction)。
| 方法 | 觸發 | 參數 | 能力檢測 |
|---|---|---|---|
pointerPressed(int x, int y) | 筆尖觸屏 | 屏幕座標 (0,0 為左上角) | hasPointerEvents() |
pointerReleased(int x, int y) | 筆尖離屏 | 座標 | hasPointerEvents() |
pointerDragged(int x, int y) | 筆在屏幕移動 | 座標 | hasPointerMotionEvents() |
座標系:像素座標,原點 (0,0) 在可繪製區域左上角 (不含標題欄/命令欄)。
public class KeyCanvasTest extends MIDlet {
private Display display;
protected void startApp() {
display = Display.getDisplay(this);
display.setCurrent(new TestCanvas());
}
protected void pauseApp() {}
protected void destroyApp(boolean u) {}
class TestCanvas extends Canvas {
String info = "Press keys / touch screen";
boolean repeatSupported = hasRepeatEvents();
boolean pointerSupported = hasPointerEvents();
boolean pointerMotionSupported = hasPointerMotionEvents();
protected void paint(Graphics g) {
g.setColor(0xFFFFFF); g.fillRect(0,0,getWidth(),getHeight());
g.setColor(0);
g.drawString(info, 5, 5, 0);
g.drawString("Repeat: " + repeatSupported, 5, 20, 0);
g.drawString("Pointer: " + pointerSupported, 5, 35, 0);
g.drawString("PtrMotion: " + pointerMotionSupported, 5, 50, 0);
}
protected void keyPressed(int kc) {
info = "KP: " + kc + " -> Action: " + getGameAction(kc);
repaint();
}
protected void keyReleased(int kc) {
info = "KR: " + kc;
repaint();
}
protected void keyRepeated(int kc) {
info = "KRe: " + kc;
repaint();
}
protected void pointerPressed(int x, int y) {
info = "PtrPress: (" + x + "," + y + ")";
repaint();
}
protected void pointerReleased(int x, int y) {
info = "PtrRel: (" + x + "," + y + ")";
repaint();
}
protected void pointerDragged(int x, int y) {
info = "PtrDrag: (" + x + "," + y + ")";
repaint();
}
}
}
g.setColor(RGB) / setColor(r,g,b) / setGrayScale(v)。drawLine/fillRect/drawRect/drawArc/fillArc。drawString(str,x,y,anchor)、drawChars/charsWidth、Font.getHeight()、stringWidth(str)。drawImage(img,x,y,anchor)、drawRegion(src,x_src,y_src,w,h,transform,x_dst,y_dst,anchor) (transform: ROTATE_90/180/270, MIRROR_ROT...)。setClip(x,y,w,h)、clipRect 限制繪製範圍,提升性能。| 坑 | 現象 | 修正 |
|---|---|---|
| 硬編碼鍵碼 | 換設備方向鍵失效 | 統一用 getGameAction() |
| 不檢測能力直接用 pointerDragged | 無觸控筆設備拋異常/不調用 | if (hasPointerMotionEvents()) { ... } |
| paint() 中做邏輯/睡眠 | 界面卡死、閃爍、幀率崩潰 | paint 只渲染,邏輯放線程/Timer |
| 頻繁創建 Image | Heap 抖動、OOM | 啟動時預載、對象池復用 |
| 忽略 anchor 參數 | 文本/圖片定位錯位 | 掌握 TOP/BOTTOM/LEFT/RIGHT/HCENTER/VCENTER 組合 |
Canvas 給予了像素級控制權,也帶來了「大權在手、責任在肩」的複雜度。掌握 鍵盤三態 → 邏輯映射 → 能力檢測 → 雙緩衝渲染 → 釋放資源 這條主線,就能在 J2ME 上寫出流暢、可移植的遊戲與自定義組件。