MIDP 低層圖形架構總覽
javax.microedition.lcdui 封裝了完整的低層繪圖體系,五大核心類各司其職:
| 類別 | 角色 | 核心職責 |
| Display | 螢幕管理器 | 單例、管理當前顯示物件、查詢螢幕屬性 (彩色/灰階/尺寸) |
| Canvas | 繪圖畫布 | 低層 UI 基類、提供 paint(Graphics) 回調、按鍵事件、全螢幕模式 |
| Graphics | 繪圖上下文 | 所有繪圖原語 (線/矩形/圓弧/文字/圖片)、顏色/字體/裁切區/錨點狀態 |
| Font | 字體描述 | 面貌/風格/大小、度量方法 (高度/寬度/基線) |
| Image | 圖片資源 | 不可變 (資源/網路) / 可變 (離屏緩衝)、建立/繪製/度量 |
Display:螢幕控制中樞
// 取得單例
Display display = Display.getDisplay(midlet);
// 切換顯示物件
display.setCurrent(canvas); // Canvas
display.setCurrent(form); // 高層 UI
display.setCurrent(alert, nextScreen); // Alert 後自動轉回
// 查詢螢幕特性
boolean isColor = display.isColor(); // 是否彩屏
int numColors = display.numColors(); // 色數 (2=黑白)
int width = display.getWidth(); // 螢幕寬 (僅全螢幕模式準確)
int height = display.getHeight(); // 螢幕高
Canvas:遊戲主迴圈載體
public class GameCanvas extends Canvas {
public GameCanvas() {
setFullScreenMode(true); // 隱藏標題/軟鍵欄 (MIDP 2.0)
}
// 核心繪圖回調 (由系統自動調用,不可手動呼叫)
protected void paint(Graphics g) {
// 所有繪圖邏輯在這裡
}
// 按鍵事件
protected void keyPressed(int keyCode) { /* 邏輯幀 */ }
protected void keyReleased(int keyCode) { /* 釋放 */ }
protected void keyRepeated(int keyCode) { /* 長按 */ }
// 遊戲動作映射 (跨裝置統一)
int action = getGameAction(keyCode); // UP, DOWN, LEFT, RIGHT, FIRE...
}
Graphics:繪圖原語大全
顏色與灰階
// RGB 24-bit
g.setColor(0xFF0000); // 紅
g.setColor(255, 0, 0); // 同義
// 灰階 (0-255)
g.setGrayScale(128);
int gray = g.getGrayScale();
文字繪製
g.setFont(font);
g.drawString(str, x, y, anchor); // 字串
g.drawChars(chars, offset, len, x, y, anchor); // 字元陣列
g.drawChar(ch, x, y, anchor); // 單字元
圖片繪製
g.drawImage(img, x, y, anchor); // 完整繪製
g.drawRegion(img, srcX, srcY, w, h, transform, x, y, anchor); // 區域+變換
// transform: TRANS_NONE, ROT90, ROT180, ROT270, MIRROR, MIRROR_ROT90...
幾何圖形
| 類別 | 空心 | 實心 | 參數 |
| 線段 | drawLine(x1,y1,x2,y2) | - | 端點座標 |
| 矩形 | drawRect(x,y,w,h) | fillRect(x,y,w,h) | 左上角、寬高 |
| 圓角矩形 | drawRoundRect(x,y,w,h,aw,ah) | fillRoundRect(...) | + 圓角寬高 |
| 圓弧/扇形 | drawArc(x,y,w,h,start,arc) | fillArc(...) | 外接矩形、起始角、掃過角 |
裁切區
g.setClip(x, y, w, h); // 設定可繪製區域
g.clipRect(x, y, w, h); // 與現有區域取交集
// 恢復全螢幕
g.setClip(0, 0, getWidth(), getHeight());
座標系與錨點
- 原點 (0,0) = 左上角,X 右增,Y 下增。
- 座標為像素格線交點,線寬 1 像素佔據格線間空間。
- 錨點組合:
LEFT|TOP (預設)、HCENTER|VCENTER (中心)、RIGHT|BOTTOM 等。
Font:字體度量與選擇
// 取得字體
Font f = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_LARGE);
// 度量 (佈局必備)
int h = f.getHeight(); // 行高
int baseline = f.getBaselinePosition(); // 基線
int w = f.stringWidth("Hello"); // 字串寬
int cw = f.charWidth('中'); // 單字寬
int sw = f.substringWidth(s, 0, 3); // 子串寬
int cwArr = f.charsWidth(arr, 0, len); // 陣列寬
Image:兩種模式與建立方式
| 模式 | 建立方式 | 可變性 | 用途 |
| 不可變 | Image.createImage("/res/img.png")
createImage(byte[], offset, len)
createImage(InputStream)
createImage(Image src) | 只讀 | 資源圖、網路下載圖、精靈幀 |
| 可變 | Image.createImage(w, h) | 可寫入 (離屏緩衝) | 雙重緩衝、動態合成、文字渲染到圖 |
可變 Image 繪製範例
// 建立離屏緩衝
Image buffer = Image.createImage(176, 208);
Graphics bg = buffer.getGraphics();
bg.setColor(0xFFFFFF);
bg.fillRect(0, 0, 176, 208);
bg.drawString("預渲染文字", 10, 10, Graphics.LEFT | Graphics.TOP);
// 主畫布直接繪製 buffer (極快)
protected void paint(Graphics g) {
g.drawImage(buffer, 0, 0, Graphics.TOP | Graphics.LEFT);
}
MIDP 2.0 進階建立
// 從源圖裁切並變換 (保留透明度、標準 API)
Image frame = Image.createImage(sourceImage,
srcX, srcY, frameW, frameH, Sprite.TRANS_NONE);
雙重緩衝與效能優化
canvas.isDoubleBuffered() 查詢硬體支援 (MIDP 2.0)。
- 軟體雙重緩衝:自建可變 Image 做離屏渲染,
paint() 只 drawImage 一次。
- 批次繪製:同顏色/字體連續繪製,減少狀態切換。
- 裁切區優化:
setClip 限制重繪區域 (局部更新)。
- 避免在
paint() 內建立物件 (new Font, new String, 運算) — 預先緩存。
完整遊戲渲染迴圈範架
public class GameCanvas extends Canvas implements Runnable {
private Image offscreen; // 軟體雙重緩衝
private Graphics offG;
private Thread gameLoop;
private volatile boolean running = true;
public GameCanvas() {
setFullScreenMode(true);
offscreen = Image.createImage(getWidth(), getHeight());
offG = offscreen.getGraphics();
gameLoop = new Thread(this);
gameLoop.start();
}
public void run() {
while (running) {
long t0 = System.currentTimeMillis();
updateLogic(); // 邏輯更新
renderOffscreen(); // 離屏繪製
repaint(); // 排程 paint()
long dt = System.currentTimeMillis() - t0;
if (dt < 40) try { Thread.sleep(40 - dt); } catch (Exception ignored) {} // ~25 FPS
}
}
private void renderOffscreen() {
offG.setColor(0x000000);
offG.fillRect(0, 0, getWidth(), getHeight());
// 繪製所有遊戲物件到 offG...
}
protected void paint(Graphics g) {
// 只需一張圖貼上去
g.drawImage(offscreen, 0, 0, Graphics.TOP | Graphics.LEFT);
}
}