遍歷順序是指無障礙服務在 UI 元素中瀏覽的順序。在 Compose 應用程式中,元素會按照預期的閱讀順序排列,通常是從左到右,然後從上到下。不過,在某些情況下,Compose 可能需要額外的提示,才能判斷正確的讀取順序。
isTraversalGroup
和 traversalIndex
是語意屬性,可讓您在 Compose 的預設排序演算法不足的情況下,影響無障礙服務的遍歷順序。isTraversalGroup
會找出需要自訂的語意上重要的群組,而 traversalIndex
會調整這些群組內的個別元素順序。您可以單獨使用 isTraversalGroup
來表示群組中所有元素應一併選取,也可以搭配 traversalIndex
進一步自訂。
在應用程式中使用 isTraversalGroup
和 traversalIndex
來控制螢幕閱讀器的檢索順序。
用於遍歷的群組元素
isTraversalGroup
是布林值屬性,可定義語意節點是否為遍歷群組。這種節點的功能是做為邊界或邊框,用於整理節點的子項。
在節點上設定 isTraversalGroup = true
表示在移動至其他元素之前,會先造訪該節點的所有子項。您可以在非螢幕閱讀器可聚焦的節點 (例如欄、列或方塊) 上設定 isTraversalGroup
。
以下範例使用 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 ) } }
程式碼會產生類似以下的輸出內容:

由於未設定語意,螢幕閱讀器的預設行為是從左到右、從上到下瀏覽元素。由於這個預設值,TalkBack 會以錯誤的順序朗讀句子片段:
「This sentence is in」→「This sentence is」→「the left column」。→「在右側」。
如要正確排序片段,請修改原始程式碼片段,將 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 會以正確的順序讀出句子片段:
「This sentence is in」→「the left column」。→「This sentence is」→「on the right.」
自訂檢索順序
traversalIndex
是浮點屬性,可讓您自訂 TalkBack 檢視順序。如果將元素分組後,TalkBack 仍無法正常運作,請使用 traversalIndex
搭配 isTraversalGroup
,進一步自訂螢幕閱讀器的順序。
traversalIndex
屬性具有下列特性:
- 系統會優先處理
traversalIndex
值較低的元素。 - 可以是正面或負面。
- 預設值為
0f
。 - 為了讓遍歷索引影響遍歷行為,您必須在可供無障礙服務選取及聚焦的元件上設定索引,例如文字或按鈕等畫面元素。
- 舉例來說,如果只開啟
traversalIndex
,Column
就不會生效,除非資料欄也設有isTraversalGroup
。
- 舉例來說,如果只開啟
以下範例說明如何搭配使用 traversalIndex
和 isTraversalGroup
。
錶面是標準檢查順序無法運作的常見情境。本節的範例是時間挑選器,使用者可在時鐘面上瀏覽數字,並選取小時和分鐘時段的數字。

在下列簡化的程式碼片段中,有一個 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
元素之後讀取。
API 注意事項
使用 traversal API 時,請考量下列事項:
isTraversalGroup = true
應設在包含已分組元素的父項上。traversalIndex
應設在包含語意且會由無障礙服務選取的子元件上。- 請確認您要調查的所有元素都位於相同的
zIndex
層級,因為這也會影響語意和檢索順序。 - 請確認沒有不必要的語意合併,因為這可能會影響要套用哪些元件遍歷索引。
為您推薦
- 注意:系統會在 JavaScript 關閉時顯示連結文字
- Compose 中的無障礙功能
- [Compose 中的 Material Design 2][19]
- 測試 Compose 版面配置