遍歷順序是指無障礙服務瀏覽 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 會以錯誤的順序讀出句子片段:
「這個句子在」→「這個句子」→「左欄」。→「在右側」
如要正確排序片段,請修改原始程式碼片段,將 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。 - 如要讓周遊索引影響周遊行為,必須在無障礙服務可選取和聚焦的元件上設定,例如文字或按鈕等螢幕元素。
- 舉例來說,如果只在
Column上設定traversalIndex,則不會有任何效果,除非該資料欄也設定了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 注意事項
使用遍歷 API 時,請注意下列事項:
isTraversalGroup = true應設定在包含分組元素的父項上。traversalIndex應設定在包含語意且會由無障礙服務選取的子項元件上。- 請確認您調查的所有元素都位於相同的
zIndex層級,因為這也會影響語意和遍歷順序。 - 請確保不會不必要地合併語意,因為這可能會影響套用周遊索引的元件。
為您推薦
- 注意:系統會在 JavaScript 關閉時顯示連結文字
- Compose 中的無障礙功能
- [Compose 中的 Material Design 2][19]
- 測試 Compose 版面配置