Agrupa los destinos de enfoque

Puedes agrupar varios destinos de enfoque con el modificador focusGroup. La agrupación 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 realizar intercepciones cuando el enfoque entra o sale de una región.

Conceptos de grupo focal: entrada, salida y recorrido predeterminado

Un grupo focal establece un límite de navegación cohesivo alrededor de sus objetivos de enfoque secundarios. Para comprender cómo se mueve el enfoque a través de este límite, se deben tener en cuenta dos conceptos clave:

  • Enter: Es el evento que se produce cuando el enfoque pasa de cualquier elemento fuera del grupo focal a un elemento dentro del grupo.
  • Salida: Es el evento que ocurre cuando el enfoque sale del último elemento interno del grupo y pasa a un objetivo de enfoque externo.
Diagrama que muestra las transiciones de entrada y salida a través de un límite de 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, ocurre lo siguiente:

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

Personaliza el redireccionamiento de la entrada de enfoque

En muchos diseños de IU, el destino de entrada predeterminado (como el primer elemento secundario en el orden de aparición) 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 principal "Confirmar" a la derecha. Cuando el usuario navega hacia abajo hasta la barra de botones desde un campo de formulario:

  • Comportamiento predeterminado: El enfoque se dirigiría a "Cancelar" porque es el primer botón en el orden de apariencia visual.
  • Comportamiento previsto: Como "Confirmar" es la acción recomendada principal, es posible que quieras que el foco se dirija directamente a "Confirmar" cuando se ingrese al grupo de botones desde un punto anterior en el orden de enfoque, sin dejar de permitir 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 cuando se ingresa un TabRow
  • Mover el enfoque directamente al control "Reproducir / Pausar" cuando se ingresa a una barra de reproducción de contenido multimedia

Implementación con onEnter y FocusRequester

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

  1. Aplica el modificador focusGroup al contenedor superior 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 con 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 en el límite o inspeccionar requestedFocusDirection para personalizar el enrutamiento en función de si el usuario navegó con Tab, Abajo o Izquierda.

Los contenedores desplazables son grupos focales de forma predeterminada

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