简体   繁体   English

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

[英]Android Accessibility Service - Screen updates not consistent

I have an Android AccessibilityService deployed to a Samsung Note 4 running Android 5.0.1. 我有一个Android AccessibilityService部署到运行Android 5.0.1的Samsung Note 4。

I'm using WhatsApp as a test bed, but this applies to any Application and is more a question around how the Accessibility Service fires off events. 我使用WhatsApp作为测试平台,但这适用于任何应用程序,而且更多的是关于可访问性服务如何触发事件的问题。

The event 2048 (TYPE_WINDOW_CONTENT_CHANGED) is not consistent fired by Android. 事件2048 (TYPE_WINDOW_CONTENT_CHANGED)不是由Android 2048 (TYPE_WINDOW_CONTENT_CHANGED) If I send messages to my WhatsApp with it in focus and the screen on 75% of the time this event is fired and at times is simply doesn't. 如果我将消息发送到我的WhatsApp并将其置于焦点,并且屏幕在75%的时间内被触发,有时则不会。

Is there a reason for this? 是否有一个原因? Are Accessibility events not dependendable..? 辅助功能事件是否不可依赖..?

Additionally, it appears that the event 4096 (TYPE_VIEW_SCROLLED) does get fired consistently when the user scrolls or when new correspondence appear in WhatsApps' chat window however, there doesn't seem to be anyway to determine what the current scroll position of the device is? 此外,当用户滚动或WhatsApps的聊天窗口中出现新的通信时,似乎事件4096 (TYPE_VIEW_SCROLLED)会一直被触发但是,似乎无论如何都无法确定设备的当前滚动位置是什么? AccessibilityEvent.getSource() provides access to some meta-data for the list (in this case android:id/list) however there is no information available as to the scroll position of this list or its children elements. AccessibilityEvent.getSource()提供对列表的一些元数据的访问(在本例中为android:id / list),但是没有关于此列表或其子元素的滚动位置的可用信息。 The child list is relative to what is displayed on the screen and the boundsToScreen/Parent values are the same regardless if you're looking at the bottom of the list or the middle or top. 子列表与屏幕上显示的内容相关,并且boundsToScreen/Parent值相同,无论您是在查看列表的底部还是中间或顶部。 Is there any clues to help me determine the scroll position from the AccessibilityEventNodeInfo instance I am presented with? 是否有任何线索可以帮助我确定我所提供的AccessibilityEventNodeInfo实例中的滚动位置?

Lastly, when the 2048 (TYPE_WINDOW_CONTENT_CHANGED) event does fire, there are times when the new elements are not actually available from the AccessibiltyEvent.getSource() (even when you iterate up to the root element vi a while loop using getParent() and then scan down again). 最后,当2048 (TYPE_WINDOW_CONTENT_CHANGED)事件触发时,有时新的元素实际上不能从AccessibiltyEvent.getSource() (即使使用getParent()迭代到根元素vi a while循环,然后再次扫描)。 It appears that the event is taking a snapshot of the screen before the change has been applied to the UI. 事件似乎是在将更改应用于UI之前拍摄屏幕快照。 A thread.sleep does not help - as it appears the AccessibilityEventNodeInfo is more of a snapshot than live access to the UI? thread.sleep没有帮助 - 因为看起来AccessibilityEventNodeInfo比实时访问UI更像是一个快照? Any way around this? 有什么方法吗?

Is there a reason for this? 是否有一个原因? Are Accessibility events not dependendable..? 辅助功能事件是否不可依赖..?

I find it more likely that you are misunderstanding when a TYPE_WINDOW_CONTENT_CHANGED event is fired, than that it is not fired or caught consistently. 我发现当你触发TYPE_WINDOW_CONTENT_CHANGED事件时,你更容易误解,而不是一直触发或捕获它。 For example, this particular event does not fire on screen refreshes, only on new window content. 例如,此特定事件不会在屏幕刷新时触发,仅在新窗口内容上触发。 An app developer may choose, instead of launching a new Activity, to re-draw content over their activity. 应用开发者可以选择(而不是启动新活动)来重新绘制活动内容。 In this case, from the users point of view the window content has changed, however, on the backend of the application all that his happened is new views have been drawn. 在这种情况下,从用户的角度来看,窗口内容已经改变,但是,在应用程序的后端,他所发生的一切都是新的视图已被绘制。

A thread.sleep does not help - as it appears the AccessibilityEventNodeInfo is more of a snapshot than live access to the UI? thread.sleep没有帮助 - 因为看起来AccessibilityEventNodeInfo比实时访问UI更像是一个快照? Any way around this? 有什么方法吗?

This is also the explanation for why elements are missing from the event source. 这也解释了为什么事件源中缺少元素的原因。 You are getting the event before dynamic elements are drawn. 在绘制动态元素之前,您将获得该事件。 So, a new Activity is launched, and initialized with new content, however, the application probably goes into a waiting pattern for some type of network/REST resources. 因此,启动了一个新的Activity,并使用新内容进行初始化,但是,应用程序可能会进入某种类型的网络/ REST资源的等待模式。 Before these resources come in the event is fired, then shortly thereafter new content is drawn. 在这些资源出现之前,事件被触发,然后不久就会绘制新内容。 So, it appears to your eyes that your getting incomplete content, but what's really happening is you're getting the complete content as of the time the event was triggered. 因此,在您看来,您的内容不完整,但实际发生的是您在事件触发时获得的完整内容。 You're thread.sleep method works just fine. 你是thread.sleep方法工作正常。 However, after you sleep, you can't inspect the value of event.getSource(), sleeping won't change what was passed to this function. 但是,在你睡觉之后,你无法检查event.getSource()的值,sleep不会改变传递给这个函数的内容。 Instead, you want to sleep, and then crawl through the entire view heirarchy to find the info you want. 相反,你想要睡觉,然后爬过整个视图heirarchy来找到你想要的信息。 Sleep and then use 睡觉再用

getRootInActiveWindow();

in place of 代替

event.getSource();

Is there any clues to help me determine the scroll position from the AccessibilityEventNodeInfo instance I am presented with? 是否有任何线索可以帮助我确定我所提供的AccessibilityEventNodeInfo实例中的滚动位置?

Yes and no. 是的,不是。 No, there is no way from an accessibility service to detect the location of the scroll bar. 不,无障碍服务无法检测滚动条的位置。 However, you can detect the approximate position of the scroll event by doing the following: 但是,您可以通过执行以下操作来检测滚动事件的大致位置:

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;
}

This is direct from Google's TalkBack code. 这直接来自Google的TalkBack代码。 You can find it in ScrollFormatter.java in the EyesFree project. 您可以在EyesFree项目的ScrollFormatter.java中找到它。 It's not an ideal solution. 这不是一个理想的解决方案。 If the event happens to come from a large layout (as happens frequently with web pages in chrome), you can end up with the same result for a large portion of scrolling. 如果事件恰好来自大型布局(就像Chrome中的网页经常发生的那样),那么在大部分滚动时最终会得到相同的结果。 However, the APIs don't support anything more precise than this, so this is a necessary hack if you want to know you approximate scroll location. 但是,API不支持比这更精确的任何东西,所以如果你想知道你的近似滚动位置,这是一个必要的黑客。 I think significant improvements could be made to this method, even with available APIs, though it would take some work. 我认为即使使用可用的API,也可以对此方法进行重大改进,但这需要一些工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM