菜單在Swing中做了重要的改進並且更加的靈活——例如,我們可以在幾乎程序中任何地方使用他們,包括在面板和程序片中。語法同它們在老的AWT中是一樣的,並且這樣使出現在老AWT的在新的Swing也出現了:我們必須為我們的菜單艱難地編寫代碼,並且有一些不再作為資源支持菜單(其它事件中的一些將使它們更易轉換成其它的編程語言)。另外,菜單代碼相當的冗長,有時還有一些混亂。下面的方法是放置所有的關於每個菜單的信息到對象的二維數組裡(這種方法可以放置我們想處理的任何事物到數組裡),這種方法在解決這個問題方面領先了一步。這個二維數組被菜單所創建,因此它首先表示出菜單名,並在剩余的列中表示菜單項和它們的特性。我們會注意到數組列不必保持一致——只要我們的代碼知道將發生的一切事件,每一列都可以完全不同。
//: Menus.java // A menu-building system; also demonstrates // icons in labels and menu items. package c13.swing; import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Menus extends JPanel { static final Boolean bT = new Boolean(true), bF = new Boolean(false); // Dummy class to create type identifiers: static class MType { MType(int i) {} }; static final MType mi = new MType(1), // Normal menu item cb = new MType(2), // Checkbox menu item rb = new MType(3); // Radio button menu item JTextField t = new JTextField(10); JLabel l = new JLabel("Icon Selected", Faces.faces[0], JLabel.CENTER); ActionListener a1 = new ActionListener() { public void actionPerformed(ActionEvent e) { t.setText( ((JMenuItem)e.getSource()).getText()); } }; ActionListener a2 = new ActionListener() { public void actionPerformed(ActionEvent e) { JMenuItem mi = (JMenuItem)e.getSource(); l.setText(mi.getText()); l.setIcon(mi.getIcon()); } }; // Store menu data as "resources": public Object[][] fileMenu = { // Menu name and accelerator: { "File", new Character('F') }, // Name type accel listener enabled { "New", mi, new Character('N'), a1, bT }, { "Open", mi, new Character('O'), a1, bT }, { "Save", mi, new Character('S'), a1, bF }, { "Save As", mi, new Character('A'), a1, bF}, { null }, // Separator { "Exit", mi, new Character('x'), a1, bT }, }; public Object[][] editMenu = { // Menu name: { "Edit", new Character('E') }, // Name type accel listener enabled { "Cut", mi, new Character('t'), a1, bT }, { "Copy", mi, new Character('C'), a1, bT }, { "Paste", mi, new Character('P'), a1, bT }, { null }, // Separator { "Select All", mi,new Character('l'),a1,bT}, }; public Object[][] helpMenu = { // Menu name: { "Help", new Character('H') }, // Name type accel listener enabled { "Index", mi, new Character('I'), a1, bT }, { "Using help", mi,new Character('U'),a1,bT}, { null }, // Separator { "About", mi, new Character('t'), a1, bT }, }; public Object[][] optionMenu = { // Menu name: { "Options", new Character('O') }, // Name type accel listener enabled { "Option 1", cb, new Character('1'), a1,bT}, { "Option 2", cb, new Character('2'), a1,bT}, }; public Object[][] faceMenu = { // Menu name: { "Faces", new Character('a') }, // Optinal last element is icon { "Face 0", rb, new Character('0'), a2, bT, Faces.faces[0] }, { "Face 1", rb, new Character('1'), a2, bT, Faces.faces[1] }, { "Face 2", rb, new Character('2'), a2, bT, Faces.faces[2] }, { "Face 3", rb, new Character('3'), a2, bT, Faces.faces[3] }, { "Face 4", rb, new Character('4'), a2, bT, Faces.faces[4] }, }; public Object[] menuBar = { fileMenu, editMenu, faceMenu, optionMenu, helpMenu, }; static public JMenuBar createMenuBar(Object[] menuBarData) { JMenuBar menuBar = new JMenuBar(); for(int i = 0; i < menuBarData.length; i++) menuBar.add( createMenu((Object[][])menuBarData[i])); return menuBar; } static ButtonGroup bgroup; static public JMenu createMenu(Object[][] menuData) { JMenu menu = new JMenu(); menu.setText((String)menuData[0][0]); menu.setMnemonic( ((Character)menuData[0][1]).charValue()); // Create redundantly, in case there are // any radio buttons: bgroup = 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])); } return menu; } static public JMenuItem createMenuItem(Object[] data) { JMenuItem m = null; MType type = (MType)data[1]; if(type == mi) m = new JMenuItem(); else if(type == cb) m = new JCheckBoxMenuItem(); else if(type == rb) { m = new JRadioButtonMenuItem(); bgroup.add(m); } m.setText((String)data[0]); m.setMnemonic( ((Character)data[2]).charValue()); m.addActionListener( (ActionListener)data[3]); m.setEnabled( ((Boolean)data[4]).booleanValue()); if(data.length == 6) m.setIcon((Icon)data[5]); return m; } Menus() { setLayout(new BorderLayout()); add(createMenuBar(menuBar), BorderLayout.NORTH); JPanel p = new JPanel(); p.setLayout(new BorderLayout()); p.add(t, BorderLayout.NORTH); p.add(l, BorderLayout.CENTER); add(p, BorderLayout.CENTER); } public static void main(String args[]) { Show.inFrame(new Menus(), 300, 200); } } ///:~
這個程序的目的是允許程序設計者簡單地創建表格來描述每個菜單,而不是輸入代碼行來建立菜單。每個菜單都產生一個菜單,表格中的第一列包含菜單名和鍵盤快捷鍵。其余的列包含每個菜單項的數據:字符串存在在菜單項中的位置,菜單的類型,它的快捷鍵,當菜單項被選中時被激活的動作接收器及菜單是否被激活等信息。如果列開始處是空的,它將被作為一個分隔符來處理。
為了預防浪費和冗長的多個Boolean創建的對象和類型標志,以下的這些在類開始時就作為static final被創建:bT和bF描述Booleans和啞類MType的不同對象描述標准的菜單項(mi),復選框菜單項(cb),和單選鈕菜單項(rb)。請記住一組Object可以擁有單一的Object句柄,並且不再是原來的值。
這個程序例子同樣展示了JLables和JMenuItems(和它們的衍生事物)如何處理圖標的。一個圖標經由它的構建器置放進JLable中並當對應的菜單項被選中時被改變。
菜單條數組控制處理所有在文件菜單清單中列出的,我們想顯示在菜單條上的文件菜單。我們通過這個數組去使用createMenuBar(),將數組分類成單獨的菜單數據數組,再通過每個單獨的數組去創建菜單。這種方法依次使用菜單數據的每一行並以該數據創建JMenu,然後為菜單數據中剩下的每一行調用createMenuItem()方法。最後,createMenuItem()方法分析菜單數據的每一行並且判斷菜單類型和它的屬性,再適當地創建菜單項。終於,像我們在菜單構建器中看到的一樣,從表示createMenuBar(menuBar)的表格中創建菜單,而所有的事物都是采用遞歸方法處理的。
這個程序不能建立串聯的菜單,但我們擁有足夠的知識,如果我們需要的話,隨時都能增加多級菜單進去。