JPopupMenu 必須在**滑鼠事件**中判斷 isPopupTrigger() 為真時顯示。不同平台觸發方式不同(Windows:右鍵釋放;macOS:Ctrl+左鍵或右鍵),因此**必須同時監聽 mousePressed 與 mouseReleased**。
public class Popup extends JPanel {
JPopupMenu popup = new JPopupMenu();
JTextField textField = new JTextField(10);
public Popup() {
add(textField);
ActionListener menuAction = e ->
textField.setText(((JMenuItem) e.getSource()).getText());
// 建立選單項
Stream.of("Hither", "Yon", "Afar", "Stay Here")
.forEach(name -> {
JMenuItem item = new JMenuItem(name);
item.addActionListener(menuAction);
popup.add(item);
if ("Afar".equals(name)) popup.addSeparator();
});
// 同一監聽器註冊到面板與文字欄位
MouseAdapter listener = new MouseAdapter() {
public void mousePressed(MouseEvent e) { showPopup(e); }
public void mouseReleased(MouseEvent e) { showPopup(e); }
private void showPopup(MouseEvent e) {
if (e.isPopupTrigger())
popup.show(e.getComponent(), e.getX(), e.getY());
}
};
addMouseListener(listener);
textField.addMouseListener(listener);
}
// main()...
}
mousePressed + mouseReleased 覆蓋跨平台觸發差異。e.getSource() 取得選單項文字,簡化邏輯。popup.addSeparator() 視覺分組。| 錯誤做法 | 後果 | 正確做法 |
|---|---|---|
| 只監聽 mouseClicked | macOS 無法觸發 | 監聽 pressed + released |
| 用 e.getButton() == MouseEvent.BUTTON3 判斷 | 跨平台失效 | 用 e.isPopupTrigger() |
| 在 ActionListener 中用 getSource() 強轉 JMenuItem | 若選單含非 JMenuItem 元件拋 ClassCastException | 加 instanceof 檢查或統一元件類型 |
showPopup() 中根據上下文重建 JPopupMenu 內容。item.setAccelerator(KeyStroke.getKeyStroke('H'))。new JMenuItem("Copy", new ImageIcon("copy.png"))。JMenu sub = new JMenu("More"); popup.add(sub); sub.add(new JMenuItem("Option"));。JPopupMenu 雖 API 簡單,但**跨平台觸發機制**是關鍵難點。掌握「雙事件 + isPopupTrigger() + 相對座標顯示」三步曲,即能實作健壯、符合原生體驗的右鍵選單。