標題稱「鼠標事件監聽」不準確。ActionListener 監聽語義動作事件:按鈕點擊、菜單選擇、回車提交、定時器觸發等。鼠標移動/拖拽/進出屬於MouseListener/MouseMotionListener。本文以ActionListener為切入點,剖析AWT/Swing事件委派模型。
package java.awt.event;
@FunctionalInterface // JDK 8+ 單抽象方法,支持Lambda
public interface ActionListener extends EventListener {
/** 發生動作時調用 */
void actionPerformed(ActionEvent e);
}
關鍵點:
ActionEvent攜帶:事件源getSource()、命令字符串getActionCommand()、修飾鍵getModifiers()、時間戳getWhen()Button、JButton、JMenuItem、Timer 等組件ActionEventsource.addActionListener(listener) 將監聽器加入事件源內部列表listener.actionPerformed(event)import java.awt.*;
import java.awt.event.*;
public class ActionListenerDemoV1 extends Frame {
public static void main(String[] args) {
Frame f = new Frame("ActionListener Demo");
f.setLayout(new FlowLayout(FlowLayout.LEFT, 20, 5));
Button btnClick = new Button("單擊");
Button btnDouble = new Button("雙擊"); // 注意:Button無雙擊事件,需自行實現
// 匿名內部類
btnClick.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("單擊事件觸發,來源: " + e.getActionCommand());
}
});
f.add(btnClick);
f.add(btnDouble);
f.pack();
f.setVisible(true);
}
}
版本3:方法引用 (最簡潔)
public class Handler { public void onClick(ActionEvent e) { System.out.println("方法引用處理: " + e.getActionCommand()); } } // 註冊 btnClick.addActionListener(new Handler()::onClick);進階:事件命令模式與Action接口
Swing提供
Action接口(擴展ActionListener),封裝「動作」的完整元數據:Action saveAction = new AbstractAction("保存", new ImageIcon("save.png")) { @Override public void actionPerformed(ActionEvent e) { saveFile(); } // 可動態控制啟用/禁用、圖標、工具提示 // putValue(Action.SHORT_DESCRIPTION, "保存文件 Ctrl+S"); // putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("ctrl S")); }; // 一處定義,多處復用 JButton saveBtn = new JButton(saveAction); JMenuItem saveMenu = new JMenuItem(saveAction); JToolBar toolBar = new JToolBar(); toolBar.add(saveAction); // 自動創建按鈕優勢:集中管理動作狀態(啟用/禁用/圖標/快捷鍵),一處修改全局生效,符合「命令模式」。
常見誤區與最佳實踐
| 誤區 | 正確認知 | 最佳實踐 |
|---|---|---|
| 在actionPerformed執行耗時操作 | 阻塞EDT(Event Dispatch Thread)導致界面凍結 | 提交至線程池/CompletableFuture/SwingWorker異步執行 |
| 匿名類持有外部對象導致內存洩漏 | Frame關閉後監聽器未移除,GC無法回收 | 使用WeakReference或組件銷毀時removeActionListener |
| 混淆ActionEvent與MouseEvent | 語義不同:動作vs坐標/按鍵狀態 | 按鈕用ActionListener,繪圖/拖拽用MouseListener |
| 重複註冊同一監聽器 | 點擊一次觸發多次 | 註冊前remove,或用標志位防重 |
// 錯誤:在監聽器中直接耗時操作
btn.addActionListener(e -> {
heavyTask(); // 界面卡死
});
// 正確:SwingWorker (JDK 6+)
btn.addActionListener(e -> {
new SwingWorker<Void, Void>() {
@Override protected Void doInBackground() {
heavyTask(); // 後台線程
return null;
}
@Override protected void done() {
updateUI(); // 回EDT更新界面
}
}.execute();
});
// 現代:CompletableFuture + SwingUtilities
btn.addActionListener(e ->
CompletableFuture.runAsync(this::heavyTask)
.thenRun(() -> SwingUtilities.invokeLater(this::updateUI))
);
Java事件模型體現觀察者模式與命令模式的結合。ActionListener作為入門級監聽器,掌握其註冊機制、事件對象、EDT約束、Action封裝四大核心,並熟練運用Lambda/方法引用/SwingWorker現代寫法,是構建響應式GUI應用的基石。Swing雖非主流,但其事件模型思想廣泛應用於Android、JavaFX、Web框架,學透一處,處處通透。