移動順序は、ユーザー補助サービスが UI 要素を移動する順序です。Compose アプリでは、要素は想定される読み取り順序で配置されます。通常は左から右、上から下です。ただし、正しい読み順を決定するために Compose に追加のヒントが必要な場合があります。
isTraversalGroup
と traversalIndex
は、Compose のデフォルトの並べ替えアルゴリズムが不十分なシナリオで、ユーザー補助サービスの走査順序に影響を与えることができるセマンティック プロパティです。isTraversalGroup
は、カスタマイズが必要な意味的に重要なグループを特定します。一方、traversalIndex
は、これらのグループ内の個々の要素の順序を調整します。isTraversalGroup
のみを使用して、グループ内のすべての要素を一緒に選択する必要があることを示すことができます。traversalIndex
と組み合わせて使用すると、さらにカスタマイズできます。
アプリで isTraversalGroup
と traversalIndex
を使用して、スクリーン リーダーの移動順序を制御します。
要素をグループ化して走査する
isTraversalGroup
は、セマンティクス ノードが走査グループであるかどうかを定義するブール値プロパティです。このタイプのノードは、ノードの子を整理する境界または境界線として機能します。
ノードに isTraversalGroup = true
を設定すると、他の要素に移動する前に、そのノードのすべての子が訪問されます。isTraversalGroup
は、スクリーン リーダーがフォーカスできないノード(列、行、ボックスなど)に設定できます。
次の例では、isTraversalGroup
を使用しています。4 つのテキスト要素を出力します。左の 2 つの要素は 1 つの CardBox
要素に属し、右の 2 つの要素は別の 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
を併用する方法を示しています。
標準の走査順序が機能しない一般的なシナリオとして、文字盤があります。このセクションの例は時刻選択ツールです。ユーザーは文字盤上の数字を移動して、時刻と分刻みの数字を選択できます。

次の簡素化されたスニペットでは、12 個の数字が 12 から始まり、時計回りに円の周囲に描画される CircularLayout
があります。
@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
がないと、画面上の他のすべての要素が訪問された後に、文字盤の 12 桁が最後に読み上げられます。これは、他のすべての要素のデフォルトの traversalIndex
が 0f
であり、時計のテキスト要素が他のすべての 0f
要素の後に読み取られるためです。
API に関する考慮事項
走査 API を使用する場合は、次の点を考慮してください。
isTraversalGroup = true
は、グループ化された要素を含む親に設定する必要があります。traversalIndex
は、セマンティクスを含む子コンポーネントに設定し、ユーザー補助サービスによって選択されるようにする必要があります。- 調査対象の要素がすべて同じ
zIndex
レベルにあることを確認します。これは、セマンティクスと走査順序にも影響します。 - 不要なセマンティクスが統合されていないことを確認してください。これは、どのコンポーネントに走査インデックスが適用されるかに影響する可能性があります。
あなたへのおすすめ
- 注: JavaScript がオフになっている場合はリンクテキストが表示されます
- Compose のユーザー補助
- [Compose のマテリアル デザイン 2][19]
- Compose レイアウトのテスト