我已经阅读了几次关于键绑定的明确教程,但我的大脑缓存似乎不足以容纳复杂的过程。
我正在调试一个键绑定问题(结果证明我使用了错误的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。)