Compose アプリのユーザー補助スクリーン リーダーの動作はデフォルトで実装されている
通常は左から右、上から下の順になります。
ただし、アルゴリズムが決定できないタイプのアプリ レイアウトもあります。
ヒントなしで実際の読む順番が示されています。ビューベースのアプリでは、次の操作が可能です。
traversalBefore
プロパティと traversalAfter
プロパティを使用して、このような問題を修正します。
Compose 1.5 以降、Compose は同等の柔軟な API を提供しますが、
説明します。
isTraversalGroup
と traversalIndex
はセマンティック プロパティです。
を使用すると、
デフォルトの並べ替えアルゴリズムは適切ではありません。isTraversalGroup
の識別情報
意味的に重要なグループですが、traversalIndex
は順序を調整します。
グループ化しますisTraversalGroup
は単独でも使用できますが、
または traversalIndex
を使用すると、さらにカスタマイズできます。
isTraversalGroup
と traversalIndex
を
アプリでスクリーン リーダーの移動順序を制御します。
isTraversalGroup
で要素をグループ化する
isTraversalGroup
はブール値のプロパティであり、セマンティクス
トラバーサルグループですこのタイプのノードは、リソースを提供するという
ノードの子を編成する際の境界または境界として使用します。
ノードに isTraversalGroup = true
を設定すると、そのノードのすべての子が
移動してから他の要素に移動できますisTraversalGroup
を ON に設定できます
スクリーン リーダー以外のフォーカス可能なノード(列、行、ボックスなど)。
次の例では、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
です。 - 画面上の要素など、スクリーン リーダーのフォーカス可能なノードにのみ影響します。
できます。たとえば、1 つの列に
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
でバインドすると、文字盤の 12 桁の数字が読み取られます。
最後の要素です。この問題は、
他のすべての要素にはデフォルトの traversalIndex
が 0f
であり、
時計のテキスト要素は、他のすべての 0f
要素の後で読み取られます。
例: フローティング アクション ボタンの移動順序をカスタマイズする
この例では、traversalIndex
と isTraversalGroup
が
マテリアル デザインのフローティング アクション ボタン(FAB)の移動順序。基礎
次のレイアウトを見てみましょう。
このサンプルのレイアウトでは、デフォルトで TalkBack は次のようになります。
トップ アプリバー → サンプル テキスト 0 ~ 6 → フローティング アクション ボタン(FAB)→ 下部 アプリバー
スクリーン リーダーでは、まず FAB にフォーカスを合わせることをおすすめします。目標の値を設定するには、
FAB などのマテリアル要素で 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
よりも小さい)は、フローティング ボックスが
画面上の他のすべての要素よりも前に配置されます。
次に、フローティング ボックスやその他の要素をスキャフォールドに配置します。 マテリアル デザイン レイアウトを実装します。
@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 のユーザー補助機能のテスト原則とツール