構建一個經典的 MDI(多文檔介面)桌面應用架構:JDesktopPane 作為容器,JInternalFrame 代表子視窗,選單列提供「新建文檔」/「退出」功能,支援內部視窗拖曳、最大化、最小化、關閉、圖標化與 Z 序管理。完整展示桌面面板配置、內部視窗生命週期、選單快捷鍵與拖曳模式設定。
public class InternalFrameDemo extends JFrame implements ActionListener {
JDesktopPane desktop;
public InternalFrameDemo() {
super("MDI Application Demo");
// 視窗佔滿螢幕留 50px 邊距
int inset = 50;
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
setBounds(inset, inset, screen.width - inset*2, screen.height - inset*2);
// 桌面面板
desktop = new JDesktopPane();
createFrame(); // 建立第一個內部視窗
setContentPane(desktop);
setJMenuBar(createMenuBar());
// 拖曳模式:僅繪製輪廓(效能較好)
desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
}
protected JMenuBar createMenuBar() {
JMenuBar bar = new JMenuBar();
JMenu menu = new JMenu("檔案(F)");
menu.setMnemonic(KeyEvent.VK_F);
// 新建文檔:Ctrl+N
JMenuItem newItem = new JMenuItem("新建文檔(N)");
newItem.setMnemonic(KeyEvent.VK_N);
newItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK));
newItem.setActionCommand("new");
newItem.addActionListener(this);
menu.add(newItem);
// 退出:Ctrl+Q
JMenuItem quitItem = new JMenuItem("退出(Q)");
quitItem.setMnemonic(KeyEvent.VK_Q);
quitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.CTRL_MASK));
quitItem.setActionCommand("quit");
quitItem.addActionListener(this);
menu.add(quitItem);
bar.add(menu);
return bar;
}
public void actionPerformed(ActionEvent e) {
if ("new".equals(e.getActionCommand())) createFrame();
else System.exit(0);
}
protected void createFrame() {
MyInternalFrame frame = new MyInternalFrame();
frame.setVisible(true);
desktop.add(frame);
try { frame.setSelected(true); } // 置頂並獲得焦點
catch (java.beans.PropertyVetoException ignored) {}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame.setDefaultLookAndFeelDecorated(true);
InternalFrameDemo frame = new InternalFrameDemo();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
});
}
}
public class MyInternalFrame extends JInternalFrame {
static int openFrameCount = 0;
static final int xOffset = 30, yOffset = 30;
public MyInternalFrame() {
super("文檔 #" + (++openFrameCount),
true, // resizable
true, // closable
true, // maximizable
true); // iconifiable
// 內容:簡單文字區
getContentPane().add(new JTextArea(5, 20));
setSize(300, 300);
// 錯開顯示位置避免重疊
setLocation(xOffset * openFrameCount, yOffset * openFrameCount);
}
}
| 屬性 | 建構式參數 | 效果 |
|---|---|---|
| 可調整大小 | resizable | 邊框可拖曳縮放 |
| 可關閉 | closable | 標題列顯示關閉按鈕 |
| 可最大化 | maximizable | 標題列顯示最大化按鈕,填滿桌面面板 |
| 可圖標化 | iconifiable | 標題列顯示最小化按鈕,縮為標題列圖示 |
LIVE_DRAG_MODE:拖曳時即時重繪內部視窗內容(流暢但消耗資源)OUTLINE_DRAG_MODE:拖曳時僅繪製輪廓框(預設,效能佳)| 操作 | 方法/事件 |
|---|---|
| 建立內部視窗 | new JInternalFrame(...) → desktop.add(frame) → frame.setVisible(true) |
| 置頂並聚焦 | frame.setSelected(true)(拋出 PropertyVetoException) |
| 最大化/還原 | frame.setMaximum(true/false) |
| 圖標化/還原 | frame.setIcon(true/false) |
| 關閉 | frame.dispose() 或使用者點關閉按鈕(預設 DISPOSE_ON_CLOSE) |
| 監聽狀態變更 | InternalFrameListener:internalFrameOpened/Closing/Closed/Iconified/Deiconified/Activated/Deactivated |
JavaFX 提供 Stage + Scene 原生支援多視窗,無需 MDI 模擬。Swing 專案維護期建議:
JPanel,以 CardLayout 或 JTabbedPane 切換,避免 MDI 複雜度。Docking Frames (Frau) 等第三方庫獲得現代停靠視窗體驗。