Android辅助功能服务 - 屏幕更新不一致

时间:2016-02-25 11:15:58

标签: android accessibility accessibilityservice accessibility-api android-accessibility

我将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比实时访问用户界面更像是一个快照?有什么方法吗?

1 个答案:

答案 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,也可以对此方法进行重大改进,但这需要一些工作。