低端手機繪圖硬件弱、無雙緩存、GC 頻繁,paint() 調用頻率高(動畫典型 20-30 FPS),稍有不慎即成性能瓶頸。以下六條鐵律源自大量真機調試血淚總結。
// ❌ 反面教材:每幀讀取圖片、創建對象
try {
Image img = Image.createImage("/back.png"); // IO 操作極慢
g.drawImage(img, 0, 0, Graphics.TOP | Graphics.LEFT);
} catch (Exception e) {}
正確姿勢:init() 或構造器中一次性加載所有資源,paint() 僅負責繪製。
兩種實現方式:
g.setClip(x, y, w, h) 限制繪圖區域,repaint() 只重繪 Clip 內內容。典型場景:角色移動時只刷新舊位置與新位置矩形區域,背景靜態區域完全跳過。
repaint(),事件驅動觸發即可。Thread.sleep(40~100),寧可低幀率不卡頓。return 跳過繪製邏輯。paint() 僅執行 g.drawXxx() 等 GPU 指令,保持極致精簡。原理:離屏繪製到 Image,再一次性繪到屏幕,消除閃爍、減少 GPU 狀態切換。
// 初始化離屏緩衝
offscreenImg = Image.createImage(width, height);
Graphics offG = offscreenImg.getGraphics();
// 邏輯線程或 init() 中繪製完整幀
offG.drawImage(bg, 0, 0, 0);
offG.drawString(text, x, y, 0);
offG.drawImage(sprite, sx, sy, 0);
// paint() 僅一行
public void paint(Graphics g) {
g.drawImage(offscreenImg, 0, 0, Graphics.TOP | Graphics.LEFT);
}
進階技巧:利用 g.translate() 偏移座標系統實現滾動背景,繪製完畢再 translate() 歸零,避免重新計算所有座標。
public void paint(Graphics g) {
g.translate(-cameraX, -cameraY); // 設置相機偏移
g.drawImage(offscreenImg, 0, 0, 0);
g.translate(cameraX, cameraY); // 還原
}
Canvas.isDoubleBuffered() 判斷硬件原生雙緩存支持。若支持,可直接依賴系統;否則**必須手動實現離屏緩衝**。
| 優化點 | 檢查標準 |
|---|---|
| 對象創建 | paint() 內零 new、零 IO |
| 圖片色深 | ≤ 256 色,尺寸最小化 |
| 刷新範圍 | 全部使用 repaint(x,y,w,h) |
| 調用頻率 | 靜態界面 0,動態 ≤ 25 FPS |
| 計算外提 | paint() 內無數學/字符串操作 |
| 雙緩存 | 無硬件支持時手動實現 |
逐項對照,真機幀率提升 2-5 倍不在話下。