API 預設值

Material、Compose UI 和 Foundation API 會在預設情況下實作並提供許多無障礙做法。這些元素包含內建語意,可根據其特定角色和功能提供服務,也就是說,只要稍微調整或不調整,即可提供大部分的無障礙支援。

使用適當的 API 來滿足適當的用途,通常表示元件會提供預先定義的無障礙行為,涵蓋標準使用情境,但請務必再次確認這些預設值是否符合您的無障礙需求。如果不是,Compose 也提供其他方式,可滿足更具體的需求。

瞭解 Compose API 中的預設無障礙語義和模式,有助於您瞭解如何在設計時考量無障礙功能,以及如何在更多自訂元件中支援無障礙功能。

觸控目標大小下限

任何使用者可點擊、輕觸或互動的螢幕元素,都必須設為適當大小,方便使用者進行互動。設定這些元素的大小時,請務必將大小下限設為 48dp,以便正確遵循「Material Design 無障礙指南」。

Material 元件,例如 CheckboxRadioButtonSwitchSliderSurface:請在內部設定此大小下限,但僅限元件可以接收使用者動作時。舉例來說,如果 CheckboxonCheckedChange 參數設為非空值,系統就會在其中包含至少 48 dp 的寬度和高度。

@Composable
private fun CheckableCheckbox() {
    Checkbox(checked = true, onCheckedChange = {})
}

寬度和高度為 48 dp 的核取方塊,並設有預設邊框。
圖 1. 具有預設邊距的核取方塊。

onCheckedChange 參數設為空值時,系統不會納入邊框間距,因為該元件無法直接互動。

@Composable
private fun NonClickableCheckbox() {
    Checkbox(checked = true, onCheckedChange = null)
}

沒有邊框間距的核取方塊。
圖 2. 沒有邊框的核取方塊。

導入 SwitchRadioButtonCheckbox 等選取控制項時,您通常會將可點擊的行為推送至父項容器,並將可組合項的點擊回呼設為 null,然後在可組合父項中新增 toggleableselectable 輔助鍵。

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

文字「Option」旁邊的核取方塊,會在選取和取消選取之間切換。
圖 3. 可點選的核取方塊。

如果可點擊的可組合項大小小於最低觸控目標大小,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)
        )
    }
}

可點選的非常小型方塊,只要輕觸方塊旁邊,即可展開為更大的觸控目標。
圖 4. 一個可點選的非常小型方塊,會擴大為更大的觸控目標。

為避免不同可組合項的觸控區域重疊,請一律為可組合項使用足夠大的最小尺寸。在範例中,則意味著使用 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)
        )
    }
}

上一個範例中的小方塊大小會增加,以建立更大的觸控目標。
圖 5. 較大的方塊觸控目標。

圖形元素

在您定義 ImageIcon 可組合項時,Android 架構無法自動瞭解應用程式正在顯示的內容。您必須傳送圖形元素的文字說明。

請設想一個螢幕,可供使用者與好友分享目前的頁面。這個畫面包含可點擊的共用圖示:

可點擊的圖示列,其中醒目顯示「共用」圖示。
圖 6. 一列可點選的圖示,其中已選取「分享」圖示。

單憑藉圖示,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 主要用於圖形元素,例如圖片。ButtonText 等 Material 元件,以及 clickabletoggleable 等可執行行為,都會附帶其他預先定義的語意,用於說明其內在行為,並可透過其他 Compose API 進行變更。

互動式元素

Material 和 Foundation Compose API 會透過 clickabletoggleable 修飾符 API,製作使用者可互動的 UI 元素。由於可互動的元件可能包含多個元素,因此 clickabletoggleable 會預設合併子項的語意,以便將元件視為單一邏輯實體。

舉例來說,Material Button 可能包含子項圖示和部分文字。Material Button 預設會合併子項語意,而非將子項視為個別項目,以便無障礙服務依據這些項目進行分組:

未合併和已合併子項語意的按鈕。
圖 7. 未合併和已合併子項語意的按鈕。

同樣地,使用 clickable 修飾符也會導致可組合函式將子項的語意合併為單一實體,並透過對應的動作表示法傳送至無障礙服務:

Row(
    // Uses `mergeDescendants = true` under the hood
    modifier = Modifier.clickable { openArticle() }
) {
    Icon(
        painter = painterResource(R.drawable.ic_logo),
        contentDescription = "Open",
    )
    Text("Accessibility in Compose")
}

您也可以在可點選的父項上設定特定 onClickLabel,為無障礙服務提供額外資訊,並提供更精緻的動作表示方式:

Row(
    modifier = Modifier
        .clickable(onClickLabel = "Open this article") {
            openArticle()
        }
) {
    Icon(
        painter = painterResource(R.drawable.ic_logo),
        contentDescription = "Open"
    )
    Text("Accessibility in Compose")
}

以 TalkBack 為例,這個 clickable 輔助鍵及其點擊標籤可讓 TalkBack 提供「輕觸兩下即可開啟此文章」的動作提示,而非更通用的「輕觸兩下即可啟用」的預設回饋。

這項意見回饋會因動作類型而異。長按會提供 TalkBack 提示「輕觸兩下並長按」,接著會顯示標籤:

Row(
    modifier = Modifier
        .combinedClickable(
            onLongClickLabel = "Bookmark this article",
            onLongClick = { addToBookmarks() },
            onClickLabel = "Open this article",
            onClick = { openArticle() },
        )
) {}

在某些情況下,您可能無法直接存取 clickable 修飾符 (例如,當它設在較低的巢狀層級時),但仍想將通知標籤從預設值變更。為此,請使用 semantics 修飾符,在其中設定點擊標籤,以便修改動作表示法,並將 clickable 設定與修改公告分開:

@Composable
private fun ArticleList(openArticle: () -> Unit) {
    NestedArticleListItem(
        // Clickable is set separately, in a nested layer:
        onClickAction = openArticle,
        // Semantics are set here:
        modifier = Modifier.semantics {
            onClick(
                label = "Open this article",
                action = {
                    // Not needed here: openArticle()
                    true
                }
            )
        }
    )
}

在這種情況下,您不需要兩次傳遞點擊動作,因為 clickableButton 等現有的 Compose API 會為您處理這項操作。這是因為合併邏輯可確保系統會針對現有資訊採取最外層的修飾符標籤和動作。

在前述範例中,openArticle() 點擊動作會由 NestedArticleListItem 自動傳遞至其 clickable 語意,並可在第二個語意修飾符動作中保留空值。不過,點擊標籤是從第二個語意修飾符 onClick(label = "Open this article") 取得,因為第一個語意修飾符中並未出現該標籤。

您可能會遇到以下情況:您預期子語義會合併至父語義,但實際上並未發生。詳情請參閱「合併和清除」一節。

自訂元件

就自訂元件而言,原則上,您應在「材質」程式庫或其他 Compose 程式庫中查看類似元件的實作方式,並模仿或修改其無障礙行為 (如果可行)。

舉例來說,如果您要將「材質」Checkbox 替換成自己的實作項目,查看現有的核取方塊實作項目,系統會提醒您新增 triStateToggleable 修飾符,以便處理此元件的無障礙屬性。

此外,請多多利用基礎修飾符,因為前者內建便有考慮無障礙功能,以及本節所述的現有 Compose 做法。

您也可以在「清除並設定語意」一節中找到自訂切換按鈕元件的範例,以及如何在API 規範中支援自訂元件的無障礙功能的詳細資訊。