MIDP 1.0 無內建動畫 API (MIDP 2.0 引入 Game API/LayerManager),但自行實現幀動畫並不複雜。核心原理:固定時間間隔切換預載入的連續圖片幀,利用視覺暫留形成動態效果。本文詳細講解從素材準備、核心類設計、雙重緩衝繪製到完整 MIDlet 範例。
bird0.png ~ bird6.png 便於批次載入封裝幀陣列、當前幀索引、裁切列表 (模擬透明)、Canvas 回呼、TimerTask 整合。
public class AnimatedImage extends TimerTask {
private Canvas canvas;
private Image[] frames;
private int[][] clipList; // 每幀裁切矩形陣列 [x,y,w,h...] 模擬透明
private int current = 0;
private int x, y, w, h;
// 建構子多載:支援帶/不帶 Canvas、帶/不帶 clipList
public AnimatedImage(Canvas canvas, Image[] frames, int[][] clipList) { ... }
// 推進到下一幀,選擇性觸發重繪
public void advance(boolean repaint) {
if (++current >= frames.length) current = 0;
if (repaint && canvas != null && canvas.isShown()) {
canvas.repaint(x, y, w, h);
canvas.serviceRepaints();
}
}
// 繪製當前幀:無 clipList 直接 drawImage;有則多次 setClip+drawImage 合成透明效果
public void draw(Graphics g) { ... }
// TimerTask 執行入口
public void run() { if (w>0 && h>0) advance(true); }
// 位置控制
public void move(int x, int y) { this.x = x; this.y = y; }
}
MIDP 1.0 不支援 Alpha 通道。將每幀不透明區域拆解為若干矩形 (clipList),繪製時逐一切換 Clip 區域繪製同一張圖,拼湊出非矩形輪廓。注意:過多 Clip 會降低效能,複雜形狀建議改用向量繪圖或 MIDP 2.0 透明 PNG。
class AnimatedCanvas extends Canvas {
private Vector animations = new Vector(); // AnimatedImage 集合
private Image offscreen; // 離屏緩衝
public void add(AnimatedImage ai) { animations.addElement(ai); }
protected void paint(Graphics g) {
Graphics target = g;
if (offscreen != null) target = offscreen.getGraphics(); // 導向離屏
target.setColor(0xFFFFFF);
target.fillRect(0, 0, getWidth(), getHeight());
for (int i = 0; i < animations.size(); i++) {
((AnimatedImage)animations.elementAt(i)).draw(target);
}
if (target != g) // 離屏模式:一次性貼回螢幕
g.drawImage(offscreen, 0, 0, Graphics.LEFT | Graphics.TOP);
}
}
離屏緩衝消除閃爍,批次繪製提升效能。
public class AnimationTest extends MIDlet implements CommandListener {
private static final int BIRD_FRAMES = 7;
private static final int NUM_BIRDS = 5;
private Display display;
private Timer timer = new Timer();
private AnimatedImage[] birds;
private Random random = new Random();
private Command exitCmd = new Command("Exit", Command.EXIT, 1);
protected void startApp() {
if (display == null) {
display = Display.getDisplay(this);
initMIDlet();
}
}
private void initMIDlet() {
try {
AnimatedCanvas canvas = new AnimatedCanvas(display);
Image[] frames = loadFrames("/images/bird", BIRD_FRAMES); // 載入 7 幀
int w = canvas.getWidth(), h = canvas.getHeight();
birds = new AnimatedImage[NUM_BIRDS];
for (int i = 0; i < NUM_BIRDS; i++) {
birds[i] = new AnimatedImage(canvas, frames);
birds[i].move(randomInt(w), randomInt(h));
canvas.add(birds[i]);
// 隨機啟動延遲 0~1000ms,幀率 250~650ms/幀
timer.schedule(birds[i], randomInt(1000), randomInt(400) + 200);
}
canvas.addCommand(exitCmd);
canvas.setCommandListener(this);
display.setCurrent(canvas);
} catch (IOException e) { exitMIDlet(); }
}
private Image[] loadFrames(String name, int count) throws IOException {
Image[] imgs = new Image[count];
for (int i = 0; i < count; i++)
imgs[i] = Image.createImage(name + i + ".png");
return imgs;
}
// commandAction, pauseApp, destroyApp, exitMIDlet, randomInt 略
}
pauseApp() 取消 Timer,startApp() 重建LayerManager + Sprite 獲得硬體加速與內建碰撞檢測J2ME 動畫核心三步:素材標準化 → AnimatedImage 封裝幀邏輯 → Canvas 雙重緩衝批次繪製。MIDP 1.0 下以 TimerTask 驅動幀推進,配合 ClipList 模擬透明,即可實現流暢動畫。掌握此模式,輕鬆構建遊戲角色、UI 特效、過場動畫等核心視覺元素。