我将Android AccessibilityService
部署到运行Android 5.0.1的Samsung Note 4上。
我使用WhatsApp作为测试平台,但这适用于任何应用程序,而且更多的是关于可访问性服务如何触发事件的问题。
事件2048 (TYPE_WINDOW_CONTENT_CHANGED)
并非由Android引发。如果我将消息发送到我的WhatsApp并将其发送到焦点,屏幕上75%的时间会触发此事件,有时根本不会。
这有什么理由吗?辅助功能事件是否不可依赖..?
此外,当用户滚动或WhatsApps中出现新的通信时,似乎事件4096 (TYPE_VIEW_SCROLLED)
会一直被触发。但是,聊天窗口似乎无论如何都无法确定设备的当前滚动位置是什么? AccessibilityEvent.getSource()
提供对列表的一些元数据的访问(在本例中为android:id / list),但是没有关于此列表或其子元素的滚动位置的可用信息。子列表与屏幕上显示的内容相关,boundsToScreen/Parent
值相同,无论您是查看列表底部还是中间或顶部。有没有任何线索可以帮助我从AccessibilityEventNodeInfo
实例中确定滚动位置?
最后,当2048 (TYPE_WINDOW_CONTENT_CHANGED)
事件触发时,有时候AccessibiltyEvent.getSource()
实际上没有新元素可用(即使使用{迭代到根元素vi a while循环时也是如此{1}}然后再次向下扫描)。事件似乎是在将更改应用于UI之前拍摄屏幕快照。 getParent()
没有帮助 - 因为看起来thread.sleep
比实时访问用户界面更像是一个快照?有什么方法吗?
答案 0 :(得分:2)
这有什么理由吗?辅助功能事件不是 dependendable ..?
我发现,当TYPE_WINDOW_CONTENT_CHANGED
事件被触发时,你更容易误解,而不是一直没有被解雇或被抓住。例如,此特定事件不会在屏幕刷新时触发,仅在新窗口内容上触发。应用开发者可以选择(而不是启动新活动)来重新绘制活动内容。在这种情况下,从用户的角度来看,窗口内容已经改变,但是,在应用程序的后端,他所发生的一切都是新的视图已被绘制。
thread.sleep没有帮助 - 因为看起来AccessibilityEventNodeInfo比实时访问UI更像是一个快照?有什么方法吗?
这也解释了为什么事件源中缺少元素的原因。在绘制动态元素之前,您将获得该事件。因此,启动了一个新的Activity,并使用新内容进行初始化,但是,应用程序可能会进入某种类型的网络/ REST资源的等待模式。在这些资源出现之前,事件被触发,然后不久就会绘制新内容。因此,在您看来,您的内容不完整,但实际发生的是您在事件触发时获得的完整内容。你是thread.sleep方法工作正常。但是,在你睡觉之后,你无法检查event.getSource()的值,sleep不会改变传递给这个函数的内容。相反,你想要睡觉,然后爬过整个视图heirarchy来找到你想要的信息。睡觉然后再使用
getRootInActiveWindow();
取代
event.getSource();
是否有任何线索可以帮助我确定我所提供的AccessibilityEventNodeInfo实例中的滚动位置?
是和否。不,无障碍服务无法检测滚动条的位置。但是,您可以通过执行以下操作来检测滚动事件的大致位置:
private float getScrollPosition(AccessibilityEvent event) {
final AccessibilityRecordCompat record = new AccessibilityRecordCompat(event);
final int itemCount = event.getItemCount();
final int fromIndex = event.getFromIndex();
// First, attempt to use (fromIndex / itemCount).
if ((fromIndex >= 0) && (itemCount > 0)) {
return (fromIndex / (float) itemCount);
}
final int scrollY = record.getScrollY();
final int maxScrollY = record.getMaxScrollY();
// Next, attempt to use (scrollY / maxScrollY). This will fail if the
// getMaxScrollX() method is not available.
if ((scrollY >= 0) && (maxScrollY > 0)) {
return (scrollY / (float) maxScrollY);
}
// Finally, attempt to use (scrollY / itemCount).
// TODO(alanv): Hack from previous versions -- is it still needed?
if ((scrollY >= 0) && (itemCount > 0) && (scrollY <= itemCount)) {
return (scrollY / (float) itemCount);
}
return 0.5f;
}
这直接来自Google的TalkBack代码。您可以在EyesFree项目的ScrollFormatter.java中找到它。这不是一个理想的解决方案。如果事件恰好来自大型布局(就像Chrome中的网页经常发生的那样),那么在大部分滚动时最终会得到相同的结果。但是,API不支持比这更精确的任何东西,所以如果你想知道你的近似滚动位置,这是一个必要的黑客。我认为即使使用可用的API,也可以对此方法进行重大改进,但这需要一些工作。