フォーカス動作を変更する

要素のデフォルトのフォーカス動作をオーバーライドすることが必要となる場合がある 画面に表示されるようになりましたたとえば、コンポーザブルをグループ化して、 特定のコンポーザブルにフォーカスし、明示的にフォーカスをリクエストする。 フォーカスをキャプチャまたは解放したり、開始時や終了時にフォーカスをリダイレクトしたりできます。この セクションでは、デフォルトと異なる場合にフォーカス動作を変更する方法について説明します。 できます。

フォーカス グループで一貫したナビゲーションを実現する

Jetpack Compose が、正しい次のアイテムをすぐに推測しないことがあります。 タブ形式ナビゲーション(特にタブなどの複雑な親 Composables の場合) リストが役立ちます

フォーカス検索は通常、Composables の宣言順序に従いますが、 これは不可能なことがあります。たとえば、プレーヤーの Composables が 階層は水平方向にスクロール可能なため、完全には表示されません。表示される言語 見てみましょう。

Jetpack Compose は、先頭に最も近い次のアイテムをフォーカスするかどうかを決定することがあります。 テスト画面に進むのではなく、 一方向ナビゲーション:

上部の水平ナビゲーションと、その下にアイテムのリストを表示するアプリのアニメーション。
図 1. 上部の水平ナビゲーションと、その下にアイテムのリストを表示するアプリのアニメーション

この例では、開発者がフォーカスを [Chocolates] タブから下の最初の画像に移動し、 [ペストリー] タブ。それまではタブに焦点を合わせ、 最後のタブを開いて、内部コンテンツに注目します。

上部の水平ナビゲーションと、その下にアイテムのリストを表示するアプリのアニメーション。
図 2. 上部の水平ナビゲーションと、その下にアイテムのリストを表示するアプリのアニメーション

コンポーザブルのグループがフォーカスを得ることが重要な状況では 前の例のタブ行のように、 focusGroup() 修飾子を持つ親の Composable:

LazyVerticalGrid(columns = GridCells.Fixed(4)) {
    item(span = { GridItemSpan(maxLineSpan) }) {
        Row(modifier = Modifier.focusGroup()) {
            FilterChipA()
            FilterChipB()
            FilterChipC()
        }
    }
    items(chocolates) {
        SweetsCard(sweets = it)
    }
}

双方向ナビゲーションは、指定された 方向 - 別のグループの要素が完全に非表示の要素よりも近くにある場合 場合は、最も近いものが選択されます。これを回避するには focusGroup() 修飾子を適用できます。

FocusGroup は、フォーカスの点でグループ全体が 1 つのエンティティのように見えるようにします。 ただし、グループ自体はフォーカスを取得せず、最も近い子がフォーカスを 集中してください。これにより、ナビゲーションは、完全に表示されていない グループから退会する前に

この場合、FilterChip の 3 つのインスタンスがフォーカスされ、 SweetsCard アイテム(SweetsCards が完全に見えている場合でも) 一部の FilterChip は非表示になっている可能性があります。これは、 focusGroup 修飾子が、フォーカス マネージャーにアイテムの表示順序を調整するように指示します 簡単に操作でき、UI と一貫性が保たれます。

focusGroup 修飾子がない場合、FilterChipC が表示されなかった場合にフォーカスする ナビゲーションが最後にピックアップされます。ただし、このような修飾子を追加しても、 検出可能な場合のみが、FilterChipB の直後にフォーカスも取得します。 期待どおりに動作しています。

コンポーザブルをフォーカス可能にする

ボタンやコンポーザブルなど、一部のコンポーザブルは、設計上フォーカス可能です。 clickable 修飾子を付けたものになります。特定のキャンペーンタイプを フォーカス可能な動作をコンポーザブルに変更するには、focusable 修飾子を使用します。

var color by remember { mutableStateOf(Green) }
Box(
    Modifier
        .background(color)
        .onFocusChanged { color = if (it.isFocused) Blue else Green }
        .focusable()
) {
    Text("Focusable 1")
}

コンポーザブルをフォーカス不可にする

一部の要素が関与すべきでない状況が存在する可能性があります。 焦点を当てますそのようなまれな状況では、canFocus property を活用できます。 Composable をフォーカス可能から除外します。

var checked by remember { mutableStateOf(false) }

Switch(
    checked = checked,
    onCheckedChange = { checked = it },
    // Prevent component from being focused
    modifier = Modifier
        .focusProperties { canFocus = false }
)

FocusRequester を使用してキーボード フォーカスをリクエストする

特定のリクエストへのレスポンスとして明示的にフォーカスを です。たとえば、再起動を希望するかどうかをユーザーに フォームに必要事項を入力し [はい]をクリックします最初のフィールドに再度フォーカスを 入力できます。

まず、FocusRequester オブジェクトを キーボードのフォーカスの移動先となるコンポーザブルを指定します。次のコードでは、 FocusRequester オブジェクトは、次のように設定することで TextField に関連付けられます。 Modifier.focusRequester という修飾子を使用します。

val focusRequester = remember { FocusRequester() }
var text by remember { mutableStateOf("") }

TextField(
    value = text,
    onValueChange = { text = it },
    modifier = Modifier.focusRequester(focusRequester)
)

実際のフォーカス リクエストを送信するには、FocusRequester の requestFocus メソッドを呼び出します。このメソッドは、Composable コンテキストの外部で呼び出す必要があります。 (そうしないと、再コンポーズのたびに再実行されます)。次のスニペット ボタンが押されたときにキーボード フォーカスを移動するようにシステムにリクエストする方法を示します。 クリック:

val focusRequester = remember { FocusRequester() }
var text by remember { mutableStateOf("") }

TextField(
    value = text,
    onValueChange = { text = it },
    modifier = Modifier.focusRequester(focusRequester)
)

Button(onClick = { focusRequester.requestFocus() }) {
    Text("Request focus on TextField")
}

キャプチャしてフォーカスを離す

フォーカスを活用して、ユーザーがアプリに適切なデータを提供できるよう導くことができる たとえば、有効なメールアドレスや電話番号の取得など、 あります。エラー状態はユーザーに状況を知らせますが、 入力ミスが起きるまで そのフィールドに誤った情報を入れて あります。

フォーカスをキャプチャするために、captureFocus() メソッドを呼び出します。 次のように、代わりに freeFocus() メソッドを使って解放します。 例:

val textField = FocusRequester()

TextField(
    value = text,
    onValueChange = {
        text = it

        if (it.length > 3) {
            textField.captureFocus()
        } else {
            textField.freeFocus()
        }
    },
    modifier = Modifier.focusRequester(textField)
)

フォーカス修飾子の優先順位

Modifiers は、子が 1 つしかない要素とみなされるため、キューに入れると その左側(または一番上)にある各 Modifier が、その後に続く Modifier をラップします。 右(または下)に表示されます。つまり、2 つ目の Modifier は そのため、2 つの focusProperties を宣言すると、最上位の 以下のものは一番上にあるため、1 つは機能します。

コンセプトをより明確にするため、次のコードをご覧ください。

Modifier
    .focusProperties { right = item1 }
    .focusProperties { right = item2 }
    .focusable()

この場合、item2 を右フォーカスとして示す focusProperties は、 前述のものに含まれているため、使用しないでください。したがって、item1 は もう 1 つは使用します

この方法を利用して、親は次の方法で動作をデフォルトにリセットすることもできます。 FocusRequester.Default を使用:

Modifier
    .focusProperties { right = Default }
    .focusProperties { right = item1 }
    .focusProperties { right = item2 }
    .focusable()

親は同じ修飾子チェーンの一部である必要はありません。保護者 コンポーザブルは、子コンポーザブルのフォーカス プロパティを上書きできます。たとえば ボタンをフォーカス不可にする FancyButton について考えてみましょう。

@Composable
fun FancyButton(modifier: Modifier = Modifier) {
    Row(modifier.focusProperties { canFocus = false }) {
        Text("Click me")
        Button(onClick = { }) { Text("OK") }
    }
}

ユーザーは、canFocustrue に設定することで、このボタンを再度フォーカス可能にできます。

FancyButton(Modifier.focusProperties { canFocus = true })

他の Modifier と同様に、フォーカス関連のものは順序に応じて動作が異なる 宣言します。たとえば、次のようなコードにより、Box フォーカス可能ですが、FocusRequester はこのフォーカス可能に関連付けられていません。 フォーカス可能な後に宣言されます。

Box(
    Modifier
        .focusable()
        .focusRequester(Default)
        .onFocusChanged {}
)

focusRequester は最初の Pod に関連付けられることに フォーカス可能であるため、この focusRequester は 最初のフォーカス可能な子です。使用できるものがない場合は、何も参照しません。 ただし、(focusable() 修飾子のおかげで)Box はフォーカス可能なため、 双方向ナビゲーションを使用できます

別の例として、onFocusChanged() は、次のいずれかでも機能します。 修飾子は、 focusable() 修飾子または focusTarget() 修飾子。

Box(
    Modifier
        .onFocusChanged {}
        .focusRequester(Default)
        .focusable()
)
Box(
    Modifier
        .focusRequester(Default)
        .onFocusChanged {}
        .focusable()
)

開始時または終了時にフォーカスをリダイレクト

場合によっては、次のような、非常に具体的なナビゲーションを提供する必要があります このようになります。

2 つの列に並んで配置されたボタンが表示され、ある列から別の列にフォーカスをアニメーション化する画面のアニメーション。
図 3. ボタンが 2 列横に並んで配置され、一方の列からもう一方の列にフォーカスが移動している様子を示す画面のアニメーション

これの作成方法について説明する前に、デフォルトの フォーカス検索の動作が異なります。フォーカス検索が終われば、変更なしで Clickable 3 の項目に移動し、D-pad(または同等のもの)の DOWN を押す 矢印キーなど)を押すと、Column の下に表示されているものにフォーカスが移動します。 グループから出て 右側のグループを無視しますコンバージョン トラッキングが フォーカス可能な項目がある場合、フォーカスは移動せず、表示されたままになります Clickable 3

この動作を変更して、意図したナビゲーションを実現するには、 focusProperties 修飾子: フォーカスしたときの動作を管理できます Composable に入る、または終了します。

val otherComposable = remember { FocusRequester() }

Modifier.focusProperties {
    exit = { focusDirection ->
        when (focusDirection) {
            Right -> Cancel
            Down -> otherComposable
            else -> Default
        }
    }
}

特定の Composable に入るたびに、フォーカスをその中に送ることができます。 UI に 2 つのルールがある場合などに、 最初の列を処理するたびに フォーカスが 2 番目に切り替わります。

2 つの列に並んで配置されたボタンが表示され、ある列から別の列にフォーカスをアニメーション化する画面のアニメーション。
図 4. ボタンが 2 列横に並んで配置され、一方の列からもう一方の列にフォーカスが移動している様子を示す画面のアニメーション

この GIF では、Column 1 でフォーカスが Clickable 3 Composable に到達すると、 フォーカスされている次のアイテムは別の ColumnClickable 4 です。この動作 これは、focusDirectionenter および exit と組み合わせることで実現できます。 focusProperties 修飾子内の値。2 人とも、引数として渡されるラムダを パラメータとしてフォーカスの方向を指定し、 FocusRequester。このラムダの動作には、次の 3 つがあります。 FocusRequester.Cancel はフォーカスの継続を停止しますが、 FocusRequester.Default の動作は変更されません。代わりに FocusRequester を別の Composable にアタッチすると、その部分にフォーカスが移動します。 固有の Composable

フォーカスの進行方向を変更する

フォーカスを次の項目や正確な方向に進めるには、 onPreviewKey 修飾子を利用して、LocalFocusManager を暗黙的に指定し、 moveFocus 修飾子を使用してフォーカスを進めます。

次の例は、フォーカス メカニズムのデフォルトの動作を示しています。 tab キーの押下が検出されると、フォーカスはフォーカス内の次の要素に進みます 選択します。これは通常構成する必要はありませんが、 システムの内部動作を把握し 確認します。

val focusManager = LocalFocusManager.current
var text by remember { mutableStateOf("") }

TextField(
    value = text,
    onValueChange = { text = it },
    modifier = Modifier.onPreviewKeyEvent {
        when {
            KeyEventType.KeyUp == it.type && Key.Tab == it.key -> {
                focusManager.moveFocus(FocusDirection.Next)
                true
            }

            else -> false
        }
    }
)

このサンプルでは、focusManager.moveFocus() 関数がフォーカスを 指定されたアイテム、または関数パラメータで指定された方向に設定されます。