提升應用程式無障礙程度的基本原則

某些 Android 裝置使用者有不一樣的無障礙需求。為了協助具有共同無障礙需求的特定群體,Android 架構可讓開發人員建立「無障礙服務」,透過這類服務向使用者提供應用程式內容及代為操作應用程式。

Android 提供多種系統無障礙服務,包括:

  • TalkBack:協助低視能或失明的使用者。透過合成語音朗讀內容,並根據使用者手勢在應用程式中執行對應操作。
  • 切換控制功能:協助動作失能的使用者。醒目顯示互動性元素,並在使用者按下按鈕時執行對應動作。可讓使用者僅透過 1 或 2 個按鈕控制裝置。

為了協助具有無障礙需求的使用者順利使用應用程式,應用程式應採用本頁所述的最佳做法,這些最佳做法是以「提高應用程式的無障礙程度」一文所述的要點為基礎。

以下每個最佳做法都能進一步提高應用程式的無障礙程度:

標籤元素
針對應用程式中每個有意義的互動性 UI 元素,使用者應該要能夠瞭解其內容和用途。
使用或擴充系統小工具
以 Android 架構隨附的檢視元素為基礎進行建構作業,而不要建立自訂檢視。Android 架構的檢視和小工具類別已能提供應用程式需要的大部分無障礙功能。
使用顏色以外的提示
使用者應該要能清楚區分 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 元素中輸入的內容。在搭載 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" ... />

集合中的元素

為集合中的元素加上標籤時,所有標籤都不得重複。這樣系統的無障礙服務在朗讀標籤時,才能僅參照一個螢幕上的元素。這種關聯可讓使用者知道自己已循環瀏覽 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>

由於無障礙服務會一次讀完內部元素的說明,因此請務必盡量讓每則說明保持精簡,同時清楚傳達元素的含義。

自訂群組標籤

如有需要,您可以提供群組本身的內容說明,藉此覆寫群組內部元素說明的平台預設分組和排序設定。

以下程式碼片段是自訂群組說明的範例:

<!-- 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) 以上版本中,您可以為螢幕的「窗格」提供能夠滿足無障礙需求的標題。就無障礙功能來說,窗格是視窗中看起來與眾不同的部分,例如片段的內容。為了讓無障礙服務瞭解窗格的類視窗行為,您應該為應用程式的窗格提供描述性的標題。這樣當窗格的外觀或內容有所改變時,無障礙服務就能為使用者提供更精細的資訊。

如要指定窗格的標題,請使用 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:contentDescription 屬性設為 "null"

如果應用程式僅支援搭載 Android 4.1 (API 級別 16) 以上版本的裝置,您可以改為將這些純裝飾性元素的 android:importantForAccessibility 屬性設為 "no"

擴充系統小工具

重點提醒:當您設計應用程式的 UI 時,請盡可能使用或擴充 Android 類別階層底部的系統提供小工具。階層底部的系統提供小工具已提供應用程式所需的大部分無障礙功能。相較於使用較一般性質的 ViewViewCompatCanvasCanvasCompat 類別自行建立小工具,擴充系統提供的小工具更簡單。

如需直接擴充 ViewCanvas (如要打造高度自訂的體驗或遊戲關卡,就可能必須這麼做),請參閱「在無障礙介面下使用自訂檢視區塊」一文。

本節說明如何實作名為 TriSwitch 的特殊類型 SwitchTriSwitch 物件的運作方式與 Switch 物件類似,只不過 TriSwitch 的每個例項可讓使用者在 3 種可能狀態之間切換。

從類別階層的底部擴充

Switch 物件會沿用階層中的多個架構 UI 類別:

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 無障礙動作必須有所更新:

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 元素的範例 (左),以及使用顏色、形狀和文字建立 UI 元素的範例 (右)

提高媒體內容的無障礙程度

如果您開發的應用程式包含媒體內容 (例如短片或音訊錄音),請設法為具有不同類型無障礙需求的使用者提供支援,協助他們理解這些內容。我們尤其建議您採取下列做法:

  • 加入控制項,讓使用者暫停或停止媒體、變更音量,以及切換字幕。
  • 如果影片中的資訊是完成工作流程的關鍵,請以轉錄稿等替代格式提供相同內容。

其他資源

如要進一步瞭解如何提高應用程式的無障礙程度,請參閱下列其他資源:

程式碼研究室

網誌文章