Android辅助功能:Talkback以错误的顺序遍历元素

时间:2018-01-07 00:10:34

标签: android accessibility talkback

我有一个带工具栏的布局和一个将托管其他控件的视图:

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <ViewStub
        android:id="@+id/root_view_stub"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</FrameLayout>

使用FrameLayout以使具有正“高程”的工具栏可以是半透明的,并且视图延伸到工具栏下方。工具栏和视图具有相同的顶部位置但高度不同。当Talkback构建视图层次结构时,即使首先定义了工具栏,它也会将工具栏置于底部。 “accessibilityTraversalBefore”和“accessibilityTraversalAfter”对视图没有任何影响。到目前为止,我发现的唯一解决方案是向ViewStub添加1px或0.1px的上边距。这可能与这里的代码有关:

https://github.com/google/talkback/blob/master/src/main/java/com/android/utils/traversal/ReorderedChildrenIterator.java

代码尝试根据屏幕位置重新排序节点。添加上边距有点hacky并且有一天可能会中断。有更好的解决方案吗?

1 个答案:

答案 0 :(得分:10)

我在调试我的应用程序和TalkBack应用程序后想出了发生了什么。可访问性遍历顺序不受布局中定义的顺序视图的控制。添加accessibilityTraversalBefore或accessibilityTraversalAfter到工具栏或activity_react_native.xml中的ViewStub无效。我找不到任何关于遍历顺序的文档,所以我检查了代码。以下是TalkBack的工作原理:

当TalkBack打开时,所有单指手势都会发送到TalkBack而不是应用程序。可以在TalkBack设置中重新定义这些手势。在向上和向下滑动手势后,TalkBack首先枚举当前可见页面上的所有可聚焦视图。这是通过向应用程序发送消息来完成的。 在应用程序进程中,通过递归遍历所有子视图来构建可访问性视图层次结构。它在ViewGroup代码中完成:https://github.com/aosp-mirror/platform_frameworks_base/blob/d18ed49f9dba09b85782c83999a9103dec015bf2/core/java/android/view/ViewGroup.java#L2314。此调用的最后一个参数是一个硬编码的true,它根据子视图在屏幕上的位置对子视图进行排序。比较逻辑在这里:https://github.com/aosp-mirror/platform_frameworks_base/blob/d18ed49f9dba09b85782c83999a9103dec015bf2/core/java/android/view/ViewGroup.java#L8397。当两个视图具有相同的顶部,左侧和宽度时,具有较大高度的视图位于具有较小高度或区域的视图之前,这在大多数情况下似乎是不好的选择。 在构建树之后,它会被发送回TalkBack,然后TalkBack会根据traversalBefore或traversalAfter属性重新排序元素。由于工具栏和ViewStub不是可聚焦元素,因此它们不会出现在此树中,这就是为什么这两个属性不起作用的原因。

修复方法是将android:importantForAccessibility添加到工具栏和ViewStub。这会将它们添加到ViewGroup的辅助功能树中,以便TalkBack可以对accessibilityTraversalBefore / After属性进行操作。

还有一种解决方法可以将工具栏向上移动1px,然后在其中添加1px填充。这个额外的1px应该被FrameLayout剪掉,所以没有视觉上的变化:

android:layout_marginTop="-1px"
android:paddingTop="1px"