如何在具有许多 JTextFields 的 Java Swing JFrame 实例中获取关键事件?

How to get a Key Event in a Java Swing JFrame instance which has many JTextFields?

我有一个 JFrame class,它实现了几个 JLabel 和 JTextFields,我想在焦点位于该 JFrame 上并且用户按下 F1 时执行一个操作。

我创建了以下 class 来处理 KeyListener:

public class PropertiesKey implements KeyListener, ActionListener {

    private Properties propertiesWindow;

    public PropertiesKey(Properties p) {
        propertiesWindow = p;
        System.out.println("DEBUG PropertiesKey");
    }

    @Override
    public void keyPressed(KeyEvent event) {

        System.out.println("DEBUG keyPressed");
        // F1 - Display Attribute Window
        if (event.getKeyCode() == KeyEvent.VK_F1){ 
            System.out.println("F1");
            if(propertiesWindow.isVisible()) {
                propertiesWindow.setVisible(false);
            } else {
                propertiesWindow.setVisible(true);
            }
        }
        System.out.println("KEY PRESSED " + event);
    }

    @Override
    public void keyReleased(KeyEvent event) {
        System.out.println("KEY RELEASED " + event);
    }

    @Override
    public void keyTyped(KeyEvent event) {
        System.out.println("KEY TYPED " + event);
    }

    @Override
    public void actionPerformed(ActionEvent event) {
        System.out.println("ACTION PERFORMED " + event);
    }

}

我附加到主 JFrame class 使用:

this.addKeyListener(new PropertiesKey(this));

我确信这实际上是附加的,因为我在创建密钥侦听器 class 时看到 System.out 消息。

虽然此策略适用于我有 AWT 框架的程序的另一个区域,但在填充有 JTextFields 的 JFrame 的这种特定情况下,我无法设法将 KeyEvent 传播到我的侦听器。

我怀疑 TextFields - JFrame 上的焦点将始终由一个 TextField 捕获 - 可能会拦截事件而不传播它。

如果是这种情况,我该如何解决?哪个 strategy/pattern 用于捕获 JFrame 级别的关键事件?

事实并非如此,我该如何进一步解决这个问题?

您不应该使用 KeyListener。只能为具有焦点的组件生成按键事件。框架不会有直接焦点,只有一个组件添加到框架。

您应该使用 Key Bindings。使用键绑定,即使组件没有焦点,您也可以将 Keystroke 映射到 Action。确保使用适当的 InputMap.

在这种情况下,如果您想为框架添加一个通用处理程序,您可以将键绑定添加到框架的 "root pane"。

阅读 How to Use Key Bindings 上的 Swing 教程部分了解更多信息。

另请阅读有关 How to Use Actions 的部分,了解使用 Action 的好处。

KeyListener 仅适用于(在大多数情况下)当前获得焦点的组件。如果您将它添加到焦点组件的容器中,以防该组件使用关键事件,它也将不起作用。注册键盘操作将是处理 window 范围热键的更好方法。

这是一个如何完成的小例子:

public class FrameHotkey
{
    public static void main ( final String[] args )
    {
        SwingUtilities.invokeLater ( new Runnable ()
        {
            @Override
            public void run ()
            {
                final JFrame frame = new JFrame ();

                frame.setLayout ( new FlowLayout ( FlowLayout.CENTER, 15, 15 ) );
                frame.add ( new JLabel ( "Field 1:" ) );
                frame.add ( new JTextField ( "Field 1", 15 ) );
                frame.add ( new JLabel ( "Field 2:" ) );
                frame.add ( new JTextField ( "Field 2", 15 ) );

                // Hotkey for the F1 in window
                frame.getRootPane ().registerKeyboardAction ( new ActionListener ()
                {
                    @Override
                    public void actionPerformed ( final ActionEvent e )
                    {
                        JOptionPane.showMessageDialog ( frame, "F1 have been pressed!" );
                    }
                }, KeyStroke.getKeyStroke ( KeyEvent.VK_F1, 0 ), JComponent.WHEN_IN_FOCUSED_WINDOW );

                frame.setDefaultCloseOperation ( WindowConstants.EXIT_ON_CLOSE );
                frame.pack ();
                frame.setLocationRelativeTo ( null );
                frame.setVisible ( true );
            }
        } );
    }
}

请注意,我在此处的 JFrameJRootPane 上注册了热键,但这通常无关紧要,因为条件是 JComponent.WHEN_IN_FOCUSED_WINDOW - 这意味着您可以在window 中的任何组件,只要 window 集中在系统中,您就会收到操作事件。

或者,如果您的应用程序 JMenuBar 的菜单项可以执行您想要的操作 - 您可以为这些菜单项指定快捷键,它们将根据指定的热键自行处理操作。

我还建议阅读其他答案中提供的 Swing 教程 camickr 中的文章。