在 Swing 應用中,JList 是常用的列表元件。使用者直覺期望**雙擊項目觸發動作**(如開啟詳情、編輯、播放)。本文示範如何透過 MouseListener 精準捕獲雙擊事件,取得選中項目索引與值。
MouseAdapter 匿名內部類(或 Lambda,Java 8+)。mouseClicked(MouseEvent e)。e.getClickCount() == 2 確認為雙擊。e.getSource() 取得 JList 參考。list.getSelectedIndex() 取得索引(-1 表無選取)。list.getModel().getElementAt(index) 取得項目值。list.addMouseListener(listener)。import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class JListDoubleClickDemo {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("JList 雙擊事件示範");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
// 建立模型與資料
DefaultListModel model = new DefaultListModel<>();
model.addElement("蘋果 🍎");
model.addElement("香蕉 🍌");
model.addElement("橘子 🍊");
model.addElement("葡萄 🍇");
model.addElement("西瓜 🍉");
JList list = new JList<>(model);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.setFont(new Font("微軟正黑體", Font.PLAIN, 18));
list.setFixedCellHeight(36);
// 滾動面板
JScrollPane scrollPane = new JScrollPane(list);
frame.add(scrollPane, BorderLayout.CENTER);
// 狀態列
JLabel status = new JLabel("雙擊任一項目查看詳情");
status.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
frame.add(status, BorderLayout.SOUTH);
// 雙擊監聽器
list.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) { // 確認為雙擊
int index = list.getSelectedIndex();
if (index >= 0) {
String item = list.getModel().getElementAt(index);
status.setText("雙擊選中:" + item + " (索引 " + index + ")");
// 這裡可觸發實際業務:開啟編輯對話框、播放媒體、導航詳情頁等
JOptionPane.showMessageDialog(frame,
"您雙擊了:" + item,
"項目詳情",
JOptionPane.INFORMATION_MESSAGE);
}
}
}
});
frame.setSize(300, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
雙擊時,選取狀態**已在第一次點擊時更新**,因此 getSelectedIndex() 直接可用。但若需區分「單擊選取」與「雙擊動作」,可在 mousePressed 記錄時間戳自行判斷。
list.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
int idx = list.getSelectedIndex();
if (idx >= 0) handleDoubleClick(list.getModel().getElementAt(idx), idx);
}
}
});
// 或使用工具方法
MouseListener doubleClickListener = e -> {
if (e.getClickCount() == 2) {
JList> src = (JList>) e.getSource();
int idx = src.getSelectedIndex();
if (idx >= 0) handleDoubleClick(src.getModel().getElementAt(idx), idx);
}
};
list.addMouseListener(doubleClickListener);
if (e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton(e)) {
// 僅處理左鍵雙擊
}
if (e.getClickCount() == 2) {
int idx = list.getSelectedIndex();
if (idx >= 0) {
String item = list.getModel().getElementAt(idx);
// 耗時操作放入背景執行緒
new SwingWorker() {
@Override protected Void doInBackground() {
heavyOperation(item); // 網路請求、資料庫查詢、檔案讀取
return null;
}
@Override protected void done() {
// 回到 EDT 更新 UI
status.setText("處理完成:" + item);
}
}.execute();
}
}
若只需響應「選取變更」而非「雙擊」,使用 ListSelectionListener 更語義化:
list.addListSelectionListener(e -> {
if (!e.getValueIsAdjusting()) { // 避免拖曳選取時多次觸發
int idx = list.getSelectedIndex();
if (idx >= 0) onSelectionChange(list.getModel().getElementAt(idx));
}
});
| 現象 | 原因 | 解決 |
|---|---|---|
| 雙擊無反應 | 未註冊監聽器、或被其他元件攔截 | 確認 addMouseListener、檢查佈局層級 |
| 單擊也觸發 | 未判斷 getClickCount() == 2 | 嚴格檢查點擊次數 |
| 取得錯誤索引 | 在 mousePressed 取值,選取尚未更新 | 使用 mouseClicked 或 mouseReleased |
| 重複觸發 | 多次註冊同一監聽器 | 註冊前移除舊監聽器、或單例模式 |
MouseAdapter + mouseClicked + getClickCount() == 2 是標準雙擊檢測模式。getSource() → JList → getSelectedIndex() → getModel().getElementAt() 完整取值鏈。SwingWorker 將耗時業務移出 EDT,保持 UI 流暢。MouseListener,選取變更用 ListSelectionListener。