tom*_*med 5 android accessibility accessibility-api accessibilityservice android-accessibility
我有一个 AndroidAccessibilityService部署到运行 Android 5.0.1 的三星 Note 4。
我使用 WhatsApp 作为测试平台,但这适用于任何应用程序,而且更多的是关于辅助功能服务如何触发事件的问题。
2048 (TYPE_WINDOW_CONTENT_CHANGED)Android 触发的事件不一致。如果我将消息发送到我的 WhatsApp 并将其聚焦并且在 75% 的时间屏幕上触发此事件,有时根本不会触发。
是否有一个原因?可访问性事件是否不可靠..?
此外,4096 (TYPE_VIEW_SCROLLED)当用户滚动或当 WhatsApp 的聊天窗口中出现新的信件时,事件似乎确实会持续触发,但是,似乎没有办法确定设备的当前滚动位置是什么?AccessibilityEvent.getSource()提供对列表的一些元数据的访问(在本例中为 android:id/list),但是没有关于此列表或其子元素的滚动位置的可用信息。子列表与屏幕上显示的内容相关,并且boundsToScreen/Parent无论您是查看列表底部还是中间或顶部,值都是相同的。是否有任何线索可以帮助我从所AccessibilityEventNodeInfo呈现的实例中确定滚动位置?
最后,当2048 (TYPE_WINDOW_CONTENT_CHANGED)事件触发时,有时新元素实际上不可用AccessibiltyEvent.getSource()(即使您通过 while 循环迭代到根元素getParent(),然后再次向下扫描)。在将更改应用于 UI 之前,该事件似乎正在拍摄屏幕快照。Athread.sleep没有帮助 - 看起来AccessibilityEventNodeInfo它更像是一个快照而不是对 UI 的实时访问?有什么办法解决这个问题吗?
是否有一个原因?可访问性事件不可靠吗?
我发现您更有可能在事件被触发时产生误解TYPE_WINDOW_CONTENT_CHANGED,而不是事件没有被一致地触发或捕获。例如,此特定事件不会在屏幕刷新时触发,仅在新窗口内容时触发。应用程序开发人员可以选择在其 Activity 上重新绘制内容,而不是启动新的 Activity。在这种情况下,从用户的角度来看,窗口内容已更改,但是,在应用程序的后端,所发生的只是绘制了新视图。
thread.sleep 没有帮助 - 因为看起来 AccessibilityEventNodeInfo 更像是快照而不是对 UI 的实时访问?有什么办法解决这个问题吗?
这也是事件源中缺少元素的解释。您将在绘制动态元素之前获取事件。因此,启动一个新的 Activity,并使用新内容进行初始化,但是,应用程序可能会进入某种类型的网络/REST 资源的等待模式。在这些资源进入之前,事件被触发,然后不久之后就会绘制新内容。因此,在您看来,您获得的内容不完整,但实际情况是您获得了事件触发时的完整内容。你的 thread.sleep 方法工作得很好。但是,睡眠后,您无法检查 event.getSource() 的值,睡眠不会更改传递给该函数的内容。相反,您想要睡觉,然后爬行整个视图层次结构以查找所需的信息。睡觉然后使用
getRootInActiveWindow();
Run Code Online (Sandbox Code Playgroud)
代替
event.getSource();
Run Code Online (Sandbox Code Playgroud)
是否有任何线索可以帮助我确定我所看到的 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;
}
Run Code Online (Sandbox Code Playgroud)
这直接来自 Google 的 TalkBack 代码。您可以在 EyesFree 项目的 ScrollFormatter.java 中找到它。这不是一个理想的解决方案。如果事件恰好来自大型布局(就像 Chrome 中的网页经常发生的那样),则大部分滚动可能会得到相同的结果。然而,API 不支持比这更精确的东西,因此如果您想知道大致的滚动位置,这是一个必要的技巧。我认为即使有可用的 API,也可以对该方法进行重大改进,尽管这需要一些工作。