在 Java Swing 開發中,構建類似 IDE(整合開發環境)那樣擁有「視窗中視窗」效果的多文檔介面(MDI),核心在於 JInternalFrame(內部視窗) 與 JDesktopPane(桌面面板) 的搭配使用。本文將透過完整程式碼範例,詳細解析如何正確建立、佈局與定位內部視窗,並解決新手常遇到的「視窗不顯示」或「大小異常」等棘手問題。
JLayeredPane,是容納內部視窗的特殊容器,負責管理 Z 軸層疊順序與拖曳行為。JDesktopPane 才能正常運作。import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class InternalFrameDemo extends JFrame {
public InternalFrameDemo() {
initUI();
}
private void initUI() {
setTitle("MDI 內部視窗範例");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 1. 建立桌面面板 (容器)
JDesktopPane desktop = new JDesktopPane();
// 設定桌面背景顏色,方便觀察範圍
desktop.setBackground(Color.LIGHT_GRAY);
// 2. 建立第一個內部視窗
JInternalFrame frame1 = createInternalFrame("內部視窗 A", 50, 50, 300, 200);
frame1.add(new JLabel("我是視窗 A 的內容", SwingConstants.CENTER));
desktop.add(frame1);
// 3. 建立第二個內部視窗
JInternalFrame frame2 = createInternalFrame("內部視窗 B", 100, 100, 300, 200);
JPanel panel = new JPanel(new BorderLayout());
panel.add(new JLabel("視窗 B 頂部資訊", SwingConstants.CENTER), BorderLayout.NORTH);
panel.add(new JButton("互動按鈕"), BorderLayout.CENTER);
frame2.add(panel);
desktop.add(frame2);
// 4. 將桌面面板置於主視窗中央
add(desktop, BorderLayout.CENTER);
// 5. 視窗定位與顯示 (關鍵步驟)
pack(); // 依據元件優先尺寸調整主視窗
centerOnScreen(); // 置中螢幕
setVisible(true);
}
/** 封裝建立內部視窗的邏輯 */
private JInternalFrame createInternalFrame(String title, int x, int y, int w, int h) {
JInternalFrame iframe = new JInternalFrame(title,
true, // resizable 可調整大小
true, // closable 可關閉
true, // maximizable 可最大化
true // iconifiable 可最小化
);
iframe.setBounds(x, y, w, h); // 設定位置與大小 (比 setSize+setLocation 更佳)
iframe.setVisible(true); // 【關鍵】必須顯式設為 true,否則不會顯示!
return iframe;
}
/** 將主視窗置中於螢幕 */
private void centerOnScreen() {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = getSize();
if (frameSize.height > screenSize.height) frameSize.height = screenSize.height;
if (frameSize.width > screenSize.width) frameSize.width = screenSize.width;
setLocation((screenSize.width - frameSize.width) / 2,
(screenSize.height - frameSize.height) / 2);
}
public static void main(String[] args) {
// 確保在 EDT (事件派發執行緒) 中啟動 Swing 程式
SwingUtilities.invokeLater(() -> new InternalFrameDemo());
}
}
setVisible(true)現象: 程式碼編譯通過,主視窗出現,但桌面面板一片空白,內部視窗消失無蹤。
原因: JInternalFrame 預設不可見,不同於 JFrame 或 JDialog,必須手動呼叫 iframe.setVisible(true)。
對策: 在加入桌面面板前,務必加上這行代碼。
現象: 視窗出現但無法拖曳、無法最大化、標題列渲染異常。
原因: JInternalFrame 依賴 DesktopManager 管理行為,只有 JDesktopPane 提供此管理器。
對策: 永遠 desktop.add(internalFrame),不要 frame.add(internalFrame)。
setSize() 卻發現視窗大小不對現象: 設定 setSize(300, 200),但實際顯示比預期小,標題列被切掉一部分。
原因: setSize 設定的是視窗「客戶區」大小,不含標題列與邊框;且在某些 Look & Feel 下計算差異大。
對策: 使用 setBounds(x, y, width, height) 一次設定位置與外部尺寸,或呼叫 pack() 讓佈局管理器自動計算優先尺寸。
pack() 就 setVisible(true)現象: 主視窗極小,只看到標題列,內容區塊被壓縮。
原因: JFrame 預設大小為 0x0。若先 setVisible(true) 再 pack(),會導致畫面閃爍或內部視窗渲染異常。
對策: 標準流程:add元件 → pack() → centerOnScreen() → setVisible(true)。
// 取得桌面上所有內部視窗
JInternalFrame[] frames = desktop.getAllFrames();
// 遍歷關閉所有視窗 (需捕獲 PropertyVetoException)
for (JInternalFrame f : frames) {
try { f.setClosed(true); }
catch (PropertyVetoException e) { e.printStackTrace(); }
}
// 監聽內部視窗事件 (關閉、圖示化、激活等)
frame1.addInternalFrameListener(new InternalFrameAdapter() {
@Override
public void internalFrameClosing(InternalFrameEvent e) {
System.out.println("視窗即將關閉,可在此儲存資料");
}
});
掌握 JDesktopPane + JInternalFrame 的正確使用姿勢,關鍵在於三個動作:**容器正確、顯式可見、尺寸定位**。避開上述四大坑點,您就能輕鬆構建出專業級的多文檔介面應用程式。雖然現代趨勢偏向單視窗多標籤頁(如 IDEA、VS Code),但 MDI 架構在特定垂直領域(如監控儀表板、金融交易終端)依然有其不可替代的價值。