【问题标题】:How to set an accelerator for a JMenu sub menu?如何为 JMenu 子菜单设置加速器?
【发布时间】:2012-10-16 07:47:20
【问题描述】:

我有一个用户请求将加速器添加到子菜单 (JMenu),这将允许用户按下快捷方式并让相应的子菜单“折叠”,显示其包含的菜单项。

我不记得每个人都见过这样的东西(Java 或任何其他语言)。我们的应用程序是使用 Swing 用 Ja​​va 编写的。我们有许多带有加速器的 JMenuItems 运行良好,但是当我尝试向 JMenu 添加加速器时出现以下异常:

java.lang.Error: setAccelerator() 没有为 JMenu 定义。请改用 setMnemonic()。

我尝试使用MenuDemo!代码来进一步试验一下。

这是我尝试过的:

//a submenu
menu.addSeparator();
submenu = new JMenu("A submenu");
submenu.setMnemonic(KeyEvent.VK_S);
submenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_U, InputEvent.CTRL_MASK));

最后一行是我添加的,导致异常。

我尝试了广泛的谷歌搜索,但我只能找到有关如何将加速器添加到 JMenuItem 的文章。

似乎 JMenu 本身不支持此功能。是否有任何解决方法来实现这种行为?

【问题讨论】:

    标签: java swing key-bindings jmenu accelerator


    【解决方案1】:

    另一个选项是覆盖加速器获取/设置并重现 JMenuItem 行为。然后 UI 将完成剩下的工作。

    重要的是触发属性更改并为加速器提供一致的获取/设置。此解决方案的优点是它还提供了快捷方式/加速器的可视化指示。

    这是一个小演示代码:

    import java.awt.event.KeyEvent;
    
    import javax.swing.JFrame;
    import javax.swing.JMenu;
    import javax.swing.JMenuBar;
    import javax.swing.JMenuItem;
    import javax.swing.KeyStroke;
    import javax.swing.SwingUtilities;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class TestMenu {
    
        protected void initUI() {
            JFrame frame = new JFrame(TestMenu.class.getSimpleName());
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            JMenuBar bar = new JMenuBar();
            JMenu topMenu = new JMenu("Top Menu");
            JMenu subMenu = new JMenu("Sub menu") {
                private KeyStroke accelerator;
    
                @Override
                public KeyStroke getAccelerator() {
                    return accelerator;
                }
    
                @Override
                public void setAccelerator(KeyStroke keyStroke) {
                    KeyStroke oldAccelerator = accelerator;
                    this.accelerator = keyStroke;
                    repaint();
                    revalidate();
                    firePropertyChange("accelerator", oldAccelerator, accelerator);
                }
            };
            subMenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, KeyEvent.CTRL_MASK));
            JMenuItem item1 = new JMenuItem("Item 1");
            JMenuItem item2 = new JMenuItem("Item 2");
            subMenu.add(item1);
            subMenu.addSeparator();
            subMenu.add(item2);
            topMenu.add(subMenu);
            bar.add(topMenu);
            frame.setJMenuBar(bar);
            frame.setSize(400, 300);
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (InstantiationException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (UnsupportedLookAndFeelException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    new TestMenu().initUI();
                }
            });
        }
    
    }
    

    【讨论】:

    • 也很酷——老太太Swing还是有惊喜的:-)
    • 谢谢,这解决了我的问题。如此简单,但您必须考虑一下:-)
    【解决方案2】:

    我认为这不可能。 但是你可以添加一个AbstractAction,它会模拟点击。

    submenu.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("control U"), "expand");
        submenu.getActionMap().put("expand", new AbstractAction("expand") {
            private static final long serialVersionUID = 1L;
    
            @Override
            public void actionPerformed(ActionEvent evt) {
                submenu.doClick();
            }
        }
    );
    

    我希望这也适用于你。

    【讨论】:

    • 我的第一个猜测是这行不通 - 但错了 :-) +1 - 可用性问题是没有视觉指示使用哪个 keyStroke
    • 就打开子菜单而言,它就像一个魅力,非常感谢。正如 kleopatra 所说,没有指标可以显示使用了哪个键绑定。是否有可能以某种方式添加此信息?那么,解决方案将是完美的:-)
    • 你唯一的选择是 afaik 要么玩弄字体,所以它看起来像 Accelerators 指示器,或者添加一个 JMenuItem 而不是 JMenu 并添加一个 PopUp 或其他选择...(但是然后你没有“真正的”菜单了)来自瑞士的问候 ;-)
    • @kleopatra 实际上,通过简单地覆盖 get/set 加速器并触发适当的属性更改事件,您可以获得击键指示并让 UI 完成其余的工作(另请参见下面的示例) .
    • @UrsBeeli 请参阅下面的答案,该答案还提供了击键的视觉反馈。
    猜你喜欢
    • 2013-03-11
    • 2012-05-26
    • 2015-10-24
    • 2012-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-09
    相关资源
    最近更新 更多