手寫選單代碼冗長、重複、難維護。本範例採用 **「資料驅動」** 思想:將選單結構定義為二維陣列資料,由通用建構方法遞歸解析生成 UI,實現**結構與邏輯分離**。
// 單一選單定義:Object[][]
Object[][] fileMenu = {
// 第 0 行:選單名稱 + 快捷鍵
{ "File", 'F' },
// 後續行:項目名稱, 類型, 快捷鍵, 監聽器, 啟用狀態, [圖示]
{ "New", MType.MI, 'N', a1, true },
{ "Open", MType.MI, 'O', a1, true },
{ "Save", MType.MI, 'S', a1, false },
{ "Save As", MType.MI, 'A', a1, false },
{ null }, // 分隔符
{ "Exit", MType.MI, 'x', a1, true }
};
// 選單列陣列
Object[] menuBar = { fileMenu, editMenu, faceMenu, optionMenu, helpMenu };
static class MType { MType(int i) {} }
static final MType MI = new MType(1), // 一般項目
CB = new MType(2), // 複選框
RB = new MType(3); // 單選鈕
// 入口:建構整個選單列
static JMenuBar createMenuBar(Object[] menuBarData) {
JMenuBar mb = new JMenuBar();
for (Object data : menuBarData)
mb.add(createMenu((Object[][]) data));
return mb;
}
// 建構單一選單
static JMenu createMenu(Object[][] menuData) {
JMenu menu = new JMenu();
menu.setText((String) menuData[0][0]);
menu.setMnemonic(((Character) menuData[0][1]).charValue());
ButtonGroup group = new ButtonGroup(); // 單選鈕分組
for (int i = 1; i < menuData.length; i++) {
if (menuData[i][0] == null)
menu.add(new JSeparator());
else
menu.add(createMenuItem(menuData[i], group));
}
return menu;
}
// 建構選單項(多型工廠)
static JMenuItem createMenuItem(Object[] data, ButtonGroup group) {
JMenuItem item;
MType type = (MType) data[1];
if (type == MI) item = new JMenuItem();
else if (type == CB) item = new JCheckBoxMenuItem();
else { item = new JRadioButtonMenuItem(); group.add(item); }
item.setText((String) data[0]);
item.setMnemonic(((Character) data[2]).charValue());
item.addActionListener((ActionListener) data[3]);
item.setEnabled(((Boolean) data[4]).booleanValue());
if (data.length == 6) item.setIcon((Icon) data[5]); // 可選圖示
return item;
}
faceMenu 示範單選鈕搭配圖示,選中時同步更新標籤圖示。null 首元素自動轉為 JSeparator。Character 對象轉 char 設定助憶鍵。Boolean 控制初始可用性。ButtonGroup 確保互斥選擇。JMenu 作為子項。Object[],編譯期檢查。ResourceBundle。資料驅動選單建構是 Swing 開發中**關注點分離**的典範:宣告式資料描述「長什麼樣」,通用引擎負責「怎麼建」。此模式易於擴展、測試、外部化,適用於所有階層式 UI 元件的建構(工具列、樹狀目錄、屬性面板等)。