在 Canvas.paint(Graphics g) 中直接繪製複雜圖形 (多層矩形、文字、圖片疊加) 時,螢幕會逐步顯示繪製過程,造成肉眼可見的閃爍。雙緩衝核心思想:
Image (大小等於螢幕)。Image 的 Graphics。drawImage(offscreen, 0, 0) 複製到螢幕 Graphics。因螢幕只經歷「空白 → 完整畫面」單一更新,閃爍消失。
現代手機 (Nokia S40/S60, Sony Ericsson JP-8 等) 多數硬體/驅動層已實作雙緩衝。優先檢測:
if (isDoubleBuffered()) {
// 直接在 paint(g) 繪製,系統自動處理離屏合成
} else {
// 需自行實作軟體雙緩衝
}
public class DoubleBufferCanvas extends Canvas implements CommandListener {
private Image offscreen; // 離屏緩衝區
private int width, height;
private Command exitCmd = new Command("退出", Command.EXIT, 1);
public DoubleBufferCanvas() {
width = getWidth();
height = getHeight();
setCommandListener(this);
addCommand(exitCmd);
// 僅在系統不支援時建立離屏緩衝區
if (!isDoubleBuffered()) {
offscreen = Image.createImage(width, height);
}
}
protected void paint(Graphics g) {
// 1. 取得實際繪圖目標
Graphics target = g;
if (offscreen != null) {
target = offscreen.getGraphics();
}
// 2. 在目標上進行所有繪圖 (複雜邏輯放這裡)
drawComplexScene(target);
// 3. 若使用離屏緩衝,一次性複製到螢幕
if (offscreen != null) {
g.drawImage(offscreen, 0, 0, Graphics.TOP | Graphics.LEFT);
}
}
private void drawComplexScene(Graphics g) {
// 範例:繪製巢狀矩形 + 狀態文字
g.setColor(0xFF8080); // 淺紅
for (int i = 2, j = 2; i < width/2 - 6 && j < height/2 - 6; i += 2, j += 2) {
g.drawRect(i, j, width - 2*i, height - 2*j);
}
g.setColor(0x000000);
g.drawString("DoubleBuffered: " + isDoubleBuffered(), width/2, height/2, Graphics.HCENTER | Graphics.TOP);
}
public void commandAction(Command c, Displayable d) {
if (c == exitCmd) {
// 通知 MIDlet 退出...
}
}
}
以下程式碼可在不支援硬體雙緩衝的舊機型 (如 Nokia 6100, Siemens S55) 上直觀觀察差異:
DoubleBufferMIDlet (MIDlet)
└─ UIController (導航控制器)
├─ MainListUI (選單:非雙緩衝 / 雙緩衝)
├─ NonDoubleCanvas (無離屏緩衝)
└─ DoubleCanvas (含離屏緩衝)
// NonDoubleCanvas: 直接在螢幕 Graphics 繪製
public class NonDoubleCanvas extends Canvas implements CommandListener {
private UIController ctrl;
public NonDoubleCanvas(UIController ctrl) { this.ctrl = ctrl; addCommand(UIController.BACK_CMD); setCommandListener(this); }
protected void paint(Graphics g) {
g.setColor(0x646464);
for (int i=2,j=2; i
| 指標 | 無雙緩衝 | 軟體雙緩衝 | 硬體雙緩衝 |
|---|---|---|---|
| 閃爍 | 明顯 | 無 | 無 |
| 額外 Heap | 0 | W × H × 2 bytes (如 240×320 ≈ 150KB) | 0 (驅動層管理) |
| CPU 開銷 | 基準 | +1 次全螢幕 blit | 極低 (硬體加速) |
| 適用場景 | 簡單 UI、靜態表單 | 遊戲、動畫、複雜圖形、舊機型 | 現代機型預設 |
isDoubleBuffered(),僅在 false 時配置 offscreen。offscreen:不要在 paint 中重複 createImage,在建構子或 sizeChanged 建立一次。Canvas 不再顯示時 (如 hideNotify),設 offscreen = null 協助 GC。Thread.sleep 或 Timer 固定幀率 (如 20fps = 50ms),在 run() 呼叫 repaint() + serviceRepaints()。repaint(x,y,w,h) 減少 blit 範圍 (需系統支援)。雙緩衝是 J2ME 圖形程式設計的基石技能。現代手機多已硬體支援,但理解軟體實作原理、掌握 isDoubleBuffered() 檢測與條件配置模式,能確保應用在全機型上獲得流暢、無閃爍的視覺體驗。