我有一个带有工具栏和一个 View 的布局,该 View 将托管其他控件:
<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是为了使具有正“高程”的工具栏可以是半透明的,并且 View 可以扩展到工具栏下方。工具栏和 View 的顶部位置相同,但高度不同。 “话语提示”构建 View 层次结构时,即使首先定义了工具栏,也会将其置于底部。 View 上的“accessibilityTraversalBefore”和“accessibilityTraversalAfter”无效。到目前为止,我发现的唯一解决方案是在ViewStub中添加1px或0.1px的上边距。这可能与此处的代码有关:
https://github.com/google/talkback/blob/master/src/main/java/com/android/utils/traversal/ReorderedChildrenIterator.java
该代码尝试根据节点的屏幕位置对其进行重新排序。增加最高边距有点麻烦,并且可能有一天会中断。有更好的解决方案吗?
最佳答案
在弄清我的应用程序和TalkBack应用程序后,我发现了发生了什么。可访问性遍历顺序不受布局中定义的顺序 View 的控制。在工具栏或activity_react_native.xml中的ViewStub中添加accessibilityTraversalBefore或accessibilityTraversalAfter无效。我找不到遍历顺序的任何文档,所以我 checkout 了代码。以下是“话语提示”的工作方式:
启用“话语提示”后,所有单指手势都会发送到“话语提示”而不是应用程序。可以在“话语提示”设置中重新定义这些手势。上下滑动手势后,“话语提示”会首先枚举当前可见页面上所有可聚焦的 View 。这是通过向应用发送消息来完成的。
在应用程序过程中,可访问性 View 层次结构是通过递归遍历所有 subview 而构建的。这是在ViewGroup代码https://github.com/aosp-mirror/platform_frameworks_base/blob/d18ed49f9dba09b85782c83999a9103dec015bf2/core/java/android/view/ViewGroup.java#L2314中完成的。此调用的最后一个参数是硬编码的true,它根据 subview 在屏幕上的位置对它们进行排序。比较逻辑在这里:https://github.com/aosp-mirror/platform_frameworks_base/blob/d18ed49f9dba09b85782c83999a9103dec015bf2/core/java/android/view/ViewGroup.java#L8397。当两个 View 的顶部,左侧和宽度相同时,高度较大的 View 位于高度或面积较小的 View 之前,在大多数情况下,这似乎是一个错误的选择。
生成树后,将其发送回TalkBack,然后TalkBack根据traversalBefore或traversalAfter属性对元素进行重新排序。由于工具栏和ViewStub都不是可聚焦元素,因此它们不会出现在该树中,这就是为什么这两个属性不起作用的原因。
解决方法是将android:importantForAccessibility添加到工具栏和ViewStub中。这会将它们从ViewGroup添加到可访问性树中,以便TalkBack可以对accessibilityTraversalBefore/After属性起作用。
还有一种解决方法是将工具栏向上移动1px,然后在内部添加1px填充。多余的1px应该由FrameLayout裁剪,因此没有视觉变化:
android:layout_marginTop="-1px"
android:paddingTop="1px"