有没有办法检测 JPopupMenu 中的当前选择(SelectionModel ChangeListener 不起作用)

Is there a way to detect current selection in JPopupMenu (SelectionModel ChangeListener doesn't work)

我想检测 JPopupMenu 中的选择何时发生变化。不是在单击菜单项时,而是在选择(准备)菜单项时。用更简单的话,我想检测这个:

应该起作用的是将 ChangeListener 添加到它的 SelectionModel,但它似乎没有响应选择事件:

public class PopupSelection extends JFrame {
    private static final long serialVersionUID = 363879723515243543L;

    public PopupSelection() {
        super("something");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLayout(new FlowLayout());

        JLabel label = new JLabel("right click me");

        JPopupMenu menu = new JPopupMenu();
        menu.getSelectionModel().addChangeListener(System.out::println);

        JMenuItem menuItem1 = new JMenuItem("Item1");
        JMenuItem menuItem2 = new JMenuItem("Item2");
        JMenuItem menuItem3 = new JMenuItem("Item3");
        menu.add(menuItem1);
        menu.add(menuItem2);
        menu.add(menuItem3);

        label.setComponentPopupMenu(menu);

        getContentPane().add(label);
        setSize(400, 400);
        setLocationRelativeTo(null);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new PopupSelection().setVisible(true));
    }
}

我尝试的第二件事是 PropertyChangeListener,但它也不起作用(未检测到此特定事件):

menu.addPropertyChangeListener(System.out::println);

我知道可以为每个 JMenuItem 添加一个 ChangeListener 并且每次迭代 JPopupMenu 的所有组件以找到选择了哪个组件,但是这个不是我想遵循的解决方案,因为它会在我的代码中增加不必要的复杂性。

那么,有没有办法检测选择?

如果用户使用箭头按钮更改菜单选择时 XY problem, my final goal is to increase/decrease this scrollbar 正确:

对项目的按钮模型使用更改侦听器。这是解决方案:

import java.awt.Component;
import java.awt.FlowLayout;
import java.util.stream.Stream;

import javax.swing.AbstractButton;
import javax.swing.ButtonModel;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/**
 * <code>PopupSelection</code>.
 */
public class PopupSelection extends JFrame {
    private static final long serialVersionUID = 363879723515243543L;

    public PopupSelection() {
        super("something");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLayout(new FlowLayout());

        JLabel label = new JLabel("right click me");

        JPopupMenu menu = new MyPopupMenu();
        menu.getSelectionModel().addChangeListener(System.out::println);

        JMenuItem menuItem1 = new JMenuItem("Item1");
        JMenuItem menuItem2 = new JMenuItem("Item2");
        JMenuItem menuItem3 = new JMenuItem("Item3");
        menu.add(menuItem1);
        menu.add(menuItem2);
        menu.add(menuItem3);

        label.setComponentPopupMenu(menu);

        getContentPane().add(label);
        setSize(400, 400);
        setLocationRelativeTo(null);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new PopupSelection().setVisible(true));
    }

    private static class MyPopupMenu extends JPopupMenu {

        private final ChangeListener listener = this::changed;

        @Override
        protected void addImpl(Component comp, Object constraints, int index) {
            super.addImpl(comp, constraints, index);
            if (comp instanceof AbstractButton) {
                ((AbstractButton) comp).getModel().addChangeListener(listener);
            }
        }

        @Override
        public void remove(int index) {
            Component comp = getComponent(index);
            if (comp instanceof AbstractButton) {
                ((AbstractButton) comp).getModel().removeChangeListener(listener);
            }
            super.remove(index);
        }

        private void changed(ChangeEvent e) {
            ButtonModel model = (ButtonModel) e.getSource();
            AbstractButton selected = Stream.of(getComponents()).filter(AbstractButton.class::isInstance)
                    .map(AbstractButton.class::cast)
                    .filter(b -> b.getModel().isArmed() && b.getModel() == model).findAny().orElse(null);
            setSelected(selected);
        }
    }
}

不是向每个 JMenuItem 添加 ChangeListener,而是向 MenuSelectionManager 添加 ChangeListener

MenuSelectionManager.defaultManager().addChangeListener(e -> {
  Object o = e.getSource();
  if (o instanceof MenuSelectionManager) {
    MenuSelectionManager m = (MenuSelectionManager) o;
    printMenuElementArray(m.getSelectedPath());
  }
});

PopupSelection2.java

import java.awt.*;
import javax.swing.*;

public class PopupSelection2 extends JFrame {
  public PopupSelection2() {
    super("something");
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setLayout(new FlowLayout());

    JLabel label = new JLabel("right click me");

    JPopupMenu menu = new JPopupMenu();
    menu.getSelectionModel().addChangeListener(System.out::println);

    JMenuItem menuItem1 = new JMenuItem("Item1");
    JMenuItem menuItem2 = new JMenuItem("Item2");
    JMenuItem menuItem3 = new JMenuItem("Item3");
    menu.add(menuItem1);
    menu.add(menuItem2);
    menu.add(menuItem3);

    label.setComponentPopupMenu(menu);

    MenuSelectionManager.defaultManager().addChangeListener(e -> {
      Object o = e.getSource();
      if (o instanceof MenuSelectionManager) {
        MenuSelectionManager m = (MenuSelectionManager) o;
        printMenuElementArray(m.getSelectedPath());
      }
    });

    getContentPane().add(label);
    setSize(400, 400);
    setLocationRelativeTo(null);
  }

  // @see javax/swing/MenuSelectionManager.java
  private static void printMenuElementArray(MenuElement[] path) {
    System.out.println("Path is(");
    for (int i = 0, j = path.length; i < j ; i++) {
      for (int k = 0; k <= i; k++) {
        System.out.print("  ");
      }
      MenuElement me = path[i];
      if (me instanceof JMenuItem) {
        System.out.println(((JMenuItem)me).getText() + ", ");
      } else if (me instanceof JMenuBar) {
        System.out.println("JMenuBar, ");
      } else if (me instanceof JPopupMenu) {
        System.out.println("JPopupMenu, ");
      } else if (me == null) {
        System.out.println("NULL , ");
      } else {
        System.out.println("" + me + ", ");
      }
    }
    System.out.println(")");
  }

  public static void main(String[] args) {
    SwingUtilities.invokeLater(() -> new PopupSelection2().setVisible(true));
  }
}