Swing 菜單層級結構:
JFrame
└─ JMenuBar
├─ JMenu "菜單 A"
│ ├─ JMenuItem "純文本" (Alt+1)
│ ├─ JMenuItem "圖標+文本" (Alt+B)
│ ├─ JMenuItem "純圖標" (Alt+D)
│ ├─ JSeparator
│ ├─ JRadioButtonMenuItem 組 (ButtonGroup)
│ ├─ JSeparator
│ ├─ JCheckBoxMenuItem 組
│ ├─ JSeparator
│ └─ JMenu "子菜單 S" (Alt+2)
│ ├─ JMenuItem "子項 1"
│ └─ JMenuItem "子項 2"
└─ JMenu "菜單 N"
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MenuDemo implements ActionListener, ItemListener {
private JTextArea output;
private final String NL = "\n";
public JMenuBar createMenuBar() {
JMenuBar bar = new JMenuBar();
// --- 菜單 A ---
JMenu menuA = new JMenu("(A)菜單");
menuA.setMnemonic(KeyEvent.VK_A);
bar.add(menuA);
// 純文本項 + 快捷鍵 Alt+1
JMenuItem item = new JMenuItem("(O)只有文本的菜單", KeyEvent.VK_O);
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1, ActionEvent.ALT_MASK));
item.addActionListener(this);
menuA.add(item);
// 圖標+文本
ImageIcon icon = createIcon("images/middle.gif");
item = new JMenuItem("(B)有圖標和文字的菜單", icon);
item.setMnemonic(KeyEvent.VK_B);
item.addActionListener(this);
menuA.add(item);
// 純圖標
item = new JMenuItem(icon);
item.setMnemonic(KeyEvent.VK_D);
item.addActionListener(this);
menuA.add(item);
menuA.addSeparator();
// 單選組
ButtonGroup group = new ButtonGroup();
JRadioButtonMenuItem rb = new JRadioButtonMenuItem("(R)使用radio的菜單");
rb.setSelected(true);
rb.setMnemonic(KeyEvent.VK_R);
group.add(rb);
rb.addActionListener(this);
menuA.add(rb);
rb = new JRadioButtonMenuItem("(d)另外一個radio菜單");
rb.setMnemonic(KeyEvent.VK_D);
group.add(rb);
rb.addActionListener(this);
menuA.add(rb);
menuA.addSeparator();
// 複選組
JCheckBoxMenuItem cb = new JCheckBoxMenuItem("(C)使用檢查盒的菜單");
cb.setMnemonic(KeyEvent.VK_C);
cb.addItemListener(this);
menuA.add(cb);
cb = new JCheckBoxMenuItem("(H)另外一個檢查盒");
cb.setMnemonic(KeyEvent.VK_H);
cb.addItemListener(this);
menuA.add(cb);
menuA.addSeparator();
// 子菜單
JMenu sub = new JMenu("(S)帶有子菜單");
sub.setMnemonic(KeyEvent.VK_S);
item = new JMenuItem("這是子菜單");
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_2, ActionEvent.ALT_MASK));
item.addActionListener(this);
sub.add(item);
sub.add(new JMenuItem("子菜單項")).addActionListener(this);
menuA.add(sub);
// --- 菜單 N ---
JMenu menuN = new JMenu("(N)第二個菜單");
menuN.setMnemonic(KeyEvent.VK_N);
bar.add(menuN);
return bar;
}
// --- 事件處理 ---
@Override
public void actionPerformed(ActionEvent e) {
output.append("動作: " + e.getActionCommand() + NL);
}
@Override
public void itemStateChanged(ItemEvent e) {
JCheckBoxMenuItem src = (JCheckBoxMenuItem) e.getSource();
output.append("複選: " + src.getText() + " -> " +
(e.getStateChange() == ItemEvent.SELECTED ? "選中" : "取消") + NL);
}
protected static ImageIcon createIcon(String path) {
java.net.URL url = MenuDemo.class.getResource(path);
return (url != null) ? new ImageIcon(url) : null;
}
// --- 主程序啟動 ---
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame f = new JFrame("MenuDemo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MenuDemo demo = new MenuDemo();
demo.output = new JTextArea(10, 40);
demo.output.setEditable(false);
f.setJMenuBar(demo.createMenuBar());
f.add(new JScrollPane(demo.output));
f.pack();
f.setVisible(true);
});
}
}
| 組件 | 用途 | 關鍵方法 |
|---|---|---|
| JMenuBar | 頂級菜單容器 | add(JMenu) |
| JMenu | 一級/子菜單 | setMnemonic, addSeparator, add(JMenuItem) |
| JMenuItem | 普通動作項 | setAccelerator, addActionListener |
| JRadioButtonMenuItem | 互斥單選項 | 需加入 ButtonGroup |
| JCheckBoxMenuItem | 獨立開關項 | addItemListener 監聽狀態變化 |
| KeyStroke | 鍵盤快捷鍵 | getKeyStroke(key, modifiers) |
/images/Action 接口(AbstractAction)封裝文本、圖標、快捷鍵、執行邏輯,菜單項與工具欄按鈕共享同一 Action 實例JPopupMenu + MouseListener 實現右鍵菜單