Restablecer el enfoque

Cuando los usuarios navegan por interfaces de usuario complejas con un teclado de hardware o un pad direccional, suelen moverse entre diferentes secciones de la pantalla, como saltar de un riel de navegación lateral a una lista de contenido, interactuar con un elemento y, luego, volver. El restablecimiento del enfoque permite que los contenedores recuerden el elemento secundario enfocado anteriormente y restablezcan el enfoque en él cuando el enfoque vuelva a ingresar al contenedor.

Restablece el enfoque en los contenedores desplazables

En Jetpack Compose, los contenedores desplazables, como LazyColumn, LazyRow y LazyVerticalGrid, actúan automáticamente como grupos focales. Para habilitar el restablecimiento del enfoque en una lista o cuadrícula diferida, adjunta el modificador focusRestorer:

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

Cuando el enfoque sale del LazyColumn, el contenedor registra qué elemento estaba activo. Cuando el usuario vuelve a navegar por la lista, el enfoque se restablece en ese elemento en lugar de reiniciarse en la parte superior.

Restablece el enfoque en Row y Column

A diferencia de los contenedores de diseño diferido, los contenedores de diseño estándar que no se pueden desplazar, como Row y Column, no establecen un límite de enfoque de forma predeterminada. Como no interceptan los eventos de entrada de enfoque por sí mismos, no basta con aplicar solo el modificador focusRestorer.

Para habilitar el restablecimiento del enfoque en Row o Column, combina focusGroup con 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")
    }
}

Especifica un objetivo de enfoque de resguardo

Cuando el enfoque ingresa a un contenedor por primera vez, no existe un elemento secundario enfocado anteriormente. Compose sigue el orden de enfoque predeterminado para determinar en qué elemento secundario enfocarse.

Puedes personalizar qué elemento secundario recibe el enfoque en la primera entrada pasando un FocusRequester de resguardo a focusRestorer (o una lambda que devuelva un FocusRequester en versiones anteriores 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 no se enfocó ningún elemento secundario anteriormente (o si falla el restablecimiento del enfoque), Compose mueve el enfoque al destino de resguardo asociado.

En los componentes de navegación, como NavigationRail, NavigationBar, NavigationDrawer o TabRow, los usuarios esperan que el enfoque se dirija al destino seleccionado actualmente cuando ingresan al componente desde el área de contenido.

Puedes dirigir el enfoque al destino activo combinando Modifier.focusGroup() con focusProperties y configurando la propiedad onEnter (o enter en versiones anteriores 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)
    )
}

Restablece el enfoque en las transiciones de pantalla

Cuando un usuario hace clic en un elemento para navegar a una pantalla de detalles y, luego, regresa con el botón o el gesto Atrás, puedes restablecer el enfoque en el elemento en el que hizo clic.

Para conservar y restablecer el enfoque en las transiciones de pantalla, haz lo siguiente:

  1. Asocia el contenedor con un FocusRequester.
  2. Antes de activar el evento de navegación, llama a saveFocusedChild() en el FocusRequester del contenedor.
  3. Cuando el usuario vuelva a la pantalla, solicita el enfoque inicial en el contenedor o llama a restoreFocusedChild().