フォーカスを復元する

ユーザーがハードウェア キーボードまたは D-pad を使用して複雑なユーザー インターフェースを操作する場合、画面の異なるセクション間を頻繁に移動します。たとえば、サイド ナビゲーション レールからコンテンツ リストにジャンプし、アイテムを操作してから、元に戻ります。フォーカスの復元を使用すると、コンテナは以前にフォーカスされた子要素を記憶し、フォーカスがコンテナに戻ったときにその要素にフォーカスを復元できます。

スクロール可能なコンテナでフォーカスを復元する

Jetpack Compose では、LazyColumn、LazyRow、LazyVerticalGrid などのスクロール可能なコンテナが自動的にフォーカス グループとして機能します。遅延リストまたはグリッドでフォーカスの復元を有効にするには、focusRestorer 修飾子をアタッチします。

LazyColumn(
    modifier = Modifier.focusRestorer()
) {
    items(items) { item ->
        Button(onClick = { /* Handle click */ }) {
            Text(item)
        }
    }
}

フォーカスが LazyColumn から外れると、コンテナはアクティブだったアイテムを記録します。ユーザーがリストに戻ると、フォーカスは先頭に戻るのではなく、そのアイテムに復元されます。

Row と Column でフォーカスを復元する

遅延レイアウト コンテナとは異なり、Row や Column などの標準のスクロール不可レイアウト コンテナは、デフォルトではフォーカス境界を確立しません。これらはフォーカス エントリ イベントを独自にインターセプトしないため、focusRestorer 修飾子を適用するだけでは不十分です。

Row または Column でフォーカスの復元を有効にするには、focusGroup と focusRestorer を組み合わせます。

Row(
    modifier = Modifier
        .focusGroup()
        .focusRestorer()
) {
    Button(onClick = { /* Action 1 */ }) {
        Text("First item")
    }
    Button(onClick = { /* Action 2 */ }) {
        Text("Second item")
    }
    Button(onClick = { /* Action 3 */ }) {
        Text("Third item")
    }
}

フォールバック フォーカス ターゲットを指定する

フォーカスが初めてコンテナに入ったとき、以前にフォーカスされた子は存在しません。Compose は、デフォルトのフォーカス順序に従って、フォーカスする子を決定します。

最初のエントリでフォーカスを受け取る子をカスタマイズするには、フォールバック FocusRequester を focusRestorer に渡します(以前の Compose バージョンでは、FocusRequester を返すラムダを渡します)。

val firstItemRequester = remember { FocusRequester() }

LazyColumn(
    modifier = Modifier.focusRestorer(firstItemRequester)
) {
    itemsIndexed(items) { index, item ->
        val itemModifier = if (index == 0) {
            Modifier.focusRequester(firstItemRequester)
        } else {
            Modifier
        }
        Button(
            onClick = { /* Handle click */ },
            modifier = itemModifier
        ) {
            Text(item)
        }
    }
}

以前にフォーカスされた子がない場合(またはフォーカスの復元に失敗した場合)、Compose は関連付けられたフォールバック ターゲットにフォーカスを移動します。

NavigationRail、NavigationBar、NavigationDrawer、TabRow などのナビゲーション コンポーネントでは、ユーザーはコンテンツ領域からコンポーネントに入るときに、現在選択されているデスティネーションにフォーカスが移動することを期待します。

Modifier.focusGroup() と focusProperties を組み合わせて onEnter プロパティ(以前の Compose バージョンでは enter)を設定することで、アクティブなデスティネーションにフォーカスを合わせることができます。

val (homeRequester, searchRequester, settingsRequester) = remember { FocusRequester.createRefs() }

val selectedRequester = when (currentDestination) {
    "home" -> homeRequester
    "search" -> searchRequester
    "settings" -> settingsRequester
    else -> homeRequester
}

NavigationRail(
    modifier = modifier
        .focusProperties {
            // Redirect focus to the selected item when focus enters the navigation rail
            onEnter = { selectedRequester.requestFocus() }
        }
        .focusGroup()
) {
    NavigationRailItem(
        selected = currentDestination == "home",
        onClick = { onNavigate("home") },
        icon = { Icon(Icons.Default.Home, contentDescription = "Home") },
        label = { Text("Home") },
        modifier = Modifier.focusRequester(homeRequester)
    )
    NavigationRailItem(
        selected = currentDestination == "search",
        onClick = { onNavigate("search") },
        icon = { Icon(Icons.Default.Search, contentDescription = "Search") },
        label = { Text("Search") },
        modifier = Modifier.focusRequester(searchRequester)
    )
    NavigationRailItem(
        selected = currentDestination == "settings",
        onClick = { onNavigate("settings") },
        icon = { Icon(Icons.Default.Settings, contentDescription = "Settings") },
        label = { Text("Settings") },
        modifier = Modifier.focusRequester(settingsRequester)
    )
}

画面遷移間でフォーカスを復元する

ユーザーがアイテムをクリックして詳細画面に移動し、その後 [戻る] ボタンまたはジェスチャーを使用して戻ったときに、クリックしたアイテムにフォーカスを戻すことができます。

画面遷移間でフォーカスを保持して復元するには:

  1. コンテナを FocusRequester に関連付けます。
  2. ナビゲーション イベントをトリガーする前に、コンテナの FocusRequester で saveFocusedChild() を呼び出します。
  3. ユーザーが画面に戻ったときに、コンテナの初期フォーカスをリクエストするか、restoreFocusedChild() を呼び出します。