Swing 視窗分層結構:JFrame → JRootPane → JLayeredPane → ContentPane。背景圖片置於 JLayeredPane 最底層 (Integer.MIN_VALUE),內容元件置於上層。
import java.awt.*;
import javax.swing.*;
public class TestBackgroundImage extends JFrame {
private JPanel imagePanel;
private ImageIcon background;
public TestBackgroundImage() {
// 1. 載入背景圖片 (需放在 classpath 或絕對路徑)
background = new ImageIcon("gradient_bg_14.png");
// 建議: getClass().getResource("/images/bg.png") 載入資源檔
// 2. 建立 JLabel 承載圖片
JLabel label = new JLabel(background);
label.setBounds(0, 0, background.getIconWidth(), background.getIconHeight());
// 3. 取得 ContentPane 並設為透明
imagePanel = (JPanel) this.getContentPane();
imagePanel.setOpaque(false); // 關鍵:讓底層圖片可見
// 4. ContentPane 使用 FlowLayout (或其他) 排列元件
imagePanel.setLayout(new FlowLayout());
imagePanel.add(new JButton("測試按鈕"));
imagePanel.add(new JTextField(20));
// 5. 關鍵:LayeredPane 設為絕對定位,將背景標籤加入最底層
this.getLayeredPane().setLayout(null);
this.getLayeredPane().add(label, new Integer(Integer.MIN_VALUE));
// 6. 視窗設定
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(background.getIconWidth(), background.getIconHeight());
this.setLocationRelativeTo(null);
this.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new TestBackgroundImage());
}
}
| 步驟 | 代碼 | 用途 |
|---|---|---|
| 1 | new ImageIcon(path) | 載入圖片 (支援 PNG/JPG/GIF) |
| 2 | new JLabel(icon) | 將圖片包裝為可佈局元件 |
| 3 | label.setBounds(...) | 設定圖片大小位置 (絕對座標) |
| 4 | contentPane.setOpaque(false) | ContentPane 預設不透明,必須設透明才能看見底層 |
| 5 | layeredPane.setLayout(null) | LayeredPane 需絕對定位才能精確放置底層元件 |
| 6 | layeredPane.add(label, Integer.MIN_VALUE) | 將背景加入最底層 (負無窮),確保被其他元件覆蓋 |
class ScaledBackgroundLabel extends JLabel {
private Image image;
public ScaledBackgroundLabel(ImageIcon icon) {
this.image = icon.getImage();
setOpaque(false);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (image != null) {
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
}
}
}
// 使用方式
JLabel bgLabel = new ScaledBackgroundImage(new ImageIcon("bg.png"));
bgLabel.setBounds(0, 0, frameWidth, frameHeight);
// 需在視窗 resize 時重繪:addComponentListener 觸發 repaint()
// 圖片放在 src/main/resources/images/bg.png
ImageIcon bg = new ImageIcon(
getClass().getClassLoader().getResource("images/bg.png")
);
// 或
ImageIcon bg = new ImageIcon(
TestBackgroundImage.class.getResource("/images/bg.png")
);
JPanel overlay = new JPanel() {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(new Color(0, 0, 0, 128)); // 黑色 50% 透明
g2.fillRect(0, 0, getWidth(), getHeight());
}
};
overlay.setOpaque(false);
overlay.setLayout(new BorderLayout());
overlay.add(mainContentPanel, BorderLayout.CENTER);
imagePanel.add(overlay, BorderLayout.CENTER);
| 現象 | 原因 | 解決 |
|---|---|---|
| 背景不顯示 | ContentPane 未設透明 | contentPane.setOpaque(false) |
| 圖片被元件遮蓋 | 層級錯誤 | 背景用 Integer.MIN_VALUE,元件用預設層級 |
| 視窗調整大小圖片不變 | 使用固定大小 JLabel | 改用自訂 paintComponent 縮放繪製 |
| 打包 JAR 後找不到圖片 | 用檔案路徑而非資源路徑 | 用 getClass().getResource() 載入 |
| 元件背景也透明了 | 元件繼承了透明屬性 | 元件個別 setOpaque(true) 並設背景色 |
更簡潔的做法:自訂 JPanel 直接繪製背景,無需 LayeredPane 技巧:
class BackgroundPanel extends JPanel {
private Image bgImage;
public BackgroundPanel(String imagePath) {
bgImage = new ImageIcon(imagePath).getImage();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(bgImage, 0, 0, getWidth(), getHeight(), this);
}
}
// 使用
setContentPane(new BackgroundPanel("bg.png"));
此法自動處理縮放、無層級困擾、程式碼更少,推薦為首選方案。
Swing 背景圖片實作的核心在於「容器透明化」與「層級控制」。理解 JRootPane 分層架構,選擇適合專案複雜度的方案 (簡單用 paintComponent,複雜互動用 LayeredPane),即能打造美觀專業的桌面應用介面。