计算窗口中两个 Swing 组件的焦点遍历距离



原始帖子

我想以编程方式确定在当前窗口中从一个 Swing 组件(拥有焦点)到另一个组件需要多少次键盘笔划(无论选项卡和/或箭头键是否无关紧要)。每个笔划应增加 1 的距离;如果无法访问该组件,则结果应为 -1。

由于我找不到实用方法,我想到了以下签名:

public static int getFocusTraversalDistance( Component from, Component to )

天真地,我会从通过getFocusCycleRootAncestor()获得fromContainer开始。之后,我会用getFocusTraversalPolicy()获取FocusTraversalPolicy,并分别使用getComponentAfter(Container, Component)getComponentBefore(Container, Component)遍历组件。

但是,我对 Swing/AWT 焦点子系统并不熟悉,我想知道是否有更优雅的方法?

编辑 #1

我需要这些信息的原因是我目前正在写的硕士论文。这个想法是通过机器学习来增强基于 GUI 的猴子测试。经过训练的模型不是随机选择组件,而是尝试根据历史用户/测试人员跟踪"推荐"组件。我为此使用的功能之一是前一个目标组件和可能的目标组件之间的焦点遍历距离。

编辑 #2

感谢 camickr 的宝贵输入,我目前正在使用以下算法:

public static int getFocusTraversalDistance( Component from, Component to ) {
if ( from.equals( to ) ) {
return 0;
}
final Container root = from.getFocusCycleRootAncestor();
if ( root == null ) {
return -1;
}
final FocusTraversalPolicy policy = root.getFocusTraversalPolicy();
final HashSet<Component> visited = new HashSet<>();
Component before = from;
Component after = from;
int distance = 1;
while ( true ) {
if ( before != null ) {
visited.add( before );
before = policy.getComponentBefore(
before.getFocusCycleRootAncestor(), before );
if ( to.equals( before ) ) {
return distance;
}
}
if ( after != null ) {
visited.add( after );
after = policy.getComponentAfter(
after.getFocusCycleRootAncestor(), after );
if ( to.equals( after ) ) {
return distance;
}
}
if ( before == null && after == null
|| visited.contains( before ) && visited.contains( after ) ) {
return -1;
}
distance++;
}
}

到目前为止,它似乎有效,但实际上不可聚焦的组件可能会产生奇怪的结果。AWT 焦点子系统文档说"[...]所有组件都从这个 [Component#isFocusable()] 方法返回 true。甚至诸如JLabel返回true,尽管 (AFAIK) 它实际上无法获得焦点,并且Component#hasFocus()总是false.

如果有人感兴趣,我可以设置一个具有完整功能测试套件的 GitHub 项目。

这个问题只与 Swing 有关,别担心——任何帮助都值得赞赏

我已经说过这听起来像是一个合理的方法。我唯一不确定的是,当您有多个嵌套面板时,该方法是否有效。或者,如果您必须保留每个容器的焦点遍历策略,至少它可能会变得更加复杂。

您可以尝试查看KeyboardFocusManager,看看它是否有帮助。

我能想到的唯一另一种方法是实际浏览表单上的所有组件并将每个组件添加到ArrayList。然后,您的getFocusTraversalDistance(...)将使用ArrayList而不是FocusTraversalPolicy。当然,这有一个问题,即组件实际上将获得焦点并且可以调用focusGained/focusLost代码。

或者也许将这两种方法结合起来。也就是说,使用您的方法来使用焦点遍历策略,但仅在创建帧时执行此方法来构建ArrayList。然后你的getFocusTraversalDistance(...)可以引用ArrayList,这应该会让它更有效率。

最新更新