開發者編寫了一個 Swing 視窗程式,透過選單項目切換背景圖片。首次點選正常,但第二次點選任一選單項,背景圖片不再更新。即使在 changePhoto() 末尾呼叫 img.repaint() 也無濟於事。
public void changePhoto(String picName) {
bg = Toolkit.getDefaultToolkit().getImage("demo\\" + picName);
bg2 = bg.getScaledInstance(600, 480, Image.SCALE_DEFAULT);
img = new JLabel(new ImageIcon(bg2)); // 每次都 new 新 JLabel
img.setBounds(0, 0, 600, 480);
p3.add(img, new Integer(-30001));
img.repaint(); // 對新 new 的 JLabel 無效
}
changePhoto() 都會 new JLabel(...),產生一個全新的標籤物件。p3,但舊標籤並未移除,導致圖層堆疊。repaint() 作用於新建的 JLabel,但該標籤剛建立尚未顯示,重繪無意義;真正顯示在最上層的可能仍是舊標籤。public void changePhoto(String picName) {
System.out.println("change photo :" + picName);
bg = Toolkit.getDefaultToolkit().getImage("demo\\" + picName);
bg2 = bg.getScaledInstance(600, 480, Image.SCALE_DEFAULT);
// 關鍵:只在第一次建立 JLabel,後續直接 setIcon
if (img == null) {
img = new JLabel(new ImageIcon(bg2));
img.setOpaque(false); // 透明,避免遮擋其他元件
img.setBounds(0, 0, 600, 480);
p3.add(img, new Integer(-30001)); // 放到最底層
} else {
img.setIcon(new ImageIcon(bg2)); // 核心:替換圖示自動觸發重繪
}
// 無需手動呼叫 repaint(),setIcon 內部已處理
}
JLabel.setIcon() 內部會觸發 repaint(),無需手動干預。JLayeredPane 並指定負數層級(如 -30001)確保背景始終在最底層。img.setOpaque(false) 與 contentPane.setOpaque(false) 配合,避免背景遮蓋前景元件。若圖片數量大、頻繁切換,建議引入圖片快取池(如 Map<String, ImageIcon>),避免重複 getScaledInstance 造成記憶體抖動;並在視窗關閉時釋放 Image.flush()。