我想以编程方式确定从当前窗口中的一个Swing组件(拥有焦点)转到另一个组件所需的键盘笔划数(无论是否有制表符和/或箭头键)。每个笔画应增加1的距离;如果无法访问组件,则结果应为-1。
由于我无法找到实用工具方法,我想到了以下签名:
public static int getFocusTraversalDistance( Component from, Component to )
天真地,我开始通过getFocusCycleRootAncestor()
获取Container
from
。之后,我会使用getFocusTraversalPolicy()
获取FocusTraversalPolicy
,然后分别使用getComponentAfter(Container, Component)
getComponentBefore(Container, Component)
遍历组件。
但是,我对Swing / AWT焦点子系统并不熟悉,我不知道是否有更优雅的方式?
我需要这些信息的原因是我的硕士论文,我正在撰写。这个想法是通过机器学习来增强基于GUI的猴子测试。训练模型不是挑选随机组件,而是尝试根据历史用户/测试人员痕迹“推荐”组件。我正在使用的功能之一是前一个目标组件与可能的目标组件之间的焦点遍历距离。
感谢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 focus subsystem doc表示“[...]所有组件从此[Component#isFocusable()
]方法返回true。”即使JLabel
等组件返回true
,但是(AFAIK)它实际上无法获得焦点,而Component#hasFocus()
始终为false
。
如果有人感兴趣,我可以使用功能齐全的测试套件设置GitHub项目。
答案 0 :(得分:5)
这个问题只与Swing有关,请不要担心 - 感谢任何帮助
我已经说过这听起来像是一种合理的方法。我不确定的唯一想法是,当你有多个嵌套面板时,这种方法会起作用。或者至少如果你必须继续获得每个容器的焦点遍历策略,它可能会变得更复杂。
您可以尝试查看KeyboardFocusManager
以查看它是否有帮助。
我能想到的唯一其他方法是实际选中表单上的所有组件并将每个组件添加到ArrayList
。然后,您的getFocusTraversalDistance(...)
会使用ArrayList
而不是FocusTraversalPolicy
。当然,这有一个问题,即组件实际上会获得焦点,并且可以调用focusGained
/ focusLost
代码。
或者可能将两种方法结合起来。这是使用您的方法来使用焦点遍历策略,但只有在创建框架时才能构建ArrayList
。然后,您的getFocusTraversalDistance(...)
可以引用ArrayList
,这样可以提高效率。