在 Java ME 高階 API(如 Form、List)中,開發者無法精確控制螢幕像素級的繪製細節,系統會自動選擇最適合裝置的顯示方式。然而,遊戲或圖形密集型應用往往需要對繪圖過程有完全掌控權。MIDP javax.microedition.lcdui 套件提供的低階 API 正是為此而生,核心在於 Canvas 類別。
Canvas 繼承自 Displayable,是一個抽象類別,提供一個可自由繪製的矩形區域。要使用它,必須建立子類別並覆寫 paint(Graphics g) 方法。Graphics 物件封裝了所有繪圖原語(線條、矩形、文字、圖片等)。
import javax.microedition.lcdui.*;
public class MyCanvas extends Canvas {
public void paint(Graphics g) {
// 設定紅色背景
g.setColor(255, 0, 0);
g.fillRect(0, 0, getWidth(), getHeight());
// 設定白色前景並繪製文字
g.setColor(255, 255, 255);
g.drawString("Hello World!", 0, 0, Graphics.TOP | Graphics.LEFT);
}
}
因 Canvas 是 Displayable 子類,可直接用 Display.setCurrent() 顯示:
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class MyMidlet extends MIDlet {
public void startApp() {
Canvas canvas = new MyCanvas();
Display.getDisplay(this).setCurrent(canvas);
}
public void pauseApp() {}
public void destroyApp(boolean unconditional) {}
}

圖 1. 在 Canvas 上繪製 "Hello World!"
getWidth()、getHeight() 取得可繪製區域尺寸Graphics 當前顏色直接覆蓋目標像素良好的應用應檢查裝置色彩支援:Display.isColor()、Display.numColors()。
直接在螢幕繪圖易產生閃爍。雙緩衝技術先在離屏 Image 緩衝區繪製,完成後一次性複製到螢幕:
// 建立與螢幕等大的離屏緩衝區
int w = getWidth(), h = getHeight();
Image buffer = Image.createImage(w, h);
Graphics gc = buffer.getGraphics();
// 在緩衝區繪製動畫幀
gc.drawRect(20, 20, 25, 30);
// paint() 中將緩衝區內容繪至螢幕
public void paint(Graphics g) {
g.drawImage(buffer, 0, 0, Graphics.TOP | Graphics.LEFT);
}
可用 Canvas.isDoubleBuffered() 檢查實作是否已內建雙緩衝。
Canvas.serviceRepaints() 會立即同步呼叫 paint(),若 paint() 需鎖定被 serviceRepaints() 持有的物件,會造成死結。避免在使用 serviceRepaints() 時鎖定 paint() 會用到的物件。Display.callSerially(Runnable) 可在所有待處理重繪完成後,於事件執行緒執行程式碼,適合遊戲主迴圈:class GameCanvas extends Canvas implements Runnable {
void gameLoop() {
// 更新遊戲狀態
repaint(); // 排程重繪
callSerially(this); // 重繪完成後繼續下一幀
}
public void run() { gameLoop(); }
}
應用無法自行建立字體,只能透過 Font.getFont(face, style, size) 向系統請求最接近的字體。三屬性組合:
| 屬性 | 可用值 |
|---|---|
| Face | MONOSPACE, PROPORTIONAL, SYSTEM |
| Size | SMALL, MEDIUM, LARGE |
| Style | BOLD, ITALIC, PLAIN, UNDERLINED (可用 | 組合) |
範例:Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD | Font.STYLE_UNDERLINED, Font.SIZE_MEDIUM)。系統保證永遠回傳有效的 Font 物件。
public class FontCanvas extends Canvas {
public void paint(Graphics g) {
g.setColor(0xFFFFFF);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(0x000000);
g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_LARGE));
g.drawString("System Font", 0, 0, Graphics.LEFT | Graphics.TOP);
g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_MEDIUM));
g.drawString("Medium Size", 0, 15, Graphics.LEFT | Graphics.TOP);
g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_MEDIUM));
g.drawString("Bold Style", 0, 30, Graphics.LEFT | Graphics.TOP);
g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_ITALIC, Font.SIZE_MEDIUM));
g.drawString("Italic Style", 0, 45, Graphics.LEFT | Graphics.TOP);
g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_UNDERLINED, Font.SIZE_MEDIUM));
g.drawString("Underlined Style", 0, 60, Graphics.LEFT | Graphics.TOP);
}
}

圖 2. 在裝置螢幕上繪製系統字體