MIDP 1.0 無原生動畫 API,但透過 Canvas、Timer、Image 組合可輕鬆實現幀動畫。本文深度解析一個通用的 AnimatedImage 動畫引擎類,支援多幀循環、透明色模擬、位置控制、定時器驅動,為 J2ME 遊戲/UI 動畫提供生產級基礎組件。
TimerTask 定期推進幀索引Canvas.paint() 中繪製當前幀import java.util.*;
import javax.microedition.lcdui.*;
public class AnimatedImage extends TimerTask {
private Canvas canvas; // 繪製目標畫布
private Image[] frames; // 幀圖片陣列
private int[][] clipRects; // 每幀的透明裁剪矩陣 [x, y, w, h]...
private int currentFrame = 0; // 當前幀索引
private int x, y; // 繪製位置
private int frameWidth, frameHeight; // 幀尺寸
// === 構造器重載 ===
public AnimatedImage(Image[] frames) { this(null, frames, null); }
public AnimatedImage(Canvas canvas, Image[] frames) { this(canvas, frames, null); }
public AnimatedImage(Canvas canvas, Image[] frames, int[][] clipRects) {
this.canvas = canvas;
this.frames = frames;
this.clipRects = clipRects;
if (frames != null && clipRects != null && clipRects.length < frames.length)
throw new IllegalArgumentException("clipRects length < frames length");
if (frames != null && frames.length > 0) {
frameWidth = frames[0].getWidth();
frameHeight = frames[0].getHeight();
}
}
// === 核心動畫控制 ===
public void advance(boolean repaint) {
if (++currentFrame >= frames.length) currentFrame = 0; // 循環
if (repaint && canvas != null && canvas.isShown()) {
canvas.repaint(x, y, frameWidth, frameHeight);
canvas.serviceRepaints(); // 立即刷新
}
}
// TimerTask 介面:定時器自動呼叫
public void run() {
if (frameWidth == 0 || frameHeight == 0) return;
advance(true);
}
// === 繪製邏輯:支援透明色模擬 ===
public void draw(Graphics g) {
if (frameWidth == 0 || frameHeight == 0) return;
int idx = currentFrame;
if (clipRects == null || clipRects[idx] == null) {
// 簡單模式:直接繪製整幀
g.drawImage(frames[idx], x, y, Graphics.TOP | Graphics.LEFT);
} else {
// 透明模擬:保存原始裁剪區,分矩形繪製
int oldX = g.getClipX(), oldY = g.getClipY();
int oldW = g.getClipWidth(), oldH = g.getClipHeight();
int[] rects = clipRects[idx];
for (int i = 0; i + 3 < rects.length; i += 4) {
g.setClip(x + rects[i], y + rects[i+1], rects[i+2], rects[i+3]);
g.drawImage(frames[idx], x, y, Graphics.TOP | Graphics.LEFT);
}
g.setClip(oldX, oldY, oldW, oldH); // 還原
}
}
// === 輔助方法 ===
public void move(int x, int y) { this.x = x; this.y = y; }
public int getWidth() { return frameWidth; }
public int getHeight() { return frameHeight; }
}
private Image[] loadFrames(String prefix, int count) throws IOException {
Image[] imgs = new Image[count];
for (int i = 0; i < count; i++) {
// 檔案命名:walk0.png, walk1.png... 放在 JAR 根目錄或 /res/
imgs[i] = Image.createImage("/" + prefix + i + ".png");
}
return imgs;
}
public class GameCanvas extends Canvas {
private AnimatedImage playerAnim;
private Timer timer;
public GameCanvas() {
try {
Image[] frames = loadFrames("walk", 8); // 8 幀走路動畫
// 定義透明區域:每幀 4 個矩形 (頭、身、左腳、右腳)
int[][] clips = new int[8][16]; // 8幀 x 4矩形 x 4座標
// ... 初始化 clips 數據 ...
playerAnim = new AnimatedImage(this, frames, clips);
playerAnim.move(50, 50);
} catch (IOException e) { /* 處理資源缺失 */ }
timer = new Timer();
timer.schedule(playerAnim, 0, 100); // 100ms/幀 = 10 FPS
}
protected void paint(Graphics g) {
g.setColor(0xFFFFFF); g.fillRect(0, 0, getWidth(), getHeight()); // 清屏
if (playerAnim != null) playerAnim.draw(g);
}
}
| 決策點 | 方案 | 理由 |
|---|---|---|
| 繼承 TimerTask | 直接實作 run() 推進幀 | 無需額外線程類,Timer 統一調度,資源佔用極小 |
| clipRects 透明模擬 | 矩形陣列多次繪製 | MIDP 1.0 無 Alpha 支援,此法相容性最好,無需外部庫 |
| canvas.serviceRepaints() | 立即刷新 | 避免繪製請求排隊延遲,保證幀率穩定 |
| isShown() 檢查 | 僅可見時重繪 | 後台/最小化時節省 CPU/電量 |
| 不可變幀尺寸 | 構造時檢查所有幀同尺寸 | 簡化繪製邏輯,避免運行時計算 |
timer.schedule(task, 0, 80-100)loadFrames() 僅在初始化調用一次timer.cancel()、frames = null、System.gc()Canvas 預設雙緩衝,無需手動管理setClip 呼叫MIDP 2.0 引入 GameCanvas、Sprite、LayerManager 遊戲 API:
// MIDP 2.0 方式
Sprite sprite = new Sprite(Image.createImage("/walk.png"), frameWidth, frameHeight);
sprite.defineReferencePixel(frameWidth/2, frameHeight/2);
sprite.setPosition(50, 50);
// 遊戲迴圈中
sprite.nextFrame(); // 自動循環
sprite.paint(g);
遷移建議:新專案優先用 MIDP 2.0 Game API;存量 MIDP 1.0 代碼可封裝 AnimatedImage 平滑過渡。
AnimatedImage 展示了在極度受限環境下,如何用 TimerTask + Canvas + 裁剪繪製構建通用動畫引擎。其設計精髓——職責單一、資源復用、平台適配、效能覺知——在現代 Canvas 2D/WebGL 輕量動畫庫、WebAssembly 遊戲引擎、嵌入式 GUI 框架 中依然閃耀著工程智慧的光芒。