【问题标题】:How to get a Key Event in a Java Swing JFrame instance which has many JTextFields?如何在具有许多 JTextField 的 Java Swing JFrame 实例中获取关键事件?
【发布时间】:2020-03-01 23:12:34
【问题描述】:

我有一个 JFrame 类,它实现了几个 JLabels 和 JTextField,我想在焦点位于该 JFrame 并且用户按 F1 时执行一个操作。

我创建了以下类来处理 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 类:

this.addKeyListener(new PropertiesKey(this));

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

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

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

如果是这种情况,我该如何解决?使用哪种策略/模式来捕获 JFrame 级别的关键事件?

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

【问题讨论】:

    标签: java swing events awt keylistener


    【解决方案1】:

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

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

    在这种情况下,如果您想为框架添加通用处理程序,您可以将键绑定添加到框架的“根窗格”。

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

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

    【讨论】:

      【解决方案2】:

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

      这里有一个小例子说明如何做到这一点:

      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 - 这意味着您可以在窗口中的任何组件上注册它并且只要当窗口聚焦在系统中时,您将收到操作事件。

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

      我还建议阅读另一个答案中提供的 Swing 教程 camickr 中的文章。

      【讨论】:

      • registerKeyboardAction(...) 的 API 声明:此方法现已过时,请结合使用 getActionMap() 和 getInputMap() 以获得类似行为。
      • Mikle 和 camickr 的信息对我理解如何实现这一点有很大帮助。
      • (1-) 用于促进过时方法的使用。
      • 直到现在还没有在JavaDoc中注意到它,我想知道为什么在这种情况下它没有标记为@Deprecated。无论哪种方式,它都只是一种通过 InputMap 和 ActionMap 分配热键的实用方法,而不是直接使用它们。
      • 该方法使用 ActionListener,这是一种较旧的方法。新的 Swing Key Binding API 旨在将 Action 绑定到 Keystroke。该方法将 ActionListener 包装在 Action 中,因此绑定将起作用。关键是您应该使用 Action API,因为它提供了比基本 ActionListener 更多的功能。应使用当前功能开发应用程序。所有 Swing 组件都对键绑定使用操作。
      猜你喜欢
      • 2018-11-06
      • 2012-02-05
      • 2013-01-24
      • 2013-04-29
      • 1970-01-01
      • 2012-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多