Agrupa los destinos de enfoque

Puedes agrupar varios destinos de enfoque con el modificador focusGroup. El agrupamiento te permite administrar jerarquías de enfoque, aislar regiones lógicas de la IU (como barras de herramientas, acciones de diálogo o listas de tarjetas) y detectar cuando el enfoque entra o sale de una región.

Conceptos de grupo focal: entrada, salida y recorrido predeterminado

Un grupo de enfoque establece un límite de navegación coherente alrededor de sus objetivos de enfoque secundarios. Para comprender cómo se mueve el enfoque a través de este límite, es necesario conocer dos conceptos clave:

  • Enter: Es el evento que ocurre cuando el enfoque pasa de cualquier elemento fuera del grupo de enfoque a un elemento dentro del grupo.
  • Exit: Es el evento que ocurre cuando el enfoque sale del último elemento interno del grupo y pasa a un destino de enfoque externo.
Diagrama que muestra las transiciones de entrada y salida a través del límite de un grupo focal.
Figura 1: Grupo focal con eventos de entrada y salida.

Comportamiento predeterminado del recorrido del enfoque

De forma predeterminada, cuando un grupo focal recibe el enfoque desde el exterior, sucede lo siguiente:

  1. Entrada al grupo: Compose selecciona automáticamente un objetivo de enfoque secundario interno. Para la navegación unidimensional (Tab), selecciona el primer elemento secundario en orden de apariencia visual. Para la navegación bidimensional (teclas de flecha o pad direccional), se selecciona el elemento secundario que está más cerca geométricamente de la dirección entrante.
  2. Navegación dentro del grupo: Las presiones repetidas de la tecla de navegación mueven el enfoque entre los destinos de enfoque secundarios incluidos en el grupo.
  3. Salir del grupo: Una vez que el usuario navega más allá del último objetivo de enfoque dentro del grupo, la siguiente presión de tecla sale del grupo y mueve el enfoque al objetivo de enfoque vecino fuera del grupo.

Personaliza el redireccionamiento de la entrada de enfoque

En muchos diseños de IU, el objetivo de entrada predeterminado (como el primer elemento secundario en orden de apariencia) no es la opción más lógica o conveniente para el usuario.

Intención y casos de uso

Considera un diálogo de confirmación con un botón "Cancelar" a la izquierda y un botón "Confirmar" principal a la derecha. Cuando el usuario navega hacia abajo en la barra de botones desde un campo de formulario, sucede lo siguiente:

  • Comportamiento predeterminado: El enfoque se detendría en "Cancelar" porque es el primer botón en el orden de apariencia visual.
  • Comportamiento previsto: Debido a que "Confirmar" es la acción recomendada principal, es posible que desees que el foco se centre directamente en "Confirmar" cuando se ingresa al grupo de botones desde un punto anterior en el orden de foco, al mismo tiempo que permites que el usuario presione Izquierda para seleccionar "Cancelar" si es necesario.
Entrada de enfoque predeterminada en un grupo de botones.
Figura 2: Botones Cancelar y Confirmar con el objetivo de entrada de enfoque predeterminado.

Otros casos de uso comunes incluyen los siguientes:

  • Mover el enfoque directamente a la pestaña seleccionada actualmente cuando se ingresa un TabRow
  • Mover el enfoque directamente al control "Reproducir / Pausar" cuando se ingresa a una barra de reproducción de medios

Implementación con onEnter y FocusRequester

Para redireccionar el enfoque cuando se ingresa a un grupo focal, haz lo siguiente:

  1. Aplica el modificador focusGroup al contenedor principal que encierra los objetivos de enfoque relacionados.
  2. Aplica el modificador focusProperties al contenedor y configura la devolución de llamada onEnter.
  3. En la devolución de llamada onEnter (con alcance para FocusEnterExitScope), llama a requestFocus() en un FocusRequester asociado con el elemento secundario predeterminado preferido usando focusRequester.

val defaultChildRequester = remember { FocusRequester() }

Row(
    modifier = Modifier
        .focusGroup()
        .focusProperties {
            // Intercept entry into this group and redirect focus to the primary action
            onEnter = {
                defaultChildRequester.requestFocus()
            }
        }
) {
    Button(onClick = { /* Cancel action */ }) {
        Text("Cancel")
    }
    Button(
        onClick = { /* Confirm action */ },
        modifier = Modifier.focusRequester(defaultChildRequester)
    ) {
        Text("Confirm (Default)")
    }
}

Dentro de onEnter y onExit, también puedes llamar a cancelFocusChange() para bloquear por completo las transiciones de enfoque a través del límite o inspeccionar requestedFocusDirection para personalizar el enrutamiento según si el usuario navegó con Tab, Down o Left.

Los contenedores desplazables son grupos de enfoque de forma predeterminada

En Jetpack Compose, los contenedores desplazables, como LazyRow, LazyColumn y LazyVerticalGrid, actúan automáticamente como grupos de enfoque. No es necesario que apliques de forma explícita el modificador focusGroup para establecer las devoluciones de llamada onEnter o onExit.