根據預設,系統會在 Compose 應用程式中實作無障礙螢幕閱讀器行為
原本的閱讀順序,通常從左到右,再從上到底部。
但某些類型的應用程式版面配置,演算法無法判斷這類版面配置
讀取順序,而不額外提示在以觀看為基礎的應用程式中,你可以:
如要修正這類問題,請使用 traversalBefore
和 traversalAfter
屬性。
從 Compose 1.5 開始,Compose 提供同樣彈性的 API,但
新的概念模型
isTraversalGroup
和 traversalIndex
是語意屬性,
可讓您控制無障礙和 TalkBack 焦點順序,
不適用預設排序演算法isTraversalGroup
識別
語意的重要群組,而 traversalIndex
則會調整
個別元素你可以單獨使用「isTraversalGroup
」。
或使用 traversalIndex
,進一步自訂內容。
在以下應用程式中使用isTraversalGroup
和 traversalIndex
:
應用程式,來控制螢幕閱讀器的周遊順序。
使用 isTraversalGroup
將元素分組
isTraversalGroup
是一個布林值屬性,用於定義是否提供語意
節點為週遊群組這類節點會為其函式提供服務
做為組織節點子項的邊界或邊界。
在節點上設定 isTraversalGroup = true
,代表該節點的所有子項
才會在移動到其他元素前
先造訪過其他元素您可以將isTraversalGroup
設為
非螢幕閱讀器可聚焦節點,例如 Column、Rows 或 Boxes。
以下範例使用 isTraversalGroup
。它會發出四個文字元素。
左側兩個元素屬於一個 CardBox
元素,而右側的兩個元素
屬於另一個 CardBox
元素:
// CardBox() function takes in top and bottom sample text. @Composable fun CardBox( topSampleText: String, bottomSampleText: String, modifier: Modifier = Modifier ) { Box(modifier) { Column { Text(topSampleText) Text(bottomSampleText) } } } @Composable fun TraversalGroupDemo() { val topSampleText1 = "This sentence is in " val bottomSampleText1 = "the left column." val topSampleText2 = "This sentence is " val bottomSampleText2 = "on the right." Row { CardBox( topSampleText1, bottomSampleText1 ) CardBox( topSampleText2, bottomSampleText2 ) } }
程式碼會產生類似以下的輸出內容:
![具有兩欄文字的版面配置,左欄顯示「This」
語句在左欄中而右欄顯示「這個句子在右邊」](https://developer.android.com/static/develop/ui/compose/images/a11y-istraversalgroup-example.png?authuser=0000&hl=zh-tw)
由於未設定語意,因此螢幕閱讀器預設行為為 從左到右,由上而下掃遍元素因此 預設情況下,TalkBack 會以錯誤的順序讀出語句片段:
「這個句子是」→「這個句子是」→ 「左欄。」→ " 。」
為確保片段正確排序,請修改原始程式碼片段以設定
isTraversalGroup
至 true
:
@Composable fun TraversalGroupDemo2() { val topSampleText1 = "This sentence is in " val bottomSampleText1 = "the left column." val topSampleText2 = "This sentence is" val bottomSampleText2 = "on the right." Row { CardBox( // 1, topSampleText1, bottomSampleText1, Modifier.semantics { isTraversalGroup = true } ) CardBox( // 2, topSampleText2, bottomSampleText2, Modifier.semantics { isTraversalGroup = true } ) } }
因為 isTraversalGroup
已明確為每個 CardBox
上設定,所以 CardBox
排序元素時會套用邊界。在這個範例中,
會先朗讀 CardBox
,後面接著正確的 CardBox
。
現在,TalkBack 會以正確順序朗讀語句片段:
「這個句子是」→ 「左欄。」→「這個句子是」→ " 。」
進一步自訂週遊順序
traversalIndex
是浮動屬性,可讓您自訂 TalkBack
遍歷順序。如果將多個元素組合在一起是 TalkBack 的功能
可正常運作,請將 traversalIndex
與
isTraversalGroup
,可進一步自訂螢幕閱讀器的排序方式。
traversalIndex
屬性具備下列特性:
traversalIndex
值較低的元素會優先顯示。- 可以是正數或負數。
- 預設值為
0f
。 - 只會影響螢幕閱讀器可聚焦的節點,例如螢幕上的元素,
文字或按鈕。舉例來說,如果在資料欄上僅設定
traversalIndex
, 除非同時對資料欄設定isTraversalGroup
,否則沒有任何效果。
以下範例說明如何使用 traversalIndex
和
一起isTraversalGroup
。
範例: Traverse 錶面
錶面是標準週遊順序並未得到的常見情境 這些研究有助於我們找出 能引導後續作業的標準本節中的範例是時間挑選器,可讓使用者周遊 透過錶面上的數字並選擇小時與分鐘的數字 版位。
![顯示時間挑選器的錶面。](https://developer.android.com/static/develop/ui/compose/images/a11y-clock-face.png?authuser=0000&hl=zh-tw)
在以下簡化的程式碼片段中,CircularLayout
含有 12
數字將會從 12 開始繪製,並順時針移動圓環:
@Composable fun ClockFaceDemo() { CircularLayout { repeat(12) { hour -> ClockText(hour) } } } @Composable private fun ClockText(value: Int) { Box(modifier = Modifier) { Text((if (value == 0) 12 else value).toString()) } }
因為錶面不會以預設的左至右從邏輯上讀取, TalkBack 會由上至下依序讀出數字。更正 請使用遞增計數器值,如以下程式碼片段所示:
@Composable fun ClockFaceDemo() { CircularLayout(Modifier.semantics { isTraversalGroup = true }) { repeat(12) { hour -> ClockText(hour) } } } @Composable private fun ClockText(value: Int) { Box(modifier = Modifier.semantics { this.traversalIndex = value.toFloat() }) { Text((if (value == 0) 12 else value).toString()) } }
如要正確設定週遊順序,請先將 CircularLayout
設為
遍歷群組並設定 isTraversalGroup = true
然後,每個時鐘文字
繪製在版面配置上,將其對應的 traversalIndex
設為計數器
值。
因為計數器值會持續增加
traversalIndex
會隨著螢幕上加入數字而變大,也就是時鐘值 0
的 traversalIndex
為 0,時鐘值 1 的 traversalIndex
為 1。
這樣一來,TalkBack 的朗讀順序就會設定成順序。現在的數值
系統會按照預期順序讀取 CircularLayout
內的內容。
因為已設定的 traversalIndexes
僅與其他檔案相關
在相同分組內索引時,其餘畫面排序方式
以保留不變換句話說,上述程式碼中的語意變更
程式碼片段只會修改錶面內
已設定 isTraversalGroup = true
。
請注意,如未將 CircularLayout's
語意設為 isTraversalGroup =
true
,traversalIndex
變更仍會套用。不過,如果沒有
CircularLayout
可繫結它們,系統會讀取錶面的十二位數
最後,在造訪完畫面上所有其他元素後。發生這種情況
因為所有其他元素的預設 traversalIndex
為 0f
,且
時鐘文字元素會在所有其他 0f
元素之後讀取。
範例:自訂懸浮動作按鈕的周遊順序
在此範例中,traversalIndex
和 isTraversalGroup
控制
Material Design 懸浮動作按鈕 (FAB) 的遍歷順序。基礎
以下範例的版面配置如下:
![版面配置含有頂端應用程式列、範例文字、懸浮動作按鈕和
底部應用程式列](https://developer.android.com/static/develop/ui/compose/images/a11y-fab-example.png?authuser=0000&hl=zh-tw)
根據預設,本範例的版面配置的 TalkBack 順序如下:
頂端應用程式列 → 範例文字 0 到 6 → 懸浮動作按鈕 (FAB) → 底部 應用程式列
建議讓螢幕閱讀器先專注於懸浮動作按鈕 (FAB),如要設定
對懸浮動作按鈕 (FAB) 等 Material 元素上的 traversalIndex
執行下列操作:
@Composable fun FloatingBox() { Box(modifier = Modifier.semantics { isTraversalGroup = true; traversalIndex = -1f }) { FloatingActionButton(onClick = {}) { Icon(imageVector = Icons.Default.Add, contentDescription = "fab icon") } } }
在這個程式碼片段中,
isTraversalGroup
已設為「true
」,並在同一個方塊中設定「traversalIndex
」
(-1f
低於預設值 0f
) 表示浮動方塊
才出現在畫面上
接著,您可以將浮動方塊和其他元素放入 Scaffold 中 實作 Material Design 版面配置:
@OptIn(ExperimentalMaterial3Api::class) @Composable fun ColumnWithFABFirstDemo() { Scaffold( topBar = { TopAppBar(title = { Text("Top App Bar") }) }, floatingActionButtonPosition = FabPosition.End, floatingActionButton = { FloatingBox() }, content = { padding -> ContentColumn(padding = padding) }, bottomBar = { BottomAppBar { Text("Bottom App Bar") } } ) }
TalkBack 會以下列順序與元素互動:
懸浮動作按鈕 (FAB) → 頂端應用程式列 → 範例文字 0 到 6 → 底部應用程式列
其他資源
- 無障礙功能:基礎概念和 所有 Android 應用程式開發通用的技巧
- 打造無障礙應用程式:重要步驟 讓你的應用程式更容易使用
- 提升應用程式品質的原則 無障礙功能:確保 提升應用程式的無障礙程度
- 無障礙設定測試: Android 無障礙功能的測試原則和工具