【问题标题】:Change reading order of elements within a focusable ViewGroup更改可聚焦 ViewGroup 中元素的阅读顺序
【发布时间】:2019-09-25 14:25:08
【问题描述】:

我的应用中有一些屏幕上的 TalkBack 无法推断出正确的阅读顺序。根据文档,我可以使用android:accessibilityTraversalAfter和朋友来改变阅读顺序。但它不适用于我应该一起阅读的可聚焦ViewGroup 中的元素。

整个布局是这样的:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:accessibilityTraversalBefore="@id/before"
    android:focusable="true"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/before"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:accessibilityTraversalAfter="@id/before"
        android:text="Before"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />

    <TextView
        android:id="@+id/after"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:accessibilityTraversalBefore="@id/after"
        android:text="After"
        app:layout_constraintBottom_toTopOf="@+id/before"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

它在屏幕中间渲染After,在底部渲染Before。我希望 TalkBack 将整个屏幕视为单个连续元素,因此我将 android:focusable 设置为 true。默认情况下,TalkBack 显示为:“之后,之前”。但我希望它阅读“之前,之后”。虽然我添加了android:accessibilityTraversalBeforeandroid:accessibilityTraversalAfter,但它仍然显示为“After, before”。这是节点树调试的输出:

TreeDebug: (-2147455381)429.FrameLayout:(0, 0 - 1080, 1920):A
TreeDebug:   (30189)429.TextView:(42, 101 - 397, 172):TEXT{My Application}:A:supportsTextLocation
TreeDebug:   (31150)429.ViewGroup:(0, 210 - 1080, 1794):Fa:focusable:accessibilityFocused
TreeDebug:     (33072)429.TextView:(499, 951 - 581, 1002):TEXT{After}:A:supportsTextLocation
TreeDebug:     (32111)429.TextView:(485, 1743 - 595, 1794):TEXT{Before}:A:supportsTextLocation

我做错了什么?

为了完整性:minSdkVersion 是 26,targetSdkVersion 是 29。

【问题讨论】:

  • 您能否验证一下,您是否在具有该布局的空白项目中看到了这种情况?还是您只能在您的应用/您的设置中重现该问题?
  • 此布局是从使用空白模板使用 AS 3.5 创建的空示例应用程序中提取的。除了onCreate() 调用super()setContentView() 之外,活动为空。

标签: android android-layout android-view talkback android-accessibility


【解决方案1】:

我已经深入研究了这个问题并发现了以下问题:accessibilityTraversalBeforeaccessibilityTraversalAfter 标志确实生效,但仅适用于它们的用途 - 它们适用于无障碍服务应用程序(例如对讲)。换句话说,如果您从根布局中删除 focusable 属性,您将看到导航是正确的。

但是这些标志不会影响AccessibilityNode 是如何为根ViewGroup 构造的。从sources of ViewGroup#onInitializeAccessibilityNodeInfoInternal() 中可以看出,实际的文本构造逻辑确实考虑孩子如何使用上面提到的标志来构造导航。

为了解决这个问题,我从布局 xml 中删除了过多的标志:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:focusable="true"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/before"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:importantForAccessibility="no"
        android:text="Before"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/after"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:importantForAccessibility="no"
        android:text="After"
        app:layout_constraintBottom_toTopOf="@+id/before"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

在宿主活动/片段内部:

val root = findViewById<ViewGroup>(R.id.root)

ViewCompat.setAccessibilityDelegate(root, object : AccessibilityDelegateCompat() {
    override fun onInitializeAccessibilityNodeInfo(
        host: View?,
        info: AccessibilityNodeInfoCompat?
    ) {
        val stringBuilder = StringBuilder()
        root.children.forEach { view ->
            val label = if (view is TextView) view.text else ""
            stringBuilder.append("$label, ")
        }

        info?.text = stringBuilder.toString()
        super.onInitializeAccessibilityNodeInfo(host, info)
    }
})

这将产生预期的结果:Talkback 将发音为 “Before,After”

不幸的是,这是一个容易出错的代码,这意味着如果您以某种方式重新构建视图层次结构,那么子节点的顺序会被交换,那么这个节点文本构造逻辑就会被破坏。不过,我想不出更好的解决方案,也看不出有可能指示父母考虑孩子的订购标志(基于来源)。

【讨论】:

  • 感谢您的深入调查。 focusable 主要用于帮助键盘导航。考虑到 screenReaderFocusable 仅在 API 28 上可用,它对 TalkBack 有影响是一个好处。但即使单独使用 screenReaderFocusable,它也不会在 after 之前读取 before。因此,我发现第一段令人困惑。如果这些属性不影响AccessibilityNodeViewGroup,它们的用途是什么?但是非常感谢您确认我必须自己构建文本层次结构。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-30
  • 1970-01-01
  • 2023-04-05
  • 2015-10-07
  • 2012-02-27
  • 1970-01-01
相关资源
最近更新 更多