Java GUI 事件處理遵循「委派事件模型」:事件源 → 事件對象 → 事件監聽器 → 事件處理器。標準四步驟:
addActionListener)actionPerformed)public class ButtonListener extends JFrame implements ActionListener {
JButton ok, cancel, exit;
public ButtonListener(String title) {
super(title);
setLayout(new FlowLayout());
ok = new JButton("確定");
cancel = new JButton("取消");
exit = new JButton("退出");
// 組件註冊 this 作為監聽器
ok.addActionListener(this);
cancel.addActionListener(this);
exit.addActionListener(this);
add(ok); add(cancel); add(exit);
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == ok) System.out.println("確定");
else if (e.getSource() == cancel) System.out.println("取消");
else if (e.getSource() == exit) System.exit(0);
}
public static void main(String[] args) {
new ButtonListener("事件演示").setVisible(true);
}
}
優點:代碼集中;缺點:類職責不清、多組件邏輯耦合在一個方法
// 監聽器類
class MyListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if ("確定".equals(cmd)) System.out.println("確定");
else if ("取消".equals(cmd)) System.out.println("取消");
else if ("退出".equals(cmd)) System.exit(0);
}
}
// 使用
public class ButtonListener2 extends JFrame {
public ButtonListener2(String title) {
super(title);
setLayout(new FlowLayout());
JButton ok = new JButton("確定");
JButton cancel = new JButton("取消");
JButton exit = new JButton("退出");
// 每個組件可共用或單獨監聽器
ok.addActionListener(new MyListener());
cancel.addActionListener(new MyListener());
exit.addActionListener(new MyListener());
add(ok); add(cancel); add(exit);
}
}
優點:職責分離、可復用;缺點:需傳遞組件引用訪問 UI 狀態
// 封裝動作邏輯
class MenuAction extends AbstractAction {
public MenuAction(String name) {
putValue(NAME, name); // 設置顯示名稱
}
@Override
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
JOptionPane.showMessageDialog(null, "執行: " + cmd);
}
}
// 構建菜單
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("文件");
fileMenu.add(new JMenuItem(new MenuAction("打開")));
fileMenu.add(new JMenuItem(new MenuAction("保存")));
menuBar.add(fileMenu);
frame.setJMenuBar(menuBar);
優點:Action 封裝名稱、圖標、快捷鍵、啟用狀態、邏輯,菜單/工具欄/按鈕共享
// 動態 Action 加載器
class DynamicAction extends AbstractAction {
private String actionClassName;
private Action cachedAction;
public DynamicAction(String actionClassName) {
this.actionClassName = actionClassName;
}
@Override
public void actionPerformed(ActionEvent e) {
Action action = getAction(actionClassName);
if (action != null) action.actionPerformed(e);
}
private Action getAction(String className) {
if (cachedAction != null) return cachedAction;
try {
Class> clazz = Class.forName(className);
cachedAction = (Action) clazz.newInstance();
return cachedAction;
} catch (Exception ex) {
return null;
}
}
}
// Action 接口與實現
interface Action { void execute(); }
class B1 implements Action { public void execute() { JOptionPane.showMessageDialog(null, "B1"); } }
class B2 implements Action { public void execute() { JOptionPane.showMessageDialog(null, "B2"); } }
class B3 implements Action { public void execute() { JOptionPane.showMessageDialog(null, "B3"); } }
// 工具欄動態綁定
JToolBar toolBar = new JToolBar();
String[] buttons = {"B1", "B2", "B3"};
for (String name : buttons) {
JButton btn = new JButton(name);
btn.addActionListener(new DynamicAction(name));
toolBar.add(btn);
}
優點:極度解耦、按名稱動態加載、支持插件化擴展;缺點:反射性能、類名硬編碼
| 方法 | 說明 |
|---|---|
getSource() | 事件源組件對象 |
getActionCommand() | 組件命令字符串(按鈕文本) |
getWhen() | 事件發生時間戳 |
getModifiers() | 修飾鍵狀態(Shift、Ctrl、Alt) |
getID() | 事件類型 ID |
| 接口 | 事件類型 | 典型組件 |
|---|---|---|
| ActionListener | 動作事件(點擊、回車) | Button、MenuItem、TextField |
| MouseListener | 鼠標事件(點擊、進入、退出) | 所有 Component |
| MouseMotionListener | 鼠標移動/拖拽 | 所有 Component |
| KeyListener | 鍵盤事件 | 焦點組件 |
| FocusListener | 焦點獲得/失去 | 所有 Component |
| WindowListener | 窗口事件 | Window/Frame |
| ItemListener | 項目狀態改變 | Checkbox、Choice、List |
| AdjustmentListener | 滾動條調整 | Scrollbar |
為避免實現接口所有方法,使用適配器類:
// 匿名內部類 + 適配器
component.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
// 僅處理點擊
}
});
// 或 Lambda (Java 8+)
button.addActionListener(e -> System.out.println("點擊: " + e.getActionCommand()));
SwingWorker 異步執行,完成後用 SwingUtilities.invokeLater 更新 UIremoveListener,防止內存洩漏Java 事件處理機制靈活多樣,從基礎的 ActionListener 到 AbstractAction 封裝,再到反射動態分發,每種模式解決不同複雜度的解耦需求。掌握四大模式優缺點、EDT 規則、適配器簡化與現代 Lambda 用法,能構建清晰、可維護、高性能的 Swing/JavaFX 應用。