Swing 開發者最熟悉的痛點莫過於:JFrame.setLayout() 拋錯、必須先 getContentPane() 才能加元件。Java 5 (Tiger) 徹底終結這段歷史——**頂層容器現在直接支援 add()、setLayout()、remove()**,內容窗格自動代理,讓 GUI 開發更直覺。
Swing 頂層容器 (JFrame/JApplet/JDialog/JWindow) 內部由 JRootPane 管理,包含四層:
過去必須寫:
JFrame frame = new JFrame("Title");
Container cp = frame.getContentPane(); // 必須取得 ContentPane
cp.setLayout(new GridLayout(1,2));
cp.add(label);
cp.add(button);
直接對 frame.setLayout() 會拋錯:Do not use JFrame.setLayout() use JFrame.getContentPane().setLayout() instead。
Tiger 讓 JFrame/JDialog/JApplet/JWindow 代理 add/setLayout/remove 至 ContentPane:
JFrame frame = new JFrame("Title");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(1,2)); // 直接設定版面
frame.add(label); // 直接加元件
frame.add(button);
frame.pack();
frame.setVisible(true);
原理:RootPaneContainer 介面新增預設方法實作,委派給 getContentPane()。
雖 95% 場景可免 getContentPane(),但以下情況**必須**直接存取:
| 操作 | 原因 | 正確寫法 |
|---|---|---|
setBackground(Color) |
JFrame 的背景非內容區背景,設定無效 | frame.getContentPane().setBackground(Color.WHITE); |
setComponentZOrder(comp, index) |
疊層順序屬於 ContentPane 管理 | frame.getContentPane().setComponentZOrder(button, 0); |
| 自訂 LayoutManager 需存取 ContentPane 專屬方法 | 代理僅涵蓋 add/setLayout/remove | 直接操作 getContentPane() |
getContentPane() 仍有效,行為不變理解 JRootPane 結構可解鎖進階功能:
frame.setGlassPane(customPanel) 實現全域事件攔截、載入遮罩、工具提示增強frame.getLayeredPane().add(component, JLayeredPane.MODAL_LAYER) 實現模態對話框、工具提示、拖放預覽frame.setJMenuBar(menuBar) 設定選單列 (已支援代理)Java 5 針對 Swing 最大痛點——ContentPane 樣板代碼——給出了優雅解法。頂層容器現在表現得像個正常容器,讓開發者專注佈局與元件組合,而非記憶委派規則。下次建立 JFrame 時,大膽地直接 frame.add(component) 吧!