Restaurer le focus

Lorsque les utilisateurs parcourent des interfaces utilisateur complexes à l'aide d'un clavier physique ou d'un pavé directionnel, ils passent fréquemment d'une section de l'écran à une autre. Par exemple, ils peuvent passer d'un rail de navigation latéral à une liste de contenus, interagir avec un élément, puis revenir en arrière. La restauration du focus permet aux conteneurs de mémoriser l'élément enfant précédemment sélectionné et de lui redonner le focus lorsque le focus revient dans le conteneur.

Restaurer la sélection dans les conteneurs à faire défiler

Dans Jetpack Compose, les conteneurs à défilement tels que LazyColumn, LazyRow et LazyVerticalGrid agissent automatiquement comme des groupes d'étude. Pour activer la restauration de la sélection sur une liste ou une grille différée, associez le modificateur focusRestorer :

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

Lorsque le focus quitte le LazyColumn, le conteneur enregistre l'élément qui était actif. Lorsque l'utilisateur revient à la liste, le focus est restauré sur cet élément au lieu de revenir au début.

Restaurer le curseur dans Row et Column

Contrairement aux conteneurs de mise en page différée, les conteneurs de mise en page non déroulants standards tels que Row et Column n'établissent pas de limite de sélection par défaut. Comme ils n'interceptent pas les événements d'entrée de sélection par eux-mêmes, l'application du modificateur focusRestorer seul ne suffit pas.

Pour activer la restauration du focus sur Row ou Column, combinez focusGroup avec 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")
    }
}

Spécifier une cible de sélection de remplacement

Lorsque le focus entre dans un conteneur pour la première fois, il n'existe aucun enfant précédemment sélectionné. Compose suit l'ordre de sélection par défaut pour déterminer l'enfant à sélectionner.

Vous pouvez personnaliser l'enfant qui reçoit le focus lors de la première entrée en transmettant un FocusRequester de remplacement à focusRestorer (ou un lambda renvoyant un FocusRequester sur les versions antérieures de Compose) :

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

Si aucun enfant n'a été sélectionné précédemment (ou si la restauration de la sélection échoue), Compose déplace la sélection vers la cible de remplacement associée.

Dans les composants de navigation tels que NavigationRail, NavigationBar, NavigationDrawer ou TabRow, les utilisateurs s'attendent à ce que la sélection se porte sur la destination actuellement sélectionnée lorsqu'ils accèdent au composant depuis la zone de contenu.

Vous pouvez diriger l'attention vers la destination active en combinant Modifier.focusGroup() avec focusProperties et en définissant la propriété onEnter (ou enter sur les versions antérieures de Compose) :

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

Restaurer le curseur lors des transitions d'écran

Lorsqu'un utilisateur clique sur un élément pour accéder à un écran d'informations, puis revient en arrière à l'aide du bouton ou du geste Retour, vous pouvez restaurer la sélection sur l'élément sur lequel il a cliqué.

Pour conserver et restaurer le focus lors des transitions d'écran :

  1. Associez le conteneur à un FocusRequester.
  2. Avant de déclencher l'événement de navigation, appelez saveFocusedChild() sur l'élément FocusRequester du conteneur.
  3. Lorsque l'utilisateur revient à l'écran, demandez la sélection initiale sur le conteneur ou appelez restoreFocusedChild().