问题
I have a user request to add an accelerator to a sub menu (JMenu) which would allow the user to press the short cut and have the corresponding sub menu "fold out", showing its contained menu items.
I don't recall every having seen something like this (either in Java or any other language). Our application is written in Java using Swing. We have a number of JMenuItems with accelerators that work well, but when I attempted to add an accelerator to JMenu I get the following exception:
java.lang.Error: setAccelerator() is not defined for JMenu. Use setMnemonic() instead.
I've tried to use the MenuDemo! code to experiment with this a bit further.
This is what I tried:
//a submenu
menu.addSeparator();
submenu = new JMenu("A submenu");
submenu.setMnemonic(KeyEvent.VK_S);
submenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_U, InputEvent.CTRL_MASK));
The last line is the one added by me, which causes the exception.
I've tried extensive googling but all I can find is articles on how to add accelerators to JMenuItem.
It seems the JMenu does not support this natively. Is there any workaround to achieve this behaviour?
回答1:
Another option is to override the accelerator get/set and reproduce the JMenuItem behaviour. Then the UI will do the rest of the job.
The important thing is to fire the property change and have a consistent get/set for the accelerator. The advantage of this solution is that it also provides a visual indication of the shortcut/accelerator.
Here is a small demo code:
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();
}
});
}
}
回答2:
I don't think that is possible just like that.
But what you could do is adding an AbstractAction
, which simulates a click.
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();
}
}
);
I hope this also works for you.
来源:https://stackoverflow.com/questions/12909851/how-to-set-an-accelerator-for-a-jmenu-sub-menu