A veces es necesario anular el comportamiento de foco predeterminado de los elementos en la pantalla. Por ejemplo, es posible que quieras agrupar los elementos componibles, evitar que Enfocarse en un elemento componible determinado de forma explícita foco de solicitud en uno captura o libera el foco, o redirecciona el foco en la entrada o salida. Esta se describe cómo cambiar el comportamiento del enfoque cuando los valores predeterminados no son los que que necesitan tus usuarios.
Brinda una navegación coherente con grupos focales
A veces, Jetpack Compose no adivina de inmediato el siguiente elemento correcto para
navegación por pestañas, en especial cuando el elemento superior Composables
complejo, como las pestañas y
listas de aplicaciones.
Si bien la búsqueda de enfoque suele seguir el orden de declaración de Composables
,
Esto es imposible en algunos casos, como cuando uno de los Composables
en la
es un elemento desplazable horizontal que no es completamente visible. Esto se muestra en
en el siguiente ejemplo.
Jetpack Compose puede decidir enfocar el siguiente elemento más cercano al inicio de la pantalla, como se muestra a continuación, en lugar de continuar la ruta que esperas para navegación unidireccional:
![Animación de una app que muestra una navegación horizontal superior y una lista de elementos debajo.](https://developer.android.com/static/develop/ui/compose/images/touchinput/one_directional_navigation_1.gif?authuser=8&hl=es)
En este ejemplo, queda claro que los desarrolladores no pretendían enfocarse en Pasa de la pestaña Chocolates a la primera imagen a continuación y, luego, regresa a la La pestaña Repostería. En cambio, querían que el foco siguiera en las pestañas hasta que pestaña y, luego, enfócate en el contenido interno:
![Animación de una app que muestra una navegación horizontal superior y una lista de elementos debajo.](https://developer.android.com/static/develop/ui/compose/images/touchinput/one_directional_navigation_2.gif?authuser=8&hl=es)
En situaciones en las que es importante que se enfoque un grupo de elementos componibles
de forma secuencial, como en la fila Pestaña del ejemplo anterior, debes unir
el Composable
en un elemento superior que tiene el modificador focusGroup()
:
LazyVerticalGrid(columns = GridCells.Fixed(4)) { item(span = { GridItemSpan(maxLineSpan) }) { Row(modifier = Modifier.focusGroup()) { FilterChipA() FilterChipB() FilterChipC() } } items(chocolates) { SweetsCard(sweets = it) } }
La navegación bidireccional busca el elemento componible más cercano para el elemento específico
dirección, si un elemento de otro grupo está más cerca que un elemento no completamente visible
elemento en el grupo actual, la navegación elige el más cercano. Para evitar esto,
puedes aplicar el modificador focusGroup()
.
FocusGroup
hace que un grupo completo parezca una sola entidad en términos de enfoque.
pero el grupo en sí no será el foco, sino que el niño más cercano
a conseguir el enfoque. De esta manera, la navegación reconoce que debe dirigirse a la parte
elemento antes de abandonar el grupo.
En este caso, las tres instancias de FilterChip
se enfocarán antes de
SweetsCard
elementos, incluso cuando SweetsCards
son completamente visibles para la
usuario y algunos FilterChip
podrían estar ocultos. Esto sucede porque el
El modificador focusGroup
le indica al administrador de enfoque que ajuste el orden en el que los elementos
estén enfocados para que la navegación sea más fácil y coherente con la IU.
Sin el modificador focusGroup
, si FilterChipC
no estaba visible, el foco
la navegación lo recogería en último lugar. Sin embargo, agregar este tipo de modificador hace que no
Solo es detectable, pero también adquirirá el foco justo después de FilterChipB
, ya que
que esperan los usuarios.
Haz que un elemento componible sea enfocable
Algunos elementos componibles son enfocables por diseño, como un elemento Button o un elemento componible con
el modificador clickable
adjunto. Si quieres agregar específicamente
comportamiento enfocable en un elemento componible, usas el modificador focusable
:
var color by remember { mutableStateOf(Green) } Box( Modifier .background(color) .onFocusChanged { color = if (it.isFocused) Blue else Green } .focusable() ) { Text("Focusable 1") }
Cómo hacer que un elemento componible no pueda enfocarse
Puede haber situaciones en las que algunos de tus elementos no deberían participar
en primer plano. En estas raras ocasiones, puedes aprovechar la canFocus property
para excluir un objeto Composable
de modo que no se pueda enfocar.
var checked by remember { mutableStateOf(false) } Switch( checked = checked, onCheckedChange = { checked = it }, // Prevent component from being focused modifier = Modifier .focusProperties { canFocus = false } )
Solicita el enfoque del teclado con FocusRequester
En algunos casos, es posible que quieras solicitar explícitamente el foco como respuesta a un la interacción del usuario. Por ejemplo, puedes preguntarle a un usuario si desea reiniciar el servicio completar un formulario y si presionan "sí" quieres volver a enfocar el primer campo de esa forma.
Lo primero que debes hacer es asociar un objeto FocusRequester
con el
elemento componible al que quieres mover el enfoque del teclado. En el siguiente código,
se asocia un objeto FocusRequester
con una TextField
estableciendo un
modificador llamado Modifier.focusRequester
:
val focusRequester = remember { FocusRequester() } var text by remember { mutableStateOf("") } TextField( value = text, onValueChange = { text = it }, modifier = Modifier.focusRequester(focusRequester) )
Puedes llamar al método requestFocus
de FocusRequester para enviar solicitudes de enfoque reales. Debes invocar este método fuera de un contexto Composable
.
(de lo contrario, se vuelve a ejecutar en cada recomposición). El siguiente fragmento
muestra cómo solicitar al sistema que mueva el enfoque del teclado cuando el botón se
se hizo clic:
val focusRequester = remember { FocusRequester() } var text by remember { mutableStateOf("") } TextField( value = text, onValueChange = { text = it }, modifier = Modifier.focusRequester(focusRequester) ) Button(onClick = { focusRequester.requestFocus() }) { Text("Request focus on TextField") }
Captura y libera el enfoque
Puedes aprovechar el enfoque para guiar a los usuarios a fin de que proporcionen los datos correctos para tu aplicación. necesita realizar la tarea; por ejemplo, obtener una dirección de correo electrónico o un número de teléfono válidos de la fila. Si bien los estados de error informan a los usuarios sobre lo que está sucediendo, podría necesitar el campo con información errónea para mantenerse enfocado hasta que y solucionar el problema.
Para capturar el enfoque, puedes invocar el método captureFocus()
.
libérala después con el método freeFocus()
, como en el siguiente ejemplo:
ejemplo:
val textField = FocusRequester() TextField( value = text, onValueChange = { text = it if (it.length > 3) { textField.captureFocus() } else { textField.freeFocus() } }, modifier = Modifier.focusRequester(textField) )
Prioridad de los modificadores de enfoque
Modifiers
se puede ver como elementos que solo tienen un elemento secundario, por lo que, cuando pones en cola
cada Modifier
de la izquierda (o superior) une la Modifier
que sigue en
la derecha (o abajo). Esto significa que el segundo Modifier
está contenido dentro
la primera, de modo que, al declarar dos focusProperties
, solo el elemento de más
uno funciona, ya que los siguientes se encuentran en la parte superior.
Para aclarar más el concepto, consulta el siguiente código:
Modifier .focusProperties { right = item1 } .focusProperties { right = item2 } .focusable()
En este caso, la focusProperties
que indica item2
como el enfoque correcto
No debe usarse, tal como se indica en el documento anterior. por lo tanto, item1
será el
usar uno.
Con este enfoque, la madre o el padre también puede restablecer el comportamiento a la configuración predeterminada
usando FocusRequester.Default
:
Modifier .focusProperties { right = Default } .focusProperties { right = item1 } .focusProperties { right = item2 } .focusable()
No es necesario que el superior sea parte de la misma cadena de modificadores. Madre o padre
puede reemplazar una propiedad de enfoque de un elemento componible secundario. Por ejemplo:
considera este FancyButton
que hace que el botón no sea enfocable:
@Composable fun FancyButton(modifier: Modifier = Modifier) { Row(modifier.focusProperties { canFocus = false }) { Text("Click me") Button(onClick = { }) { Text("OK") } } }
Para volver a seleccionar este botón, el usuario puede establecer canFocus
en true
:
FancyButton(Modifier.focusProperties { canFocus = true })
Como todos los Modifier
, los relacionados con el enfoque se comportan de manera diferente según el orden.
cuando las declaras. Por ejemplo, un código como el siguiente hace que Box
pero la FocusRequester
no está asociada con este enfocable, ya que
se declara después del elemento enfocable.
Box( Modifier .focusable() .focusRequester(Default) .onFocusChanged {} )
Es importante recordar que un focusRequester
se asocia con el primer
enfocable debajo de él en la jerarquía, por lo que este focusRequester
apunta al
primer hijo enfocable. En caso de que no haya ninguno disponible, no apuntará a nada.
Sin embargo, dado que Box
es enfocable (gracias al modificador focusable()
),
puedes navegar hasta allí mediante la navegación bidireccional.
Como otro ejemplo, funcionaría cualquiera de las siguientes opciones, ya que onFocusChanged()
modificador hace referencia al primer elemento enfocable que aparece después de
Modificadores focusable()
o focusTarget()
Box( Modifier .onFocusChanged {} .focusRequester(Default) .focusable() ) |
Box( Modifier .focusRequester(Default) .onFocusChanged {} .focusable() ) |
Enfoque de redireccionamiento en la entrada o la salida
A veces, debes proporcionar un tipo de navegación muy específico, como la que como se muestra en la animación a continuación:
![Animación de una pantalla que muestra dos columnas de botones colocadas una al lado de la otra y animando el enfoque de una columna a la otra.](https://developer.android.com/static/develop/ui/compose/images/touchinput/two_dimensional_navigation_1.gif?authuser=8&hl=es)
Antes de profundizar en cómo crear esto, es importante comprender la configuración
el comportamiento de la búsqueda de enfoque. Sin ninguna modificación, una vez que la búsqueda de enfoque
llega al elemento Clickable 3
y presiona DOWN
en el pad direccional (o su equivalente
la tecla de flecha) moverá el enfoque a lo que se muestre debajo de Column
,
saliendo del grupo e ignorando el de la derecha. Si no hay
elementos enfocables disponibles, el enfoque no se mueve a ningún lugar, sino que permanece
Clickable 3
Para modificar este comportamiento y proporcionar la navegación prevista, puedes aprovechar las
Modificador focusProperties
, que te ayuda a administrar lo que sucede cuando el foco
la búsqueda ingresa o sale de la Composable
:
val otherComposable = remember { FocusRequester() } Modifier.focusProperties { exit = { focusDirection -> when (focusDirection) { Right -> Cancel Down -> otherComposable else -> Default } } }
Es posible dirigir el enfoque a una Composable
específica cada vez que entra
o sale de una cierta parte de la jerarquía, por ejemplo, cuando tu IU tiene dos
y quiere asegurarse de que cuando se procese la primera
el enfoque cambia al segundo:
![Animación de una pantalla que muestra dos columnas de botones colocadas una al lado de la otra y animando el enfoque de una columna a la otra.](https://developer.android.com/static/develop/ui/compose/images/touchinput/two_dimensional_navigation_2.gif?authuser=8&hl=es)
En este GIF, una vez que el enfoque llegue a Clickable 3 Composable
en Column
1,
el siguiente elemento que se enfoca es Clickable 4
en otro Column
. Este comportamiento
Se puede lograr combinando focusDirection
con enter
y exit
.
dentro del modificador focusProperties
. Ambos necesitan una lambda que tome
como parámetro, la dirección de la que proviene el foco y que devuelve un
FocusRequester
Esta lambda puede comportarse de tres maneras diferentes: mostrar
FocusRequester.Cancel
evita que el enfoque continúe, mientras
FocusRequester.Default
no altera su comportamiento. En cambio, se proporciona
Un FocusRequester
adjunto a otro Composable
hace que el foco salte a ese elemento.
Composable
específica
Cambiar el enfoque de la dirección de avance
Para avanzar el enfoque al siguiente elemento o hacia una dirección precisa, puedes
Aprovecha el modificador onPreviewKey
e implica el LocalFocusManager
para
hacer avanzar el enfoque con el modificador moveFocus
.
En el siguiente ejemplo, se muestra el comportamiento predeterminado del mecanismo de enfoque: cuando un elemento
Se detecta la pulsación de teclas tab
, el foco avanza al siguiente elemento
lista. Aunque no es algo que normalmente tendrás que configurar, es importante
el funcionamiento interno del sistema para poder cambiar la configuración
de tu modelo.
val focusManager = LocalFocusManager.current var text by remember { mutableStateOf("") } TextField( value = text, onValueChange = { text = it }, modifier = Modifier.onPreviewKeyEvent { when { KeyEventType.KeyUp == it.type && Key.Tab == it.key -> { focusManager.moveFocus(FocusDirection.Next) true } else -> false } } )
En este ejemplo, la función focusManager.moveFocus()
hace avanzar el enfoque a
el elemento especificado o en la dirección implícita en el parámetro de la función.
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Reacciona ante la concentración
- Enfoque en Compose
- Cómo cambiar el orden de recorrido del enfoque