JFrame的KeyListener无响应

82

我试图为我的JFrame实现一个KeyListener。在构造函数中,我使用了以下代码:

System.out.println("test");
addKeyListener(new KeyListener() {
    public void keyPressed(KeyEvent e) { System.out.println( "tester"); }

    public void keyReleased(KeyEvent e) { System.out.println("2test2"); }

    public void keyTyped(KeyEvent e) { System.out.println("3test3"); }
});
当我运行它时,test消息会在我的控制台中出现。然而,当我按下键时,我没有收到任何其他消息,就好像KeyListener根本不存在一样。 我认为可能是因为焦点不在JFrame上,所以KeyListener没有接收到任何事件。但我相当确定它已经在那里了。 我是否漏掉了什么?
12个回答

135

如果您不想为每个组件注册监听器,
您可以将自己的KeyEventDispatcher添加KeyboardFocusManager

public class MyFrame extends JFrame {    
    private class MyDispatcher implements KeyEventDispatcher {
        @Override
        public boolean dispatchKeyEvent(KeyEvent e) {
            if (e.getID() == KeyEvent.KEY_PRESSED) {
                System.out.println("tester");
            } else if (e.getID() == KeyEvent.KEY_RELEASED) {
                System.out.println("2test2");
            } else if (e.getID() == KeyEvent.KEY_TYPED) {
                System.out.println("3test3");
            }
            return false;
        }
    }
    public MyFrame() {
        add(new JTextField());
        System.out.println("test");
        KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
        manager.addKeyEventDispatcher(new MyDispatcher());
    }

    public static void main(String[] args) {
        MyFrame f = new MyFrame();
        f.pack();
        f.setVisible(true);
    }
}

5
KeyboardFocusManager 是应用程序级别的,如果你有多个窗口,可能会遇到问题。 - neoedmund
2
所以这应该可以工作,类似于:foreach(“框架中可聚焦的组件” as _){ _.addkeylistener(frameKeylistener);} - neoedmund

52

你需要将keyListener添加到每个需要的组件上。只有具有焦点的组件才会发送这些事件。例如,如果你的JFrame中只有一个TextBox,那么这个TextBox就拥有了焦点。所以你必须也要为这个组件添加一个KeyListener。

步骤是相同的:

myComponent.addKeyListener(new KeyListener ...);

注意:有些组件(如JLabel)不能获得焦点。

要将它们设置为可获得焦点,您需要执行以下操作:

myComponent.setFocusable(true);

2
是的,你说得对,当程序启动时,你可以稍微看到焦点在按钮A上。给每个按钮添加一个键盘监听器可以解决这个问题。有些奇怪,我本以为给JFrame添加一个键盘监听器会起作用,但似乎不行。谢谢! - Tomek
我在JFrame上创建了一个监听器,用于监听键盘。我想让它在被动模式下工作,即使窗口没有在前面(聚焦)。JFrame在被动模式下无法监听。 - Usman

15
InputMaps和ActionMaps旨在捕获组件及其所有子组件或整个窗口的按键事件。这是通过JComponent.getInputMap()中的参数来控制的。请参见如何使用键绑定以获取文档。
这种设计的美妙之处在于可以挑选并选择重要的监视按键,并根据这些按键触发不同的操作。
当窗口中的任何位置按下Escape键时,此代码将调用JFrame上的dispose()方法。JFrame不派生自JComponent,因此您必须在JFrame中使用另一个组件来创建键绑定。内容面板可能是这样的组件。
InputMap inputMap; 
ActionMap actionMap;
AbstractAction action;
JComponent component;

inputMap  = component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
actionMap = component.getActionMap();

action    = new AbstractAction()
{
   @Override
   public void actionPerformed(ActionEvent e)
   {
      dispose();
   }
};

inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "dispose");
actionMap.put("dispose", action);

12

我曾经遇到过同样的问题,直到我读到真正的问题是关于焦点的。你的JFrame已经添加了监听器,但是你的窗口从未获得焦点,因为你的JFrame中有很多可聚焦的组件,所以请尝试:

JFrame.setFocusable(true);

祝你好运


2
我发现这只在我使用 JFrame 上的某些东西时有效,然后 KeyListener 就不再响应了。 - user3328784

10

KeyListener 是一个低级别的接口,仅应用于单个组件。尽管有努力提高它的可用性,但 JFrame 创建了许多组件,其中最明显的是内容面板。 JComboBox UI 也常以类似的方式实现。

值得注意的是,鼠标事件和键盘事件有略微不同的奇怪工作方式。

有关你应该做什么的详细信息,请参见我在Application wide keyboard shortcut - Java Swing上的答案。


9
< p >Deion(以及其他提出类似问题的人),您可以使用Peter上面的代码,但不是打印到标准输出,而是测试按键码PRESSED、RELEASED或TYPED。< /p >
@Override
public boolean dispatchKeyEvent(KeyEvent e) {
    if (e.getID() == KeyEvent.KEY_PRESSED) {
        if (e.getKeyCode() == KeyEvent.VK_F4) {
            dispose();
        }
    } else if (e.getID() == KeyEvent.KEY_RELEASED) {
        if (e.getKeyCode() == KeyEvent.VK_F4) {
            dispose();
        }
    } else if (e.getID() == KeyEvent.KEY_TYPED) {
        if (e.getKeyCode() == KeyEvent.VK_F4) {
            dispose();
        }
    }
    return false;
}

4
为了捕获JFrame中所有文本字段的关键事件,可以使用关键事件后处理器。以下是一个可行的示例,在添加明显的包含文件之后。
public class KeyListenerF1Demo extends JFrame implements KeyEventPostProcessor {
    public static final long serialVersionUID = 1L;

    public KeyListenerF1Demo() {
        setTitle(getClass().getName());

        // Define two labels and two text fields all in a row.
        setLayout(new FlowLayout());

        JLabel label1 = new JLabel("Text1");
        label1.setName("Label1");
        add(label1);

        JTextField text1 = new JTextField(10);
        text1.setName("Text1");
        add(text1);

        JLabel label2 = new JLabel("Text2");
        label2.setName("Label2");
        add(label2);

        JTextField text2 = new JTextField(10);
        text2.setName("Text2");
        add(text2);

        // Register a key event post processor.
        KeyboardFocusManager.getCurrentKeyboardFocusManager()
                .addKeyEventPostProcessor(this);
    }

    public static void main(String[] args) {
        JFrame f = new KeyListenerF1Demo();
        f.setName("MyFrame");
        f.pack();
        f.setVisible(true);
    }

    @Override
    public boolean postProcessKeyEvent(KeyEvent ke) {
        // Check for function key F1 pressed.
        if (ke.getID() == KeyEvent.KEY_PRESSED
                && ke.getKeyCode() == KeyEvent.VK_F1) {

            // Get top level ancestor of focused element.
            Component c = ke.getComponent();
            while (null != c.getParent())
                c = c.getParent();

            // Output some help.
            System.out.println("Help for " + c.getName() + "."
                    + ke.getComponent().getName());

            // Tell keyboard focus manager that event has been fully handled.
            return true;
        }

        // Let keyboard focus manager handle the event further.
        return false;
    }
}

对于一个工作示例,您可以考虑添加导入。 我通常会添加“包导入”以使它们简短。 否则,加1。 有趣的技术。 - Andrew Thompson

3

这应该会有所帮助

    yourJFrame.setFocusable(true);
    yourJFrame.addKeyListener(new java.awt.event.KeyAdapter() {


        @Override
        public void keyTyped(KeyEvent e) {
            System.out.println("you typed a key");
        }

        @Override
        public void keyPressed(KeyEvent e) {
            System.out.println("you pressed a key");
        }

        @Override
        public void keyReleased(KeyEvent e) {
            System.out.println("you released a key");
        }
    });

2

嗯..你的构造函数是用来干什么的?可能是某个继承JFrame的类吧?窗口焦点应该在窗口上,但我不认为这是问题所在。

我扩展了你的代码,尝试运行它,它可以工作 - 按键的结果会输出。(通过Eclipse在Ubuntu上运行):

public class MyFrame extends JFrame {
    public MyFrame() {
        System.out.println("test");
        addKeyListener(new KeyListener() {
            public void keyPressed(KeyEvent e) {
                System.out.println("tester");
            }

            public void keyReleased(KeyEvent e) {
                System.out.println("2test2");
            }

            public void keyTyped(KeyEvent e) {
                System.out.println("3test3");
            }
        });
    }

    public static void main(String[] args) {
        MyFrame f = new MyFrame();
        f.pack();
        f.setVisible(true);
    }
}

我也获取所有的输出消息。在 Windows 命令行中运行。 - Darrel
2
你会收到所有的消息,因为在这个例子中JFrame拥有焦点。尝试向JFrame添加一个TextBox组件,看看会发生什么。 - bruno conde

1
我也遇到了同样的问题。我按照Bruno给你的建议,发现在JFrame中的“第一个”按钮(即左上角)添加KeyListener可以解决这个问题。但我同意你的看法,这种方法确实有点不稳定。所以我尝试了一下,并发现了一种更简洁的解决方法。只需添加以下代码行:
myChildOfJFrame.requestFocusInWindow();

在你创建了继承自JFrame的子类实例并将其设置为可见后,返回到你的主方法。


谢谢,我也遇到了同样的问题。奇怪的是,即使它是内容面板,该组件也会失去焦点... - Androbin

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