我正在尝试JMenuBar模拟Firefox和iTunes菜单栏的行为。行为:菜单栏最初是隐藏的。但是,当您按时Alt,会出现菜单栏(选择了第一项),而当您没有选择菜单项时,菜单栏会消失。我的想法是侦听选择更改JMenuBar通过ChangeListener其SelectionModel。
JMenuBar
Alt
ChangeListener
SelectionModel
但是,附加的SSCCE的行为不是所希望的。框架加载后,看JMenuBar不到。当您按时Alt,菜单栏出现,并选择了第一个菜单(由于WindowsLookAndFeel)。但是,随后的所有Alt按下均不会触发ChangeEvents。我不知道为什么…
WindowsLookAndFeel
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进行。无法挂接自定义管理器(可以实现为处理未显示的菜单栏)。
但是,在链的另一端,我们可以进行干预。基本上有两个选项,两个都属于菜单栏:
修改后的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; } }