Applet 雖已退出歷史舞台,但其核心程式設計模型 — 生命週期管理、事件驅動、多執行緒協作、繪圖管線 — 仍是 Java GUI (Swing/JavaFX)、Android、遊戲引擎的基因。本文系統整理五大進階技巧。
| 方法 | 觸發時機 | 核心職責 | 執行緒 |
|---|---|---|---|
init() | 首次載入頁面 | 一次性初始化:解析參數、載入資源、建立 UI、啟動背景執行緒 | AWT 初始化執行緒 |
start() | 頁面可見 / 返回頁面 | 啟動/恢復動畫、定時器、網路輪詢 | AWT 事件派發執行緒 (EDT) |
paint(Graphics g) | 需重繪 (初次、遮蔽恢復、repaint()) | 純繪圖邏輯,禁止耗時運算/阻塞 | EDT |
stop() | 離開頁面 / 最小化 | 暫停動畫、釋放非必要資源、停止背景執行緒 | EDT |
destroy() | 關閉瀏覽器 / 卸載 Applet | 最終清理:關閉連線、儲存狀態、釋放原生資源 | EDT |
關鍵:init() 僅執行一次;start()/stop() 可多次交替;destroy() 為最後機會釋放資源。
MultiThreadApplet預設 Applet 邏輯在單一 EDT 執行,死迴圈會凍結瀏覽器。解法:引入專屬執行緒跑業務迴圈。
public abstract class MultiThreadApplet extends Applet implements Runnable {
private Thread appletThread;
public void start() {
if (appletThread == null) {
appletThread = new Thread(this);
appletThread.start();
}
}
public void stop() {
if (appletThread != null) {
appletThread.interrupt(); // 優雅中斷
appletThread = null;
}
}
// 子類別實作業務迴圈
public abstract void run();
}
使用範例 — 計數器動畫:
public class CounterApplet extends MultiThreadApplet {
private int counter = 0;
public void run() {
while (!Thread.currentThread().isInterrupted()) {
counter++;
repaint(); // 排程重繪
try { Thread.sleep(100); } catch (InterruptedException e) { break; }
}
}
public void paint(Graphics g) {
g.drawString("Count: " + counter, 10, 20);
}
}
優點:執行緒管理封裝於基底類,業務邏輯聚焦於 run(),stop() 自動釋放 CPU。
paint() 直接畫在螢幕顯示記憶體,清除→重畫背景→畫前景 過程中畫面可見,造成閃爍。
Image offscreen = createImage(width, height)。Graphics offG = offscreen.getGraphics()。offG (清除、背景、前景)。g.drawImage(offscreen, 0, 0, this) 一次性貼上螢幕。public class BouncingBall extends MultiThreadApplet {
private Image offscreen;
private Graphics offG;
private int ballX = 0, ballY = 80, dx = 2, dy = 2;
private final int BALL_SIZE = 30;
public void init() {
setBackground(Color.white);
}
public void run() {
// 延遲建立離屏緩衝 (需元件尺寸已定)
while (offscreen == null) {
offscreen = createImage(getWidth(), getHeight());
if (offscreen != null) offG = offscreen.getGraphics();
try { Thread.sleep(50); } catch (InterruptedException ignored) {}
}
while (!Thread.currentThread().isInterrupted()) {
// 物理更新
ballX += dx; ballY += dy;
if (ballX <= 0 || ballX + BALL_SIZE >= getWidth()) dx = -dx;
if (ballY <= 0 || ballY + BALL_SIZE >= getHeight()) dy = -dy;
// 離屏繪製
offG.setColor(getBackground());
offG.fillRect(0, 0, getWidth(), getHeight()); // 清除
offG.setColor(Color.red);
offG.fillOval(ballX, ballY, BALL_SIZE, BALL_SIZE); // 畫球
repaint(); // 觸發 paint()
try { Thread.sleep(16); } catch (InterruptedException e) { break; } // ~60 FPS
}
}
public void paint(Graphics g) {
if (offscreen != null) g.drawImage(offscreen, 0, 0, this);
}
public void update(Graphics g) { paint(g); } // 覆寫 update 避免預設清除再 paint 導致閃爍
}
| 策略 | 說明 | 適用場景 |
|---|---|---|
| 1. 最小化繪圖區域 | 僅重繪變化區域 (clipRect、repaint(x,y,w,h)) | 大畫面局部動畫 |
| 2. 高演算法繪圖 | 中點畫圓、布雷森漢姆畫線、掃描線填充 | 大量幾何圖元 |
| 3. 色彩循環 | 調色盤索引動畫,無需重繪像素 | 256 色模式、復古風格 |
| 4. 圖樣/貼圖替代細節 | 預渲染複雜圖形為 Image/Sprite,執行期僅 drawImage | 粒子系統、背景圖層 |
| 5. 定點整數 3D 運算 | 避免浮點運算開銷,定點數模擬 3D 變換 | 早期無 FPU 處理器 |
| 6. 查表法預計算 | 三角函數、物理軌跡預存陣列,執行期 O(1) 索引 | 固定軌跡、週期性動畫 |
javac -O (舊版) / ProGuard 壓縮優化。init() 僅做極速必要初始化,啟動輔助執行緒載入資料/圖片/音效。Applet 程式設計技巧是 Java 圖形系統、遊戲迴圈、即時渲染管線的縮影。掌握生命週期契約、EDT 與業務執行緒分離、雙緩衝渲染、效能剖析與優化、載入體驗設計,能讓開發者在任何平台 (Swing, JavaFX, Android, LibGDX, LWJGL) 駕輕就熟地構建流暢、高效能的視覺應用。