為了滿足使用者的無障礙需求,Android 架構可讓您建立無障礙服務,以便對使用者呈現應用程式內容,並代為操作應用程式。
Android 提供多種系統無障礙服務,包括以下項目:
- TalkBack:輔助低視能或失明的使用者。透過合成語音朗讀內容,並根據使用者手勢在應用程式中執行對應動作。
- 切換控制功能:輔助動作失能的使用者。醒目顯示互動性元素,並在使用者按下按鈕時執行對應動作,讓使用者只透過一或兩個按鈕就能控制裝置。
為了協助具有無障礙需求的使用者順利使用應用程式,應用程式必須採用本頁所述的最佳做法。這些最佳做法是以「提高應用程式的無障礙程度」一文所述的準則為基礎。
這些最佳做法可進一步提高應用程式的無障礙程度,如以下各節說明:
- 標籤元素
- 針對應用程式中每個有意義的互動性 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
元素中輸入的內容。您可以設定 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、Voice Access 或切換控制功能的使用者可能需要透過其他方法,完成應用程式中特定的使用者流程。如果是與手勢相關的動作 (如拖曳或滑動),您可以讓應用程式以無障礙服務使用者能使用的方式,提供這類動作。
透過無障礙功能動作,應用程式可讓使用者以其他方式完成操作。
舉例來說,如果您的應用程式允許使用者滑動項目,您也可以透過自訂無障礙功能動作提供這項功能,例如:
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 這類無障礙服務就會朗讀「輕觸兩下並長按」。
這則通用朗讀內容並未提供任何背景資訊,讓使用者瞭解按住動作的作用。
為了讓朗讀內容更加淺顯易懂,您可以依照下列方式替換無障礙動作的朗讀訊息:
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
(打造高度自訂的體驗或遊戲關卡,就可能必須這麼做),請參閱「在無障礙介面下使用自訂檢視區塊」一文。
本節使用的範例將實作名為 TriSwitch
的特殊類型 Switch
,同時遵循擴充系統小工具的最佳做法。TriSwitch
物件的運作方式與 Switch
物件類似,只不過 TriSwitch
的每個例項可讓使用者在三種可能狀態之間切換。
從類別階層的底部擴充
Switch
物件會沿用階層中的多個架構 UI 類別:
View ↳ TextView ↳ Button ↳ CompoundButton ↳ Switch
建議您直接從 Switch
類別擴充新的 TriSwitch
類別。如此一來,Android 無障礙架構就能提供 TriSwitch
類別所需的大部分無障礙功能:
- 無障礙功能動作:向系統告知無障礙服務如何模擬每種可能針對
TriSwitch
物件執行的使用者輸入內容 (從View
沿用而來)。 - 無障礙功能事件:向無障礙服務告知
TriSwitch
物件外觀在螢幕刷新或更新時各種可能的改變方式 (從View
沿用而來)。 - 特性:每個
TriSwitch
物件的詳細資料,例如任何所顯示文字的內容 (從TextView
沿用而來)。 - 狀態資訊:
TriSwitch
物件目前狀態的說明,例如「已勾選」或「未勾選」(從CompoundButton
沿用而來)。 - 狀態的文字說明:以文字說明各個狀態所代表的意義 (從
Switch
沿用而來)。
Switch
及其父類別的行為與 TriSwitch
物件的行為幾乎相同,因此在實作時,您可以專注將可能的狀態數量從 2 個擴充到 3 個。
定義自訂事件
擴充系統小工具時,您可能會變更使用者與小工具互動方式的某個面向。請務必定義這些互動方式的變更,讓無障礙服務能夠更新應用程式的小工具,如同使用者直接與小工具互動一樣。
一般而言,每覆寫一個以檢視元素為基礎的回呼時,您也必須覆寫 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 顯示同一活動的兩個版本。其中一個版本只使用顏色來區分工作流程中的兩個可能動作。另一個版本則採用最佳做法,除了顏色以外還利用形狀和文字來凸顯兩個選項的差異:
提高媒體內容的無障礙程度
如果您開發的應用程式包含媒體內容 (例如短片或音訊錄音),請設法為具有不同類型無障礙需求的使用者提供支援,協助他們理解這些內容。我們尤其建議您採取下列做法:
- 加入控制項,讓使用者暫停或停止播放媒體、變更音量及切換字幕。
- 如果影片中的資訊是完成工作流程的關鍵,請以轉錄稿等替代格式提供相同內容。
其他資源
如要進一步瞭解如何提高應用程式的無障礙程度,請參閱下列其他資源: