移動順序を制御する

Compose アプリのユーザー補助スクリーン リーダーの動作はデフォルトで実装されている 通常は左から右、上から下の順になります。 ただし、アルゴリズムが決定できないタイプのアプリ レイアウトもあります。 ヒントなしで実際の読む順番が示されています。ビューベースのアプリでは、次の操作が可能です。 traversalBefore プロパティと traversalAfter プロパティを使用して、このような問題を修正します。 Compose 1.5 以降、Compose は同等の柔軟な API を提供しますが、 説明します。

isTraversalGrouptraversalIndex はセマンティック プロパティです。 を使用すると、 デフォルトの並べ替えアルゴリズムは適切ではありません。isTraversalGroup の識別情報 意味的に重要なグループですが、traversalIndex は順序を調整します。 グループ化しますisTraversalGroup は単独でも使用できますが、 または traversalIndex を使用すると、さらにカスタマイズできます。

isTraversalGrouptraversalIndex を アプリでスクリーン リーダーの移動順序を制御します。

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
        )
    }
}

このコードにより、次のような出力が生成されます。

左側の列に「This」と書かれた 2 列のテキストがあるレイアウト
  文章は左の列にあります」右側の列は右の列に配置されています。
図 1. 2 つの文(左に 1 つ)があるレイアウト 別の列と右側の列に 1 つずつ)。

セマンティクスが設定されていないため、スクリーン リーダーのデフォルトの動作は次のとおりです。 要素を左から右、上から下にトラバースできます。このため、 デフォルトでは、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 }
        )
    }
}

isTraversalGroupCardBox ごとに特別に設定されるため、CardBox 境界は要素を並べ替える際に適用されます。この例では 最初に CardBox が読み込まれ、次に右側の CardBox が読み取られます。

これで、TalkBack が正しい順序で文の断片を読み上げるようになりました。

「この文が含まれています」→ 「左の列」を選択します。→ 「この文は」→ " あります

移動順序をさらにカスタマイズする

traversalIndex は、TalkBack をカスタマイズできる浮動小数点プロパティです。 移動順序を指定します要素をグループ化するだけでは TalkBack が traversalIndex を以下と組み合わせて使用すると、 スクリーン リーダーの順序をさらにカスタマイズするには、isTraversalGroup を使用します。

traversalIndex プロパティには次の特徴があります。

  • traversalIndex の値が小さい要素が優先されます。
  • 正数または負数を指定できます。
  • デフォルト値は 0f です。
  • 画面上の要素など、スクリーン リーダーのフォーカス可能なノードにのみ影響します。 できます。たとえば、1 つの列に traversalIndex のみを設定すると、 列に isTraversalGroup も設定されていない限り、効果はありません。

次の例は、traversalIndex と 一緒に isTraversalGroup しましょう。

例: 時計の文字盤をトラバースする

時計の文字盤は、標準的なトラバーサルの順序付けが行われない一般的なシナリオです。 説明します。このセクションの例は時間選択ツールです。ユーザーはこの時間選択ツールを使用して、 文字盤の数字を通し、時間と分の数字を選択します あります。

上部に時刻選択ツールがある文字盤。
図 2. 文字盤の画像。

次の簡略化されたスニペットでは、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 桁の数字が読み取られます。 最後の要素です。この問題は、 他のすべての要素にはデフォルトの traversalIndex0f であり、 時計のテキスト要素は、他のすべての 0f 要素の後で読み取られます。

例: フローティング アクション ボタンの移動順序をカスタマイズする

この例では、traversalIndexisTraversalGroup が マテリアル デザインのフローティング アクション ボタン(FAB)の移動順序。基礎 次のレイアウトを見てみましょう。

トップ アプリバー、サンプル テキスト、フローティング アクション ボタン、および
  追加できます
図 3. トップ アプリバー、サンプル テキスト、フローティング アクション ボタンがあるレイアウト ボトム アプリバーなどがあります。

このサンプルのレイアウトでは、デフォルトで 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")
        }
    }
}

このスニペットでは、スペースを作成し、 同じボックスで isTraversalGrouptrue に設定し、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 → ボトム アプリバー

参考情報