Java Swing:消费键盘事件

4

我有一个包含各种文本框和表格等组件的JFrame, 希望安装一个热键功能,在窗口打开的时候应用该功能(类似于菜单加速器快捷方式)。以下代码大部分情况下可以实现,不管哪个字段或控件拥有焦点,我的操作都会被调用:

String MY_GLOBAL_ACTION_TRIGGER = "hotKey";
InputMap im = getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
KeyStroke ks = KeyStroke.getKeyStroke('`');
im.put(ks, MY_GLOBAL_ACTION_TRIGGER);
ActionMap am = getRootPane().getActionMap();
am.put(MY_ACTION_TRIGGER, new AbstractAction() { public void actionPerformed() ... });

然而,按键操作并未被消费,我仍然在文本框中插入了反引号。一旦我的操作被调用,我该如何防止按键操作传播到文本框中?

3个回答

2
使用KeyboardFocusManager和KeyEventDispatcher
private void myListener implements KeyEventDispatcher {
  public boolean dispatchKeyEvent (KeyEvent ke) {
    if (ke.getKeyChar() == '`') {
      MY_GLOBAL_ACTION.actionPerformed(null);
      return true;
    }
    return false;
  }
}

注意,这种情况无法捕捉到用户将文本粘贴到字段中的用例... - MadProgrammer
1+ 用于处理热键。如果 KeyEvent 没有被分派,那么您就不必担心它会被分派到其他组件。 - camickr

2
很可能文本字段在键事件通知方面具有优先权,这意味着您的键绑定直到文本字段已更新后才会收到通知。
一般来说,您真的不想在文本组件上监视按键/事件,因为它没有考虑用户将文本粘贴到字段中的用例。
如果您想要过滤进入textField的内容,应该使用DocumentFilter
请参见 实现DocumentFilter

1
我不感兴趣监视单个文本组件上的事件,我试图处理整个窗口的键事件(有点像菜单加速器快捷方式)。 - Rolf
你感兴趣的和你能实现的是不同的问题。文本组件通常在按键绑定和其他按键监听器之前处理关键事件。 DocumentFilter 在提交给 Document 之前处理内容,包括用户将文本粘贴到字段的用例。仅消耗事件可能也不足够,因为某些关键进程也可以忽略消耗状态。你需要决定什么是有效的... - MadProgrammer

1
这里有两个问题:
  • 使用char参数构造的KeyStroke似乎不能真正捕获击键。请尝试使用KeyStroke(KeyEvent key, int modifiers)
  • 文本字段应该过滤所选的键,或者说监听器应该将其消耗掉。
  • 请尝试以下代码:
    public class KeyStrokeFrame extends JFrame {
        public static void main(String[] args) {
            new KeyStrokeFrame().setVisible(true);
        }
    
        public KeyStrokeFrame() {
            setSize(200, 200);
            JTextField jtf = new JTextField();
            getContentPane().add(jtf);
            String MY_GLOBAL_ACTION_TRIGGER = "hotKey";
            InputMap im = getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
            KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_1, 0);
            ((AbstractDocument)jtf.getDocument()).setDocumentFilter(new DocumentFilter() {
                @Override
                public void insertString(FilterBypass fb, int offset,
                        String string, AttributeSet attr)
                        throws BadLocationException {
                    if (string.equals("1")) return;
                    super.insertString(fb, offset, string, attr);
                }
    
                @Override
                public void replace(FilterBypass fb, int offset, int length,
                        String text, AttributeSet attrs)
                        throws BadLocationException {
                    if (text.equals("1")) return;
                    super.replace(fb, offset, length, text, attrs);
                }
            });
            im.put(ks, MY_GLOBAL_ACTION_TRIGGER);
            ActionMap am = getRootPane().getActionMap();
            am.put(MY_GLOBAL_ACTION_TRIGGER, new AbstractAction() { 
                public void actionPerformed(ActionEvent e) {
                    System.out.println("pressed");} 
                }); 
            }
    }
    

    抱歉,我的代码不是很清晰,我忘记在动作映射中添加动作处理程序了。我现在已经更新了代码。谢谢提醒。 - Rolf

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