java - JMenuBar SelectionModel ChangeListener 仅触发一次

标签 java swing jmenuitem jmenubar changelistener

我正在尝试让我的 JMenuBar 来模拟 Firefox 和 iTunes 菜单栏的行为。行为:菜单栏最初是隐藏的。但是,当您按 Alt 时,菜单栏会出现(选择第一个项目),而当您没有选择菜单项时,菜单栏会消失。我的想法是通过 SelectionModel 上的 ChangeListener 监听对 JMenuBar 的选择更改。

但是,附加 SSCCE 的行为不符合预期。当框架加载时,JMenuBar 不可见。当您按 Alt 时,菜单栏会出现,并选择第一个菜单(感谢 WindowsLookAndFeel)。但是,随后按下的每个 Alt 都不会触发 ChangeEvents。我不明白为什么...

有人要发光吗?

public class MenuBarTest extends javax.swing.JFrame {

    public MenuBarTest() {
        initComponents();
        jMenuBar1.setVisible(false);
        jMenuBar1.getSelectionModel().addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                System.out.println(e.toString());
                jMenuBar1.setVisible(jMenuBar1.isSelected());
                System.out.println(jMenuBar1.isSelected());
                System.out.println(jMenuBar1.getSelectionModel().isSelected());
            }
        });
    }

    private void initComponents() {

        jMenuBar1 = new javax.swing.JMenuBar();
        jMenu1 = new javax.swing.JMenu();
        jMenuItem1 = new javax.swing.JMenuItem();
        jMenu2 = new javax.swing.JMenu();
        jMenuItem2 = new javax.swing.JMenuItem();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jMenu1.setText("File");
        jMenuItem1.setText("jMenuItem1");
        jMenu1.add(jMenuItem1);
        jMenuBar1.add(jMenu1);
        jMenu2.setText("Edit");
        jMenuItem2.setText("jMenuItem2");
        jMenu2.add(jMenuItem2);
        jMenuBar1.add(jMenu2);
        setJMenuBar(jMenuBar1);
        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGap(0, 400, Short.MAX_VALUE));
        layout.setVerticalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGap(0, 279, Short.MAX_VALUE));

        pack();
    }

    public static void main(String args[]) {
        try {
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) {
            ex.printStackTrace();
        }
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new NewClass().setVisible(true);
            }
        });
    }
    private javax.swing.JMenu jMenu1;
    private javax.swing.JMenu jMenu2;
    private javax.swing.JMenuBar jMenuBar1;
    private javax.swing.JMenuItem jMenuItem1;
    private javax.swing.JMenuItem jMenuItem2;
}

最佳答案

看起来菜单栏一旦被选择就永远不会被取消选择。不确定这是否是一个错误。

直接监听 MenuSelectionManager 可能是一个更好的主意,因为您可以在其中收到有关任何地方菜单选择的所有更改的通知。需要一些逻辑来过滤掉那些与menuBar无关的内容,类似于:

ChangeListener listener = new ChangeListener() {
    @Override
    public void stateChanged(ChangeEvent e) {
        MenuElement[] elements = MenuSelectionManager.defaultManager().getSelectedPath();
        jMenuBar1.setVisible(elements.length > 0 && elements[0] == jMenuBar1);
    }
};
MenuSelectionManager.defaultManager().addChangeListener(listener);

更新

隐藏菜单栏的一个重大缺点是其菜单项的加速器停止工作。原因是只有正在显示的组件的 componentInputMaps 被要求处理它们。这是在 swing 包的内部深处完成的,即通过包私有(private)类 KeyboardManager 完成的。无法 Hook 自定义管理器(可能会实现该管理器来处理未显示的菜单栏)。

不过,在链条的另一端,我们可以进行干预。基本上有两个选项,都是菜单栏的子类:

  • (极其肮脏的伎俩!)覆盖 isShowing 以始终返回 true。我已经看到这样做了,但不能真正推荐,因为可能有我不知道的副作用
  • 一个有点肮脏的技巧:添加一个隐藏属性并实现 getPreferredSize 以在隐藏时返回 0 高度。肮脏之处在于它对 RootPaneLayout 的依赖,尊重首选项高度......

修改后的ChangeListener:

bar.setHidden(true);
ChangeListener listener = new ChangeListener() {
    @Override
    public void stateChanged(ChangeEvent e) {
        MenuElement[] elements = MenuSelectionManager.defaultManager().getSelectedPath();
        bar.setHidden(!(elements.length >0 && elements[0] == bar));
    }
};
MenuSelectionManager.defaultManager().addChangeListener(listener);

自定义菜单栏:

public static class JHideableMenuBar extends JMenuBar {

    private boolean hidden;

    public void setHidden(boolean hidden) {
        if (this.hidden == hidden) return;
        this.hidden = hidden;
        revalidate();
    }

    @Override
    public Dimension getPreferredSize() {
        Dimension pref = super.getPreferredSize();
        if (hidden) {
            pref.height = 0;
        }
        return pref;
    }

}

关于java - JMenuBar SelectionModel ChangeListener 仅触发一次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18093773/

相关文章:

java - JMenu 栏项目(退出)不起作用

java - 更改字段字符串变量的值

java - 我想逐行显示 JTextArea 中的元素。如何实现这一目标?

java - 如何获取所有java核心类的列表?

java - 如何在 Swing 中显示每个菜单项的 JPanel?

java - 在文件夹中创建 .asm 文件作为 JMenuItems 并将它们添加到 JMenu,添加 ActionListener 事件

java - 在 [ 和 ] 之间分割

Java:根据动态生成的文本字段名称更改变量?

java - 将节点转换为对象

java - 读取字符串数组