為了協助具有無障礙需求的使用者順利使用您的應用程式,您設計應用程式時可支援重要的無障礙功能需求。
考量最低觸控目標大小
任何使用者可點擊、輕觸或進行互動的螢幕元素,都必須設為適當大小,方便使用者進行互動。設定這些元素的大小時,請務必將大小下限設為 48dp,以便正確遵循「質感設計無障礙指南」。
Material Design 元件 (例如 Checkbox
、RadioButton
、Switch
、Slider
和 Surface
) 可在內部設定這個最小尺寸,但前提是元件可以接收使用者動作。舉例來說,如果 Checkbox
的 onCheckedChange
參數設為非空值,核取方塊會包含邊框間距,其寬度和高度至少為 48 dp。
@Composable private fun CheckableCheckbox() { Checkbox(checked = true, onCheckedChange = {}) }
當 onCheckedChange
參數設為空值時,系統不會納入邊框間距,因為該元件無法直接互動。
@Composable private fun NonClickableCheckbox() { Checkbox(checked = true, onCheckedChange = null) }
導入 Switch
、RadioButton
或 Checkbox
等選取控制項時,您通常會將可點擊的行為推送至父項容器,並將可組合項的點擊回呼設為 null
,然後在可組合父項中新增 toggleable
或 selectable
修飾符。
@Composable private fun CheckableRow() { MaterialTheme { var checked by remember { mutableStateOf(false) } Row( Modifier .toggleable( value = checked, role = Role.Checkbox, onValueChange = { checked = !checked } ) .padding(16.dp) .fillMaxWidth() ) { Text("Option", Modifier.weight(1f)) Checkbox(checked = checked, onCheckedChange = null) } } }
如果可點擊的可組合項大小小於最低觸控目標大小,Compose 仍會增加觸控目標大小。方法是將觸控目標大小擴大到可組合項的邊界之外。
以下範例包含非常小的可點擊 Box
。觸控目標區域會自動擴展到 Box
的邊界之外,因此輕觸 Box
旁邊的位置仍會觸發點擊事件。
@Composable private fun SmallBox() { var clicked by remember { mutableStateOf(false) } Box( Modifier .size(100.dp) .background(if (clicked) Color.DarkGray else Color.LightGray) ) { Box( Modifier .align(Alignment.Center) .clickable { clicked = !clicked } .background(Color.Black) .size(1.dp) ) } }
為避免不同可組合項的觸控區域重疊,請一律為可組合項使用夠大的最小尺寸。在本範例中,則意味著使用 sizeIn
修飾符設定內部方塊的最小尺寸:
@Composable private fun LargeBox() { var clicked by remember { mutableStateOf(false) } Box( Modifier .size(100.dp) .background(if (clicked) Color.DarkGray else Color.LightGray) ) { Box( Modifier .align(Alignment.Center) .clickable { clicked = !clicked } .background(Color.Black) .sizeIn(minWidth = 48.dp, minHeight = 48.dp) ) } }
新增點擊標籤
您可以使用點擊標籤,為可組合項的點擊行為新增語意含義。點擊標籤可說明使用者與可組合項的互動情形。無障礙服務會根據點擊標籤,向有特定需求的使用者描述應用程式。
在 clickable
修飾符中傳遞參數,藉此設定點擊標籤:
@Composable private fun ArticleListItem(openArticle: () -> Unit) { Row( Modifier.clickable( // R.string.action_read_article = "read article" onClickLabel = stringResource(R.string.action_read_article), onClick = openArticle ) ) { // .. } }
如果您無法存取可點擊的修飾詞,請在「semantics」修飾符中設定點擊標籤:
@Composable private fun LowLevelClickLabel(openArticle: () -> Boolean) { // R.string.action_read_article = "read article" val readArticleLabel = stringResource(R.string.action_read_article) Canvas( Modifier.semantics { onClick(label = readArticleLabel, action = openArticle) } ) { // .. } }
描述視覺元素
在您定義 Image
或 Icon
可組合項時,Android 架構無法自動瞭解應用程式顯示的內容。您必須傳送視覺元素的文字說明。
請設想一個螢幕,可供使用者與好友分享目前的頁面。這個畫麵包含一個可點選的分享圖示:
單憑圖示,Android 架構無法向視障使用者描述。Android 架構需要圖示的其他文字說明。
contentDescription
參數會描述視覺元素,請使用會向使用者顯示的本地化字串。
@Composable private fun ShareButton(onClick: () -> Unit) { IconButton(onClick = onClick) { Icon( imageVector = Icons.Filled.Share, contentDescription = stringResource(R.string.label_share) ) } }
有些視覺元素只是單純裝飾用途,而您可能不想向使用者傳達這些元素。將 contentDescription
參數設為 null
時,您必須向 Android 架構指出這個元素沒有相關聯的動作或狀態。
@Composable private fun PostImage(post: Post, modifier: Modifier = Modifier) { val image = post.imageThumb ?: painterResource(R.drawable.placeholder_1_1) Image( painter = image, // Specify that this image has no semantic meaning contentDescription = null, modifier = modifier .size(40.dp, 40.dp) .clip(MaterialTheme.shapes.small) ) }
請依此判斷特定視覺元素是否需要 contentDescription
。請自問這個元素是否傳送使用者執行工作所需的資訊。如果不是,最好將該說明省略。
合併元素
無障礙服務 (例如 TalkBack 和切換控制功能) 可讓使用者在不同元素間移動畫面聚焦。元素只聚焦於適當的精細度很重要。如果畫面上的每個低階可組合項各自聚焦,使用者就必須互動許多次才能在畫面上移動。如果元素過度合併,使用者可能不知道哪些元素屬於同一群組
將 clickable
修飾符套用至可組合項時,Compose 會自動合併可組合項包含的所有元素。這個做法也適用於 ListItem
;清單項目中的元素會合併,無障礙服務也可將其視為單一元素。
您可以將一組可組合元件組成一個邏輯群組,但該群組不可點擊,也不是清單項目的一部分。您仍希望無障礙服務將其視為單一元素。舉例來說,假設可組合項會顯示使用者的顯示圖片、使用者名稱和一些額外資訊:
您可以在 semantics
輔助鍵中使用 mergeDescendants
參數,讓 Compose 合併這些元素。這樣一來,無障礙服務只會選取合併的元素,並合併子系的所有語意屬性。
@Composable private fun PostMetadata(metadata: Metadata) { // Merge elements below for accessibility purposes Row(modifier = Modifier.semantics(mergeDescendants = true) {}) { Image( imageVector = Icons.Filled.AccountCircle, contentDescription = null // decorative ) Column { Text(metadata.author.name) Text("${metadata.date} • ${metadata.readTimeMinutes} min read") } } }
無障礙服務現在一次聚焦於整個容器,並合併其內容:
新增自訂動作
請查看下列清單項目:
使用 TalkBack 等螢幕閱讀器聆聽螢幕上顯示的內容時,會先選取整個項目,然後選取書籤圖示。
在冗長的清單,重複性可能變得很高。更好的做法是定義自訂動作,讓使用者將項目加入書籤。請注意,您也必須明確移除書籤圖示本身的行為,以確保無障礙服務未選取書籤圖示。方法是使用 clearAndSetSemantics
修飾符完成:
@Composable private fun PostCardSimple( /* ... */ isFavorite: Boolean, onToggleFavorite: () -> Boolean ) { val actionLabel = stringResource( if (isFavorite) R.string.unfavorite else R.string.favorite ) Row( modifier = Modifier .clickable(onClick = { /* ... */ }) .semantics { // Set any explicit semantic properties customActions = listOf( CustomAccessibilityAction(actionLabel, onToggleFavorite) ) } ) { /* ... */ BookmarkButton( isBookmarked = isFavorite, onClick = onToggleFavorite, // Clear any semantics properties set on this node modifier = Modifier.clearAndSetSemantics { } ) } }
說明元素的狀態
可組合項可以定義語意的 stateDescription
,供 Android 架構用來解讀可組合項所處的狀態。舉例來說,可切換的可組合項可能處於「已勾選」或「未勾選」狀態。在部分情況下,您可能會想覆寫 Compose 使用的預設狀態說明標籤。您可以先明確指定狀態說明標籤,再將可組合項定義為可切換:
@Composable private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) { val stateSubscribed = stringResource(R.string.subscribed) val stateNotSubscribed = stringResource(R.string.not_subscribed) Row( modifier = Modifier .semantics { // Set any explicit semantic properties stateDescription = if (selected) stateSubscribed else stateNotSubscribed } .toggleable( value = selected, onValueChange = { onToggle() } ) ) { /* ... */ } }
定義標題
應用程式有時會在同一個畫面中,使用可捲動的容器顯示大量內容。舉例來說,畫面可能會顯示使用者正在閱讀文章的完整內容:
無障礙功能的使用者難以瀏覽這類畫面。為協助瀏覽,請指出哪些元素是標題。在上述範例中,每個子區段標題都可以定義為無障礙功能的標題。部分無障礙服務 (例如 Talkback) 可讓使用者從標題直接瀏覽到標題。
在 Compose 中,您可以透過定義 semantics
屬性來表示可組合項為「標題」:
@Composable private fun Subsection(text: String) { Text( text = text, style = MaterialTheme.typography.headlineSmall, modifier = Modifier.semantics { heading() } ) }
處理自訂可組合項
當您將應用程式中的特定 Material 元件替換成自訂版本時,請務必考量無障礙功能的注意事項。
假設您將 Material Checkbox
替換成自己的實作項目,您可能會忘記新增用於處理此元件無障礙屬性的 triStateToggleable
修飾符。
原則上,請查看「材質」程式庫中該元件的實作方式,並模仿能找到的任何無障礙功能。此外,請多多利用基礎修飾符 (而非 UI 層級的修飾符),因為前者內建便有考慮無障礙功能。
使用多個無障礙服務測試自訂元件實作項目,驗證其行為。
其他資源
- 無障礙設計:所有 Android 應用程式開發作業常見的基本概念和技巧
- 建構無障礙應用程式:您可以透過幾個重要步驟來打造無障礙應用程式
- 提升應用程式無障礙程度的基本原則:改善應用程式的無障礙設計時,請記住下列重要原則
- 無障礙設計測試:測試 Android 無障礙功能的原則和工具