在開發遠程桌面協助功能時,接收對方螢幕畫面並顯示於 JLabel 上,若直接在 paint() 繪製圖片,畫面會嚴重閃爍。根因在於 Swing 預設繪圖流程:repaint() → update() → paint(),其中 update() 會先清除背景再呼叫 paint(),導致畫面瞬間變白再重繪,產生閃爍。
雙緩衝(Double Buffering)在記憶體中建立一個與元件同尺寸的離屏圖像 off_screen_buf,所有繪圖操作先在離屏 Graphics 上完成,最後一次性將離屏圖像繪製到螢幕 Graphics 上,避免中間態被使用者看見。
public class MyLabel extends JLabel {
// 雙緩衝離屏緩衝區
private Image offScreenImage;
private Graphics offScreenGraphics;
@Override
public void paint(Graphics g) {
// 實際業務繪圖邏輯:繪製遠程螢幕截圖
if (Myjxta.image != null) {
this.setPreferredSize(new Dimension(
Myjxta.image.getWidth(), Myjxta.image.getHeight()));
g.drawImage(Myjxta.image, 0, 0, this);
}
// 模擬網路延遲
try { Thread.sleep(200); } catch (Exception ignored) {}
}
@Override
public void update(Graphics g) {
if (Myjxta.image != null) {
// 1. 建立離屏圖像與 Graphics
offScreenImage = this.createImage(this.getWidth(), this.getHeight());
offScreenGraphics = offScreenImage.getGraphics();
// 2. 在離屏上完成所有繪圖
paint(offScreenGraphics);
// 3. 釋放離屏 Graphics 資源
offScreenGraphics.dispose();
// 4. 一次性將離屏圖像畫到螢幕
g.drawImage(offScreenImage, 0, 0, null);
// 5. 觸發下一幀重繪(形成動畫迴路)
this.repaint();
}
}
}
| 步驟 | 說明 |
|---|---|
| 建立離屏圖像 | createImage(w, h) 產生與元件等大的 Image |
| 取得離屏 Graphics | offScreenImage.getGraphics() 所有繪圖導向記憶體 |
| 呼叫 paint() | 將原本給螢幕的 Graphics 換成離屏 Graphics |
| 釋放資源 | dispose() 避免記憶體洩漏 |
| 一次性上螢幕 | g.drawImage(offScreenImage, 0, 0, null) 無閃爍 |
| 持續重繪 | repaint() 形成動畫迴路 |
Swing 元件預設已啟用雙緩衝(JComponent.setDoubleBuffered(true)),若仍閃爍,請檢查:
paint() 中執行耗時運算或 I/O(應移至背景執行緒)update() 卻未正確處理背景清除BufferStrategy(適用於全螢幕/Canvas)或 JavaFX 內建雙緩衝本例展示手動控制雙緩衝的經典寫法,理解其機制有助於排查疑難雜症。