Java JMenuBar SelectionModel ChangeListener仅激发一次
我试图让我的Java JMenuBar SelectionModel ChangeListener仅激发一次,java,swing,jmenuitem,jmenubar,changelistener,Java,Swing,Jmenuitem,Jmenubar,Changelistener,我试图让我的JMenuBar模拟Firefox和iTunes菜单栏的行为。行为:菜单栏最初是隐藏的。但是,当您按下Alt时,菜单栏会出现(选中第一个项目),而当您没有选择菜单项时,菜单栏会消失。我的想法是通过其SelectionModel上的changelister来监听对JMenuBar的选择更改 但是,附着的对象的行为不符合要求。加载帧时,JMenuBar不可见。当您按下Alt时,菜单栏将显示选定的第一个菜单(感谢WindowsLookAndFeel)。但是,每次按下Alt键都不会触发任何C
JMenuBar
模拟Firefox和iTunes菜单栏的行为。行为:菜单栏最初是隐藏的。但是,当您按下Alt
时,菜单栏会出现(选中第一个项目),而当您没有选择菜单项时,菜单栏会消失。我的想法是通过其SelectionModel
上的changelister
来监听对JMenuBar
的选择更改
但是,附着的对象的行为不符合要求。加载帧时,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;
}
但是,每次按下Alt键都不会触发任何ChangeEvents。我不能
找出原因
- ChangeListener从SelectionModel、鼠标或按键事件触发事件,这些事件是预期的
- 您可以从ChangeListener模拟事件,例如在菜单上重置选择(将其改为将焦点移动到JTextField)
- 还有可访问的其他侦听器,它们可以正确地触发自己的事件
看起来菜单栏一旦被选中,就永远不会取消选中。不确定这是不是一个bug 直接收听MenuSelectionManager可能是一个更好的主意,因为在那里,您可以随时收到有关菜单选择的所有更改的通知。需要一些逻辑来过滤掉与菜单栏无关的内容,类似于:
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完成的。无法挂接自定义管理器(可以实现自定义管理器来处理未显示的菜单栏)
然而,在链条的另一端,我们可以干涉。基本上有两个选项,都是菜单栏的子类:
- (非常肮脏的把戏!)覆盖显示总是返回true。我已经看过了,但不能真正推荐,因为可能有副作用,我不知道
- 一个略显肮脏的技巧:添加一个隐藏的属性,并实现getPreferredSize以在隐藏时返回0高度。肮脏是它依赖于根面板布局,尊重优先高度
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;
}
}
我没有得到指定的行为。嗯。。。或者我不明白你的意思?@kleopatra 1。我没有回答什么问题,有什么答案吗。玩可接受的事件3。模拟结果表明,JMenuBar总是从API中实现的方法返回非_期望值,可能是bug,可能是feature,4。只有它的子级(例如JMenu)可以这样做,jMenuBar1.setVisible(jMenu1.isSelected());可从ButtonModel EDIT访问(已启用,其余事件可从MenuListener访问),5。我必须测试PropertyChangeListener中的事件,@kleopatra PropertyChangeListener-->可能是,但由于woodoo被包装成布尔值(PropertyChangeListener触发相反的事件,并且在启动时永远看不到JMenuBar)@mKorbel,很抱歉,我在理解您时遇到了很多困难。非常好,这很有效。我自己永远也找不到
MenuSelectionManager
。但是,我仍然想知道为什么直接从JMenuBar
上选择管理器上的ChangeListener
不起作用。但它起作用了,所以我很高兴!谢谢。由于某种原因,这种方法会禁用菜单项加速器。尽管setAccelerator
的文档明确指出:“请注意,键入键盘加速器时,无论菜单当前是否显示,它都会工作。”但现在不是这样……如果添加以下代码:jMenuItem2.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F,java.awt.event.InputEvent.CTRL_MASK));jMenuItem2.setText(“查找”);jMenuItem2.addActionListener(新java.awt.event.ActionListener(){public void actionPerformed(java.awt.event.ActionEvent evt){String what=JOptionPane.showInputDialog(MenuBarTest.this),“搜索什么?”;System.out.println(what);}});
到上面的SSCE加速器设置为ctrl+f,只有在菜单栏可见时才有效。嗯……你是说如果菜单栏不可见,加速器键不起作用吗?不幸的是,这是另一个隐藏问题(很抱歉,我需要想出一个解决办法)
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;
}
}