フォーカスを復元する

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

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

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

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

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

行と列のフォーカスを復元する

遅延レイアウト コンテナとは異なり、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() を呼び出します。