26、无障碍开发:TalkBack支持、内容描述与焦点顺序优化

说实话,无障碍开发这个话题,在很多Android开发者的优先级列表里排得挺靠后的。我以前也这么觉得——项目工期紧,功能都做不完,谁有空管这个?直到有一次,我亲眼看到一位视障用户拿着遥控器,对着我做的TV应用干着急,TalkBack读出来的内容完全驴唇不对马嘴。那一刻,说实话,挺扎心的。

从那天起,我就把无障碍当成一个硬性指标来做了。今天咱们就聊聊,在TV和大屏应用里,怎么把TalkBack支持、内容描述和焦点顺序这些事做好。

为什么TV应用的无障碍比手机更重要?

你想想看,手机上的视障用户可以用触屏手势、语音助手来操作。但在TV上,遥控器就那么几个按键。如果TalkBack读不出来正确的信息,用户基本就等于被挡在门外了。

我个人的习惯是:把无障碍当成功能的一部分来开发,而不是最后补丁。这样反而省时间,因为不用回头改布局。

TalkBack支持:让遥控器“说话”

TalkBack在TV上的工作方式和手机不太一样。手机靠触摸,TV靠焦点移动。当某个View获得焦点时,TalkBack就会读出它的内容。

最基本的支持,就是给所有可交互的控件设置contentDescription

<Button
    android:id="@+id/btn_play"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:contentDescription="播放当前视频" />

嗯,这里要注意:不要用控件本身的文本作为描述。比如按钮上写着“播放”,但TalkBack读出来还是“播放”,这就浪费了一次信息传递的机会。更好的做法是加上上下文:

android:contentDescription="播放《流浪地球2》"

核心原则:contentDescription 应该告诉用户“这是什么”以及“操作后会怎样”。

内容描述:别让用户猜谜

我在项目中遇到过最典型的问题,就是图片没有描述。TV上很多海报、封面图都是ImageView,如果不加描述,TalkBack只会读出一个“图片”或者“未标记”。用户根本不知道焦点落在哪。

正确的做法:

<ImageView
    android:id="@+id/img_movie_poster"
    android:layout_width="200dp"
    android:layout_height="300dp"
    android:contentDescription="电影《肖申克的救赎》海报,评分9.7" />

对于纯装饰性的图片,比如分割线、背景纹理,应该设置:

android:contentDescription="@null"
android:importantForAccessibility="no"

这样TalkBack就会跳过它,不会干扰用户。

焦点顺序优化:别让用户乱跳

TV应用靠遥控器方向键移动焦点。如果焦点顺序乱了,用户按一下“下”,焦点却跳到右边去了,体验极差。

我见过最离谱的一个项目,焦点在九宫格布局里乱跳,用户想从第一行移到第二行,结果焦点直接飞到右下角去了。嗯,这种问题通常是因为系统自动计算的焦点顺序不符合预期

解决办法有两个:

  1. 显式指定焦点顺序:使用android:nextFocusDownandroid:nextFocusUp等属性。
  2. 调整布局层级:让系统自动计算时更合理。

我个人更推荐第一种,因为可控性高:

<Button
    android:id="@+id/btn_1"
    android:nextFocusDown="@id/btn_4"
    android:nextFocusRight="@id/btn_2" />

<Button
    android:id="@+id/btn_2"
    android:nextFocusDown="@id/btn_5"
    android:nextFocusLeft="@id/btn_1" />

小技巧:在RecyclerView或GridLayout中,可以用android:focusableInTouchMode="true"来确保每个item都能获得焦点。但别滥用,否则焦点会变得很“粘”。

知识体系结构图

下面这张图,是我自己整理的无障碍开发核心逻辑。你看一眼就能明白各个模块之间的关系:

TV无障碍开发核心体系 TalkBack支持 让遥控器“说话” 内容描述 别让用户猜谜 焦点顺序 别让焦点乱跳 contentDescription 设置 importantForAccessibility 图片/按钮/图标描述 装饰性元素跳过 nextFocusDirection 属性 布局层级优化 目标:让视障用户也能流畅操作TV应用 三个模块缺一不可,建议从开发初期就纳入计划

避坑指南:我曾经踩过的几个坑

坑一:自定义View不发声

我曾经写了一个自定义的播放器控件,里面用Canvas画了各种按钮。结果TalkBack完全不识别。后来才发现,自定义View需要手动处理onInitializeAccessibilityNodeInfoonPopulateAccessibilityEvent。说白了,系统不知道你画了个啥,你得告诉它。

override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) {
    super.onInitializeAccessibilityNodeInfo(info)
    info?.contentDescription = "播放控制面板,包含播放、暂停、快进按钮"
    info?.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK)
}

坑二:焦点被“吃掉”了

有一次,用户反馈说遥控器按了没反应。查了半天,发现是一个FrameLayout设置了android:focusable="true",但它本身没有任何交互逻辑。焦点落到它上面就卡住了,再也移不出来。解决办法很简单:只有真正需要交互的控件才设置focusable

警告:不要给容器布局随意设置 focusable="true"。除非你确定它需要独立接收焦点事件。

实战:一个完整的例子

假设我们有一个视频列表页面,每行显示三部电影。用户通过方向键左右选择,按确定键进入详情。代码可以这样写:

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/movie_list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:contentDescription="电影列表,共20部电影"
    android:focusable="true" />

<!-- 每个item的布局 -->
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:contentDescription="电影《{{title}}》,评分{{rating}}"
    android:nextFocusLeft="@id/prev_item"
    android:nextFocusRight="@id/next_item">
    
    <ImageView
        android:id="@+id/movie_poster"
        android:layout_width="120dp"
        android:layout_height="180dp"
        android:contentDescription="{{title}}海报"
        android:importantForAccessibility="yes" />
    
    <TextView
        android:id="@+id/movie_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="{{title}}"
        android:contentDescription="电影名称:{{title}}" />
</LinearLayout>

这里有个细节:RecyclerView本身也要设置contentDescription,这样当焦点进入列表时,TalkBack会先读一句“电影列表,共20部电影”,让用户知道当前在哪个区域。

测试:别靠猜,用真机

我个人的习惯是,每次提交代码前,都会打开TalkBack(设置 → 无障碍 → TalkBack),然后用遥控器把整个页面走一遍。你很快就会发现哪些地方描述不对、哪些焦点跳得不合理。

如果条件允许,找一位真实用户测一下。你会发现很多自己完全想不到的问题。比如,有些用户习惯把遥控器拿得很近,有些则离得很远——这会影响他们看屏幕上的焦点高亮。嗯,这些细节,只有真实用户才能告诉你。

测试清单

  • 所有可点击的控件都有contentDescription
  • 装饰性元素被标记为不重要
  • 焦点顺序符合视觉布局(从左到右,从上到下)
  • 自定义View正确处理无障碍事件
  • 列表/网格的容器也有描述

好了,关于无障碍开发,今天就聊到这儿。说白了,就是多花一点心思,让更多人能用你的应用。这个投入,绝对值。


公众号:蓝海资料掘金营,微信deep3321