应用程序全局键盘快捷键 - Java Swing

34

我希望为一个Java Swing应用程序创建一个应用程序范围的键盘快捷方式。循环所有组件并在每个组件上添加快捷方式会产生焦点相关的副作用,并且似乎是一种蛮力解决方案。

有没有更清晰的解决方案?

6个回答

43

对于每个窗口,使用JComponent.registerKeyboardAction,设置条件为WHEN_IN_FOCUSED_WINDOW。另外也可以使用:

JComponent.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(keyStroke, command);
JComponent.getActionMap().put(command,action);
根据registerKeyboardAction API文档中的描述。

这是我找到的最好、最简单的答案。我会点赞1000次。 - Alba Mendez
但是JFrame没有getInputMap或getActionMap方法。 - Can't Tell
@Can'tTell,“JFrame”不是“JComponent”。这些方法需要在顶层窗口中的组件上调用。 - Tom Hawtin - tackline
1
对于第一种解决方案的示例:window.getRootPane().registerKeyboardAction(save.getActionListeners()[0], KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_MASK), JComponent.WHEN_IN_FOCUSED_WINDOW);save 替换为您的 JButton 的名称,将 window 替换为您的 JFrame 的名称。 - Zezombye

21

安装自定义的KeyEventDispatcher。 KeyboardFocusManager类也是此功能的良好选择。

KeyEventDispatcher


13

对于像我这样想知道如何使用KeyEventDispatcher的人,这是我整理的一个例子。它使用HashMap来存储所有的全局动作,因为我不喜欢大型的if (key ==..) then .. else if (key ==..) then .. else if (key ==..) ..结构。

/** map containing all global actions */
private HashMap<KeyStroke, Action> actionMap = new HashMap<KeyStroke, Action>();

/** call this somewhere in your GUI construction */
private void setup() {
  KeyStroke key1 = KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.CTRL_DOWN_MASK);
  actionMap.put(key1, new AbstractAction("action1") {
    @Override
    public void actionPerformed(ActionEvent e) {
      System.out.println("Ctrl-A pressed: " + e);
    }
  });
  // add more actions..

  KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
  kfm.addKeyEventDispatcher( new KeyEventDispatcher() {

    @Override
    public boolean dispatchKeyEvent(KeyEvent e) {
      KeyStroke keyStroke = KeyStroke.getKeyStrokeForEvent(e);
      if ( actionMap.containsKey(keyStroke) ) {
        final Action a = actionMap.get(keyStroke);
        final ActionEvent ae = new ActionEvent(e.getSource(), e.getID(), null );
        SwingUtilities.invokeLater( new Runnable() {
          @Override
          public void run() {
            a.actionPerformed(ae);
          }
        } ); 
        return true;
      }
      return false;
    }
  });
}

使用SwingUtils.invokeLater()可能并非必须,但最好不要阻塞全局事件循环。


不是最简单的解决方案,但绝对是最优雅和可靠的。 - Haroldo_OK

6
当你有一个菜单时,可以为菜单项添加全局键盘快捷键:
    JMenuItem item = new JMenuItem(action);
    KeyStroke key = KeyStroke.getKeyStroke(
        KeyEvent.VK_R, KeyEvent.CTRL_DOWN_MASK);
    item.setAccelerator(key);
    menu.add(item);

3
一个简单的例子:
import java.awt.KeyboardFocusManager;
import java.awt.KeyEventDispatcher;
import java.awt.event.KeyEvent;

KeyboardFocusManager keyManager;

keyManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
keyManager.addKeyEventDispatcher(new KeyEventDispatcher() {
  
  @Override
  public boolean dispatchKeyEvent(KeyEvent e) {
    if (e.getID() == KeyEvent.KEY_PRESSED && e.getKeyCode() == 27) {
      System.out.println("Esc");
      return true;
    }
    return false;
  }
  
});

-1
请使用以下代码片段。
ActionListener a=new ActionListener(){
   public void actionPerformed(ActionEvent ae)
   {
    // your code
   }
};
getRootPane().registerKeyboardAction(a,KeyStroke.getKeyStroke("ctrl D"),JComponent.WHEN_IN_FOCUSED_WINDOW);

将“ctrl D”替换为您想要的快捷方式。


1
不,那是过时的API(自JDK 1.2或1.3以来被actionMap/inputMap取代 - 那时候还很古老)。 - kleopatra
@kleopatra 嗯,谢谢评论。我想知道原因。我没有找到它! - JavaTechnical
不太理解 - 为什么的原因是什么? - kleopatra
为什么registerKeyboardAction()已经过时? - JavaTechnical
那是十多年前Swing团队的问题 :-) 曾经有一篇文章(旧的swingconnection?)介绍了keyBindings,同时也解释了为什么这种方法很有用..不过,我没有参考资料,很抱歉。 - kleopatra
1
请查看javadoc(JComponent.registerKeyboardAction(java.awt.event.ActionListener,java.lang.String,javax.swing.KeyStroke,int)):此方法现已过时,请使用getActionMap()和getInputMap()的组合以获得类似的行为。 - serg.nechaev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接