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

Hai*_* Li 3 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>
Run Code Online (Sandbox Code Playgroud)

使用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并且有一天可能会中断.有更好的解决方案吗?

Hai*_* Li 12

我发现调试我的应用程序和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"
Run Code Online (Sandbox Code Playgroud)

  • 很不错的研究! (3认同)