9

我已经阅读了几次关于键绑定的明确教程,但我的大脑缓存似乎不足以容纳复杂的过程。

我正在调试一个键绑定问题(结果证明我使用了错误的JComponent.WHEN_*条件),并且我偶然发现了一个简明有趣的 javadoc,用于javax.swing.KeyboardManager一个(不幸的)匿名 Java 工程师的私有包。

我的问题是:除了KeyEventDispatcher一开始就检查的内容之外,描述是否遗漏和/或错误?

KeyboardManager 类用于帮助为 WHEN_IN_FOCUSED_WINDOW 样式操作分派键盘操作。具有其他条件的操作直接在 JComponent 中处理。

这是对我所理解的键盘调度至少应如何工作的语义[原文如此]的描述。

KeyEvent 被分派到焦点组件。焦点管理器首先处理此事件。如果焦点管理器不想要它,则 JComponent 调用 super.processKeyEvent() 这允许侦听器有机会处理事件。

如果没有一个侦听器“消费”该事件,那么键绑定就会被击中。这是事情开始变得有趣的地方。首先,使用 WHEN_FOCUSED 条件定义的 KeyStokes [原文如此] 有机会。如果这些都不想要该事件,那么组件会通过它的 [sic] 父项查找类型为 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT 的操作。

如果还没有人拿走它,那么它就会在这里结束。然后我们查找为 WHEN_IN_FOCUSED_WINDOW 事件注册的组件并向它们触发。请注意,如果这些都没有找到,那么我们将事件传递给菜单栏并让它们对其进行破解。它们的处理方式不同。

最后,我们检查我们是否正在查看内部框架。如果我们是并且没有人想要该事件,那么我们向上移动到 InternalFrame 的创建者并查看是否有人想要该事件(等等等等)。


(更新)如果您想知道键绑定指南中的这个粗体警告:

因为搜索组件的顺序是不可预测的,所以要避免重复的 WHEN_IN_FOCUSED_WINDOW 绑定!

这是因为这个片段在KeyboardManager#fireKeyboardAction

     Object tmp = keyMap.get(ks);
     if (tmp == null) {
       // don't do anything
     } else if ( tmp instanceof JComponent) {
           ...
     } else if ( tmp instanceof Vector) { //more than one comp registered for this
         Vector v = (Vector)tmp;
             // There is no well defined order for WHEN_IN_FOCUSED_WINDOW
             // bindings, but we give precedence to those bindings just
             // added. This is done so that JMenus WHEN_IN_FOCUSED_WINDOW
             // bindings are accessed before those of the JRootPane (they
             // both have a WHEN_IN_FOCUSED_WINDOW binding for enter).
             for (int counter = v.size() - 1; counter >= 0; counter--) {
         JComponent c = (JComponent)v.elementAt(counter);
         //System.out.println("Trying collision: " + c + " vector = "+ v.size());
         if ( c.isShowing() && c.isEnabled() ) { // don't want to give these out
             fireBinding(c, ks, e, pressed);
         if (e.isConsumed())
             return true;
         }
     }

所以搜索的顺序其实是可以预测的,但是显然依赖于这个特定的实现,所以最好不要依赖它。保持不可预测。

(Javadoc 和代码来自 WinXP 上的 jdk1.6.0_b105。)

4

1 回答 1

2

我们需要从Component.dispatchEventImpl开始调试。
只需阅读该方法的源代码注释,您就可以很好地了解事件在 Swing 中是如何流动的(您也可以从 EventQueue.pumpEventsForHeirarchy 开始上一级)。

为清楚起见,让我从代码中摘录:

  1. 设置当前事件的时间戳和修饰符。预调度员。在我们通知 AWTEventListener 之前,请在此处执行任何必要的重定向/重新排序。
  2. 允许 Toolkit 将此事件传递给 AWTEventListeners。
  3. 如果没有人消费过按键事件,则允许 KeyboardFocusManager 处理它。
  4. 允许输入法处理事件
  5. 在交付前预处理任何特殊事件
  6. 交付事件以进行正常处理
  7. 4061116 的特殊处理:浏览器挂钩以关闭模式对话框。:)
  8. 允许对等方处理事件。除了 KeyEvents,它们将在所有 KeyEventPostProcessor 之后由 peer 处理(参见 DefaultKeyboardFocusManager.dispatchKeyEvent())

现在您可以将上述流程与您的描述相匹配,以确定它是否正确。但关键是你真的不应该依赖私有类的javadocs,原因是开发人员通常不关心在代码更改时更新私有类的注释,因此文档可能会过时。

于 2011-06-18T05:56:57.300 回答