某些使用 Android 设备的用户具有不同于他人的无障碍功能需求。为了帮助具有共同的无障碍功能需求的特定人群,Android 框架为开发者创建无障碍服务提供了相关功能,这种服务可将应用的内容呈现给此类用户,并代表他们在应用中执行操作。
Android 提供了一些系统无障碍服务,其中包括:
-
TalkBack: 帮助视力低下或失明的人。通过合成语音读出内容,并在应用中执行操作以响应用户手势。
-
开关控制: 帮助有运动障碍的人。突出显示互动元素,并执行操作以响应用户按某个按钮的动作。用户只使用 1 个或 2 个按钮就能控制设备。
为了帮助具有无障碍功能需求的用户顺利使用您的应用,应用应遵循本页中介绍的最佳做法,这些做法基于 让应用使用起来更没有障碍 中所述的关键准则。
以下各种最佳做法可以进一步改进应用的无障碍功能:
-
标签元素 : 用户应该能够理解应用中每个有意义的互动界面元素的内容和用途。
-
使用或扩展系统微件 : 基于框架包含的视图元素构建,而不是创建您自己的自定义视图。框架的视图和微件类已经提供了应用所需的大多数无障碍功能。
-
使用除颜色之外的提示 : 用户应该能够明确区分界面中的各类元素。为此,除了颜色之外,还应使用图案和位置表示这些差异。
-
让媒体内容使用起来更没有障碍 : 尝试向应用的视频或音频内容添加说明,这样使用这些内容的用户就无需完全依靠视觉或听觉提示。
标签元素
请务必针对应用中的每个互动界面元素,为用户提供实用且描述性的标签。每个标签都应说明特定元素的含义和用途。TalkBack 等屏幕阅读器可以向依靠这些服务的用户读出这些标签。
在大多数情况下,您可以在包含给定界面元素的布局资源文件中指定该元素的说明。虽然您通常使用 contentDescription 属性添加标签,如 让应用使用起来更没有障碍 指南中所述,但还需要牢记一些其他标签添加方法,如下面几部分所述。
可修改的元素
为可修改的元素 (如 EditText 对象) 添加标签时,您可以列举有效输入的示例,除了让相应的示例文字可由屏幕阅读器读出之外,最好还在元素本身中显示这些文字。在这些情况下,您可以使用 android:hint 属性,如以下代码段所示:
<!-- The hint text for en-US locale would be
"Apartment, suite, or building". -->
<EditText
android:id="@+id/addressLine2"
android:hint="@string/aptSuiteBuilding" ... />
在这种情况下,View 对象的 android:labelFor 属性应设为 EditText 元素的 ID。如需了解详情,请参阅下文中介绍如何为相互描述的元素对添加标签的部分。
相互描述的元素对
给定的 EditText 元素通常会有一个相应的 View 对象,该对象描述了用户应在 EditText 元素中输入的内容。在搭载 Android 4.2 (API 级别 17) 或更高版本的设备上,您可以通过设置 View 对象的 android:labelFor 属性来指明这种关系。
下面的代码段显示了为此类元素对添加标签的示例:
<!-- Label text for en-US locale would be "Username:" -->
<TextView
android:id="@+id/usernameLabel" ...
android:text="@string/username"
android:labelFor="@+id/usernameEntry" />
<EditText
android:id="@+id/usernameEntry" ... />
<!-- Label text for en-US locale would be "Password:" -->
<TextView
android:id="@+id/passwordLabel" ...
android:text="@string/password
android:labelFor="@+id/passwordEntry" />
<EditText
android:id="@+id/passwordEntry"
android:inputType="textPassword" ... />
集合中的元素
向集合中的元素添加标签时,每个标签都应该是唯一的。这样,系统的无障碍服务在读出标签时就可以特指屏幕上的 1 个元素。这种对应关系可让用户知道他们何时循环浏览界面,或何时将焦点移到了已经发现的元素。
需要特别注意的是,您应在重用布局内的元素 (如 RecyclerView 对象) 中添加更多文字或上下文信息,以便能够唯一标识每个子元素。
为此,请在适配器实现中设置内容说明,如以下代码段所示
data class MovieRating(val title: String, val starRating: Integer)
class MyMovieRatingsAdapter(private val myData: Array<MovieRating>):
RecyclerView.Adapter<MyMovieRatingsAdapter.MyRatingViewHolder>() {
class MyRatingViewHolder(val ratingView: ImageView) :
RecyclerView.ViewHolder(ratingView)
override fun onBindViewHolder(holder: MyRatingViewHolder, position: Int) {
val ratingData = myData[position]
holder.ratingView.contentDescription = "Movie ${position}: " +
"${ratingData.title}, ${ratingData.starRating} stars"
}
}
相关内容组
如果应用显示的多个界面元素构成一个自然组 (如歌曲的详细信息或消息的属性),应将这些元素整理到一个容器中,该容器通常是 ViewGroup
的子类。将容器对象的 android:screenReaderFocusable 属性设为 true
,并将每个内部对象的 android:focusable
属性设为 false。这样,无障碍服务就可以在单次语音中逐个读出内部元素的内容说明。这样整合相关元素有助于使用辅助技术的用户更高效地发现屏幕上的信息。
注意 : 在 Android 8.1 (API 级别 27) 及更低版本中,android:screenReaderFocusable 属性不可用,因此应改为设置容器的 android:focusable 属性。
以下代码段包含彼此相关的内容片段,因此容器元素 (即 ConstraintLayout
的实例) 的 android:screenReaderFocusable
属性设为 true
,每个内部 TextView
元素的 android:focusable
属性设为 false
:
<!-- In response to a single user interaction, accessibility services announce
both the title and the artist of the song. -->
<ConstraintLayout
android:id="@+id/song_data_container" ...
android:screenReaderFocusable="true">
<TextView
android:id="@+id/song_title" ...
android:focusable="false"
android:text="@string/my_song_title" />
<TextView
android:id="@+id/song_artist"
android:focusable="false"
android:text="@string/my_songwriter" />
</ConstraintLayout>
由于无障碍服务在单次语音中读出内部元素的说明,因此务必确保每条说明都简明扼要地传达出元素的含义。
自定义组标签
如果需要,您可以通过为某个组本身提供内容说明,替换平台中该组的内部元素说明的默认分组和排序。
以下代码段展示了一个示例自定义组说明:
<!-- In response to a single user interaction, accessibility services
announce the custom content description for the group. -->
<ConstraintLayout
android:id="@+id/song_data_container" ...
android:screenReaderFocusable="true"
android:contentDescription="@string/title_artist_best_song">
<TextView
android:id="@+id/song_title" ...
<!-- Content ignored by accessibility services -->
android:text="@string/my_song_title" />
<TextView
android:id="@+id/song_artist"
<!-- Content ignored by accessibility services -->
android:text="@string/my_songwriter" />
</ConstraintLayout>
嵌套组
如果应用的界面显示多维信息 (如节日活动的每日列表),应对内部组容器使用 android:screenReaderFocusable
属性。这种标签添加方案可以很好地平衡发现屏幕内容所需的语音条数与每条语音的长度。
以下代码段展示了一种为较大的组内的组添加标签的方法:
<!-- In response to a single user interaction, accessibility services
announce the events for a single stage only. -->
<ConstraintLayout
android:id="@+id/festival_event_table" ... >
<ConstraintLayout
android:id="@+id/stage_a_event_column"
android:screenReaderFocusable="true">
<!-- UI elements that describe the events on Stage A. -->
</ConstraintLayout>
<ConstraintLayout
android:id="@+id/stage_b_event_column"
android:screenReaderFocusable="true">
<!-- UI elements that describe the events on Stage B. -->
</ConstraintLayout>
</ConstraintLayout>
文字中的标题
某些应用使用标题总结屏幕上显示的多组文字。如果特定的 View 元素表示一个标题,您可以通过将该元素的 android:accessibilityHeading 属性设为 true,表明它的无障碍服务用途。
无障碍服务的用户可以选择浏览标题,而不是浏览段落或字词。这种灵活性可改善文字浏览体验。
无障碍窗格标题
在 Android 9 (API 级别 28) 及更高版本中,您可以为屏幕的窗格提供使用起来没有障碍的标题。出于无障碍目的,窗格是窗口中能够从视觉上加以区分的部分,如 Fragment 的内容。为了让无障碍服务能够理解与窗口行为类似的窗格行为,您应该为应用的窗格指定描述性标题。这样一来,当窗格的外观或内容发生变化时,无障碍服务就可以为用户提供更精细的信息。
如需指定窗格的标题,请使用 android:accessibilityPaneTitle 属性,如以下代码段所示:
<!-- Accessibility services receive announcements about content changes
that are scoped to either the "shopping cart view" section (top) or
"browse items" section (bottom) -->
<MyShoppingCartView
android:id="@+id/shoppingCartContainer"
android:accessibilityPaneTitle="@string/shoppingCart" ... />
<MyShoppingBrowseView
android:id="@+id/browseItemsContainer"
android:accessibilityPaneTitle="@string/browseProducts" ... />
装饰性元素
如果界面中某个元素的存在只是为了让内容看起来间距合理或布局美观,请将其 android:contentDescription 属性设为 "null"
:
如果应用仅支持搭载 Android 4.1 (API 级别 16) 或更高版本的设备,您可以将这些纯装饰性元素的 android:importantForAccessibility 属性设为 “no”。
扩展系统微件
要点: 在设计应用的界面时,请使用或扩展位于 Android 类层次结构中尽可能靠下位置的系统微件。位于层次结构最下方的系统微件已经具备应用所需的大多数无障碍功能。扩展系统提供的这些微件比根据更通用的 View、ViewCompat、Canvas 和 CanvasCompat 类创建您自己的微件更容易。
如果您必须直接扩展 View 或 Canvas (这可能是高度个性化体验或游戏关卡所必需的),请参阅 让自定义视图使用起来更没有障碍。
本部分介绍了如何实现一种特殊类型的 Switch,称为 TriSwitch。TriSwitch 对象的工作方式与 Switch 对象类似,不同之处在于,TriSwitch 的每个实例允许用户在 3 种可能的状态之间切换。
从类层次结构最下方扩展
Switch
对象从其层次结构中的几个框架界面类继承一些属性:
View
↳ TextView
↳ Button
↳ CompoundButton
↳ Switch
新的 TriSwitch
类最好直接从 Switch
类扩展。这样,Android 无障碍功能框架就可以提供 TriSwitch 类所需的大多数无障碍功能:
-
无障碍操作 : 告知系统无障碍服务如何模拟在
TriSwitch
对象上执行的各种可能的用户输入。(继承自 View。) -
无障碍事件 : 告知无障碍服务当屏幕刷新或更新时,
TriSwitch
对象的外观会发生的各种可能的变化方式。(继承自 View。) -
特征 : 有关每个
TriSwitch
对象的详细信息,例如其显示的任何文字的内容。(继承自TextView
。) -
状态信息 :
TriSwitch
对象的当前状态的说明,如 “选中” 或 “未选中”。(继承自CompoundButton
。) -
状态的文字说明: 文字类说明,解释了各种状态的含义。(继承自
Switch
。)
继承自 Switch
及其父类的这种聚合行为几乎就是 TriSwitch
对象的预期行为。因此,您的实现可以着重于将可能的状态数从 2 种扩展到 3 种。
定义自定义事件
当您扩展某个系统微件时,可能会改变用户与该微件互动方式的某一方面。请务必定义这些互动变化,以便无障碍服务可以更新应用的微件,就像用户直接与微件互动一样。
一般准则是,对于您替换的每个基于视图的回调,您还需要通过替换 ViewCompat.replaceAccessibilityAction() 来重新定义相应的无障碍操作。在应用的测试中,您可以通过调用 ViewCompat.performAccessibilityAction() 来验证这些重新定义的操作的行为。
此原则如何对 TriSwitch 对象起作用
与普通的 Switch
对象不同,点按 TriSwitch
对象可以循环切换 3 种可能的状态。因此,需要更新相应的 ACTION_CLICK
无障碍操作:
class TriSwitch(context: Context) : Switch(context) {
// 0, 1, or 2.
var currentState: Int = 0
private set
init {
updateAccessibilityActions()
}
private fun updateAccessibilityActions() {
ViewCompat.replaceAccessibilityAction(this, ACTION_CLICK,
action-label) {
view, args -> moveToNextState()
})
}
private fun moveToNextState() {
currentState = (currentState + 1) % 3
}
}
使用除颜色之外的提示
为了帮助有色觉缺陷的用户,请使用除颜色之外的提示区分应用屏幕中的界面元素。具体方法包括采用不同的形状或大小、提供文字或视觉图案,或者添加基于音频的反馈或基于轻触手势的触感反馈来表示元素的差异。
下图显示了一个 Activity 的两个版本。一个版本仅使用颜色区分工作流程中两种可能的操作。另一个版本采用了最佳做法: 除了颜色之外,还使用了形状和文字来突出两个选项之间的差异:
△ 仅使用颜色创建界面元素的示例 (左图),以及使用颜色、形状和文字创建界面元素的示例 (右图)
让媒体内容使用起来更没有障碍
如果您开发的应用包含视频剪辑或音频录音等媒体内容,应设法为具有不同类型的无障碍功能需求的用户提供支持,让他们能够理解此类内容。特别是,我们建议您做到以下几点:
-
添加可让用户暂停或停止播放媒体、调整音量以及切换字幕的控件。
-
如果视频提供的信息对于完成工作流程至关重要,应以其他格式提供相同的内容,如转录内容。
其他资源
如需详细了解如何让您的应用使用起来更没有障碍,请参阅下面列出的其他资源:
- Codelab: 基本的 Android 无障碍功能
- 博文: 无障碍功能: 是否所有用户都能使用您的应用?
欢迎您持续关注我们,及时了解更多开发技术和产品更新等资讯动态。