提升应用可访问性的原则

为了帮助有可访问性需求的用户,Android 框架允许您创建辅助功能服务,该服务可以向用户呈现应用中的内容,并代表他们操作应用。

Android 提供了几种系统辅助功能服务,包括以下内容

  • TalkBack:帮助视力低下或失明人士。它通过合成语音宣布内容,并根据用户手势对应用执行操作。
  • Switch Access:帮助有运动障碍的人。它突出显示交互式元素,并响应用户按下按钮执行操作。它允许仅使用一个或两个按钮控制设备。

为了帮助有可访问性需求的人员成功使用您的应用,您的应用必须遵循本页上描述的最佳实践,这些最佳实践建立在使应用更易于访问中描述的指南的基础上。

以下各节中描述的每种最佳实践都可以进一步提高应用的可访问性

标记元素
用户必须能够理解应用中每个交互式和有意义的 UI 元素的内容和用途。
添加辅助功能操作
通过添加辅助功能操作,您可以使辅助功能服务的使用者能够完成应用中的关键用户流程。
扩展系统部件
构建框架包含的视图元素,而不是创建您自己的自定义视图。框架的视图和部件类已经提供了应用所需的大多数辅助功能。
使用颜色以外的提示
用户必须能够清楚地区分 UI 中元素的类别。为此,请使用模式和位置以及颜色来表达这些差异。
使媒体内容更易于访问
为应用的视频或音频内容添加描述,以便使用此内容的用户无需完全依赖视觉或听觉提示。

标记元素

务必为应用中每个交互式 UI 元素提供有用且描述性的标签。每个标签都必须解释特定元素的含义和用途。屏幕阅读器(如 TalkBack)可以向用户宣布这些标签。

在大多数情况下,您在包含该元素的布局资源文件中指定 UI 元素的描述。通常,您使用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 元素中输入的内容。您可以通过设置 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" ... />

集合中的元素

在向集合的元素添加标签时,每个标签必须唯一。这样,系统的辅助功能服务在宣布标签时就可以准确地参考一个屏幕上的元素。这种对应关系可以让用户知道何时循环浏览 UI 或何时将焦点移动到他们已经发现的元素。

特别是,在重复使用的布局(例如 RecyclerView 对象)中的元素中包含其他文本或上下文信息,以便唯一标识每个子元素。

为此,请将内容描述设置为适配器实现的一部分,如下面的代码片段所示

Kotlin

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"
    }
}

Java

public class MovieRating {
    private String title;
    private int starRating;
    // ...
    public String getTitle() { return title; }
    public int getStarRating() { return starRating; }
}

public class MyMovieRatingsAdapter
        extends RecyclerView.Adapter<MyAdapter.MyRatingViewHolder> {
    private MovieRating[] myData;


    public static class MyRatingViewHolder extends RecyclerView.ViewHolder {
        public ImageView ratingView;
        public MyRatingViewHolder(ImageView iv) {
            super(iv);
            ratingView = iv;
        }
    }

    @Override
    public void onBindViewHolder(MyRatingViewHolder holder, int position) {
        MovieRating ratingData = myData[position];
        holder.ratingView.setContentDescription("Movie " + position + ": " +
                ratingData.getTitle() + ", " + ratingData.getStarRating() +
                " stars")
    }
}

相关内容组

如果您的应用显示构成自然组的多个 UI 元素(例如歌曲的详细信息或消息的属性),请将这些元素排列在一个容器中,该容器通常是 ViewGroup 的子类。将容器对象的 android:screenReaderFocusable 属性设置为 true,并将每个内部对象的 android:focusable 属性设置为 false。这样,辅助功能服务就可以依次宣布内部元素的内容描述,一次宣布一个。这种相关元素的整合有助于辅助技术用户更有效地发现屏幕上的信息。

以下代码片段包含相互关联的内容片段,因此容器元素(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>

由于辅助功能服务在单个话语中宣布内部元素的描述,因此务必使每个描述尽可能简短,同时仍然传达元素的含义。

注意:通常,应避免通过聚合其子元素的文本来创建组的内容描述。这样做会使组的描述变得脆弱,并且当子元素的文本发生变化时,组的描述可能不再与可见文本匹配。

在列表或网格上下文中,屏幕阅读器可能会合并列表或网格元素的子文本节点的文本。最好避免修改此公告。

嵌套组

如果您的应用界面显示多维信息(例如节日活动的每日列表),请在内部组容器上使用 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)及更高版本中,您可以为屏幕的窗格提供辅助功能友好的标题。出于辅助功能目的,窗格是窗口中视觉上不同的部分,例如片段的内容。为了使辅助功能服务了解窗格的窗口式行为,请为应用的窗格提供描述性标题。当窗格的外观或内容发生变化时,辅助功能服务可以向用户提供更详细的信息。

要指定窗格的标题,请使用 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" ... />

装饰性元素

如果 UI 中的元素仅用于视觉间距或视觉外观目的,请将其 android:importantForAccessibility 属性设置为 "no"

添加辅助功能操作

务必允许辅助功能服务的使用者轻松地在应用中执行所有用户流程。例如,如果用户可以滑动列表中的项目,则此操作也可以公开给辅助功能服务,以便用户有另一种方法来完成相同的用户流程。

使所有操作都可访问

TalkBack、语音访问或开关访问的用户可能需要其他方法来完成应用中的某些用户流程。对于与手势(例如拖放或滑动)相关的操作,您的应用可以以辅助功能服务用户可以访问的方式公开这些操作。

使用 辅助功能操作,应用可以为用户提供完成操作的替代方法。

例如,如果您的应用允许用户滑动项目,您还可以通过自定义辅助功能操作公开此功能,如下所示

Kotlin

ViewCompat.addAccessibilityAction(
    // View to add accessibility action
    itemView,
    // Label surfaced to user by an accessibility service
    getText(R.id.archive)
) { _, _ ->
    // Same method executed when swiping on itemView
    archiveItem()
    true
}

Java

ViewCompat.addAccessibilityAction(
    // View to add accessibility action
    itemView,
    // Label surfaced to user by an accessibility service
    getText(R.id.archive),
    (view, arguments) -> {
        // Same method executed when swiping on itemView
        archiveItem();
        return true;
    }
);

通过 自定义 辅助功能 操作 实现 用户 可以 通过 操作 菜单 访问 操作

使 可用 操作 易于理解

视图 支持 诸如 触摸 & 按住 之类的 操作 诸如 TalkBack 之类的 辅助功能服务 将其 宣布为 "双击并按住以长按。"

通用 公告 没有't 用户 提供 任何 关于 触摸 & 按住 操作 做什么 上下文

为了 使 公告 更具 描述性 您可以 替换 辅助功能 操作s 公告 如下:

Kotlin

ViewCompat.replaceAccessibilityAction(
    // View that contains touch & hold action
    itemView,
    AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_LONG_CLICK,
    // Announcement read by TalkBack to surface this action
    getText(R.string.favorite),
    null
)

Java

ViewCompat.replaceAccessibilityAction(
    // View that contains touch & hold action
    itemView,
    AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_LONG_CLICK,
    // Announcement read by TalkBack to surface this action
    getText(R.string.favorite),
    null
);

导致 TalkBack 宣布 "双击并按住以收藏," 帮助 用户 理解 操作 的目的

扩展 系统 窗口小部件

注意 设计 应用' UI, 使用 扩展 系统-提供的 控件 尽可能 靠近 Android' 层次结构 底部 系统-提供的 控件 靠近 层次结构 底部, 可访问性 功能 , 应用 可能 满足 可访问性 需求 扩展 这些 系统-提供的 控件 通用的 View, ViewCompat, Canvas, CanvasCompat 创建 自己的 控件 容易

如果 必须 直接 扩展 View Canvas , 可能 对于 高度 定制化 体验 游戏 级别 必要的, 请参阅 使自定义视图更易于访问

本节 使用 实现 一种 特殊 类型 Switch 的示例, 该示例称为 TriSwitch, 同时 遵循 扩展系统控件 的最佳实践 TriSwitch 对象 的工作原理 类似于 Switch 对象, 只是 每个 TriSwitch 实例 允许用户在三种可能的状态之间切换

从类层次结构底部开始扩展

Switch 对象 在其层次结构中继承自多个框架UI类:

View
 TextView
   Button
     CompoundButton
       Switch

最好让新的TriSwitch 类直接从Switch 类扩展 这样, Android可访问性框架 将提供TriSwitch 类所需的大多数可访问性功能

  • 可访问性操作:有关系统如何模拟对TriSwitch 对象执行的每种可能的用户信息输入的信息 (View 继承。)
  • 可访问性事件:有关TriSwitch 对象的外观在屏幕刷新或更新时可能发生的所有变化的信息 (View 继承。)
  • 特征:有关每个TriSwitch 对象的详细信息, 例如其显示的任何文本的内容 (TextView 继承。)
  • 状态信息:TriSwitch 对象当前状态的描述, 例如“选中”或“未选中” (CompoundButton 继承。)
  • 状态的文本描述:每个状态代表什么的基于文本的解释 (Switch 继承。)

来自Switch 及其超类的此行为几乎与TriSwitch 对象的行为相同 因此, 您的实现可以专注于将可能状态的数量从两个扩展到三个

定义自定义事件

扩展系统控件时, 您可能会更改用户与该控件交互方式的某个方面 定义这些交互更改非常重要, 以便可访问性服务可以更新应用的控件, 就像用户直接与控件交互一样

一般准则是, 对于覆盖的每个基于视图的回调, 还需要通过覆盖ViewCompat.replaceAccessibilityAction() 重新定义相应的可访问性操作 在应用的测试中, 可以通过调用ViewCompat.performAccessibilityAction() 验证这些重新定义的操作的行为

此原则如何适用于TriSwitch对象

与普通的Switch 对象不同, 轻触TriSwitch 对象会循环遍历三种可能的状态 因此, 相应的ACTION_CLICK 可访问性操作需要更新

Kotlin

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
    }
}

Java

public class TriSwitch extends Switch {
    // 0, 1, or 2
    private int currentState;

    public int getCurrentState() {
        return currentState;
    }

    public TriSwitch() {
        updateAccessibilityActions();
    }

    private void updateAccessibilityActions() {
        ViewCompat.replaceAccessibilityAction(this, ACTION_CLICK,
            action-label, (view, args) -> moveToNextState());
    }

    private void moveToNextState() {
        currentState = (currentState + 1) % 3;
    }
}

使用颜色以外的提示

为了帮助色觉障碍用户, 请使用颜色以外的提示来区分应用屏幕中的UI元素 这些技术可以包括使用不同的形状或大小, 提供文本或视觉图案, 或添加音频或触觉反馈以标记元素的差异

图 1 显示了活动的两个版本。一个版本仅使用颜色来区分工作流程中两个可能的动作。另一个版本使用了最佳实践,除了颜色之外还包括形状和文本,以突出显示这两个选项之间的差异。

图 1. 使用仅颜色(左)和使用颜色、形状和文本(右)创建 UI 元素的示例。

使媒体内容更易于访问

如果您正在开发包含媒体内容(例如视频剪辑或音频录制)的应用程序,请尝试支持不同类型的辅助功能需求的用户理解此材料。特别是,我们鼓励您执行以下操作

  • 包含允许用户暂停或停止媒体、更改音量和切换字幕(隐藏式字幕)的控件。
  • 如果视频呈现的信息对于完成工作流程至关重要,请以另一种格式(例如成绩单)提供相同的内容。

其他资源

要了解有关使您的应用程序更易于访问的更多信息,请参阅以下其他资源

代码实验室

博客文章