当前位置: 首页 > 面试题库 >

JMenuBar SelectionModel ChangeListener仅触发一次

姚淳
2023-03-14
问题内容

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

但是,附加的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包的肠子深处完成的,即由包私有类KeyboardManager进行。无法挂接自定义管理器(可以实现为处理未显示的菜单栏)。

但是,在链的另一端,我们可以进行干预。基本上有两个选项,两个都属于菜单栏:

  • (极其肮脏的把戏!)重写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);

自定义menuBar:

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;
    }

}


 类似资料:
  • 我在Android应用程序中遇到了Firebase实时数据库的问题。 我正在尝试的是实时更新列表。但更新只适用于应用程序启动。它不会自动更新。所以,我需要重新启动的应用程序,每当想要更新。 有缺少的部分吗?谢谢你的帮助。谢谢 问题:监听器仅在应用程序重启时触发了方法。数据正确。但是监听器在那之后就不起作用了。 代码:

  • 我有一个Azure Web作业,其中包含一些blob触发的函数。我通过门户上的添加作业对话框将其上传到Azure,并将其设置为“连续运行”预期的行为是,任何时候将blob添加 /modifiedblob中指定的容器都会触发调用相应的函数。但是这不会发生。 触发这些功能的唯一方法(在上传blobs之后)是停止web作业并再次重启它。 每次我重启作业时,这些功能似乎都被触发了,而且只触发了一次。任何后

  • 问题内容: 这是我遇到的问题-我需要每2小时触发一次作业构建,但前提是存在git commit(如果没有活动,则跳过)。我可以单独解决它们,但不确定如何一起解决- 有人有什么好主意吗?我唯一能想到的是一个cron作业,该作业每2小时检查一次,并且在此期间是否有git commit,请手动触发作业,但这看起来并不那么优雅。 任何好的想法表示赞赏。 问题答案: 将詹金斯设置为每2小时(0 * / 2

  • 我想让我的discord机器人每隔5秒发送一条消息,告诉我如果我最近在聊天中输入了什么,你已经说了。例如,如果我发送这些消息(如下)Hi(消息1,一秒钟过去了)Hi(消息2,两秒钟过去了)Hi(消息3,三秒钟过去了)Hi(消息4,5秒钟过去了)(机器人说):你说话了(记住,机器人只说了一次,不是四次) 但是,到目前为止,它处于空闲状态,不发送任何消息。我没有收到任何错误,并且bot本身运行并且在线

  • 我有一个包含复杂输入(一些图表数据)的子组件。在获得 API 响应后,我通过 Angular 5 中的 @Input() 装饰器从父组件发送此图表数据。因此,每次在父组件上更改图表时,我都需要根据API响应同步图表,因此,OnChange生命周期。但不幸的是,我无法让它正常工作。我尝试使用基本数字类型设置虚拟输入字段并递增它,但徒劳无功。这是我的实现: 子图表组件: 父组件html: 父组件: P

  • 问题内容: 我在集群环境中将Quartz Scheduler用作Spring bean。 我有一些用@NotConcurrent注释的作业,它们每个集群运行一次(即,仅在一个节点中,仅在一个线程中)。 现在,我需要在集群的每个节点上运行一项作业。我删除了@NotConcurrent批注,但是它仅在一台计算机上的每个线程上运行。它不会在其他节点上触发。 我应该用什么来注释作业? 示例:带注释的Job