Generalidades

Concéntrese en Jetpack Compose.Una breve introducción a las API de Focus en … | Jamie Sanson | Junio ​​de 2021

Jamie Sanson

Para empezar a utilizar realmente Compose se requiere un cambio de mentalidad. Cuando empiezas a pensar de la manera correcta, usar la IU declarativa puede ser muy rápido y beneficioso. Se necesita tiempo para entrenarse para deshacerse de los viejos hábitos y aprender a hacer las cosas nuevamente. La gestión del enfoque en Compose es un poco diferente a la que estamos acostumbrados en el viejo Android, ¡así que echemos un vistazo!

1*e7wMpsqnOkqHGS1iDsfd3g
Foto tomada por Stefan Cosma en Unsplash

He estado trabajando por un tiempo Como elemento secundario en la aplicación de la lista de tareas pendientes. Esta aplicación puede hacer muchas cosas peculiares y su interfaz de usuario está completamente integrada en Compose.Ignore todas las cosas elegantes por ahora, vamos Enfocar Acerca de la experiencia principal de usar esta aplicación. Este es un fragmento de código que describe una lista básica de tareas pendientes.

LazyColumn 
items(todos) todo ->
TodoRow(
text = todo.text,
isDone = todo.isDone,
callbacks = TodoCallbacks(/* For things a row can do */)
)

Me propuse mejorar la navegabilidad de esta lista esta mañana. Puede agregar y eliminar tareas pendientes de forma rápida y sencilla utilizando solo el teclado. Este gif muestra el tipo de comportamiento que busco, tomado de Google Keep.

1*YPLa0Pi0ojMJieGNlCIqBQ

En resumen, si estoy escribiendo algo que debe hacerse y presiono Intro, quiero que mi cursor se mueva a una nueva línea con un nuevo elemento de tarea pendiente. Si retrocedo completamente un elemento, quiero que se elimine y mi cursor se moverá a la línea superior. Obviamente, necesitamos administrar el enfoque, así que veamos nuestras opciones.

Compose UI incluye un FocusManager Type, que le permite presionar el foco en una llamada, es muy adecuado para un recorrido de contenido simple.

Por ejemplo, puede tener una columna simple TextFieldEl formulario que desea explorar cuando el usuario presiona el botón «Siguiente» en el teclado. FocusManager Atravesará su jerarquía de enfoque por usted y encontrará lo siguiente en lo que enfocarse en la dirección que necesita.Este es un código, con FocusDirection.Down Hace que el foco se mueva al siguiente campo de la lista.

Column 
val focusManager = LocalFocusManager.current

for (i in 1..4)
TextField(
// ...
keyboardActions = KeyboardActions(
onNext =
focusManager.moveFocus(FocusDirection.Down)

)
)

Y si quieres Down ¿Representa algo diferente al siguiente elemento de la lista? ¿O quieres que tu combinación personalizada sea enfocable?Bueno, tienes suerte porque el modificador de enfoque admite FocusManagerY se puede utilizar de cualquier forma.

Puede interactuar con el mecanismo de enfoque en Redactar a través de varios modificadores diferentes

  • Modifier.focusTarget() – Esto le permite hacer que el componente sea enfocable
  • Modifier.focusOrder() – Combinar FocusRequesters, que le permite cambiar el orden de enfoque
  • Modifier.focusRequester() – Agregar personalización FocusRequester, Lo que le permite observar el estado de enfoque y solicitar el enfoque para un solo componente
  • Modifier.onFocusEvent(), Modifier.onFocusChanged()– Es más fácil observar cambios internos o reales en el estado de enfoque.

Para entender cómo funcionan, veamos algunos ejemplos.

Observa el estado de enfoque onFocusChanged

onFocusChanged Le permite reaccionar a los cambios de enfoque que afectan la composición o sus elementos secundarios:

TextField(
value = "My text field",
onValueChange = ,
modifier = Modifier.onFocusChanged focusState ->
when
focusState.isFocused ->
println("I'm focused!")
focusState.hasFocus ->
println("A child of mine has focus!")


)

Puedes pasar onFocusEvent Modificador, emitirá un nuevo FocusState Siempre que se escriba el estado de enfoque interno.

Logra el enfoque para diseños personalizados

Al implementar la capacidad de composición personalizada, puede centrarse en la interactividad. Bajo estas circunstancias, focusTarget() ¡Es tu amigo!

@Composable
fun CoolFocusableGraph(modifier: Modifier = Modifier)
// Make ensure our laid out component is focusable, and
// observe focus events to make it interactive
val customComponentModifier = modifier
.focusTarget() // Now focusable!
.onFocusEvent TODO("React to events")
.drawBehind TODO("Draw something cool")

Layout(
content = ,
modifier = customComponentModifier,
measurePolicy = TODO()
)

Cambiar el orden de enfoque

En algunos casos, es posible que desee que el orden de enfoque sea diferente del orden predeterminado. En la mayoría de los casos, esto no es deseable, pero si lo necesita, siga los pasos a continuación. Un mal ejemplo podría ser omitir campos al completar datos válidos.

// First, get a reference to two focus requesters
val (first, second) = FocusRequester.createRefs()

Column
// Down should take us to the third component
TextField(
...
modifier = Modifier.focusOrder(first) down = second
)

// Skip this one when moving in the "down" direction
TextField(...)

// Set the requester to tie them together
TextField(
...
modifier = Modifier.focusOrder(second)
)

Solicitar programáticamente el enfoque de un componente específico

¡Aún más indeseable es que puede elegir administrar el enfoque usted mismo! Si no tiene cuidado, esto puede ser una mala idea, porque es fácil pasar por alto las sutilezas de cómo funciona el enfoque transversal. Si es lo suficientemente cuidadoso, eventualmente volverá a implementar la lógica transversal del enfoque sin usar los componentes internos del enfoque, no es divertido.En términos generales, vale la pena intentar llevarlo a cuestas FocusManager Mientras más, mejor.

Si esto es definitivamente algo que necesita hacer, puede usar FocusRequester versus focusRequester() Los modificadores solicitan mediante programación el enfoque de un componente específico.

// Focus could be part of your state
data class InputField(val text: String, val isFocused: Boolean)

@Composable
fun InputRow(item: InputField)
val requester = FocusRequester()

TextField(
...
modifier = Modifier.focusRequester(requester)
)

// Request focus as a SideEffect (after the composition)
SideEffect
if (item.isFocused)
requester.requestFocus()


Ahora que hemos visto todos los puntos importantes, intentemos aplicarlo a nuestra pregunta de la lista de tareas pendientes.

La solución que quiero es FocusManager, Estoy tratando de mover el enfoque hacia arriba o hacia abajo al agregar o eliminar elementos. Esto es muy efectivo para eliminar elementos, porque el siguiente objetivo de enfoque ya existe. Cuando la atención se centra en los elementos recién agregados, las cosas comienzan a complicarse un poco. Intenté mover el foco a un componente que no existe actualmente, dejándome en la línea donde comencé.

La solución en la que pensé primero fue intentar ejecutar focusManager.moveFocus llamada SideEffect -Un fragmento de código que se ejecuta después de cada combinación. Esto tiene sentido porque publico efectivamente el estado en algo externo, pero hace las cosas más complicadas porque solo quiero llamarlo una vez por cada elemento agregado a la lista. Lo que realmente quiero es cambiar el enfoque después de una combinación exitosa de cambios en la lista de elementos.Finalmente encontré una combinación LaunchedEffect, con focusManager.moveFocus Se llama para mover el foco solo cuando cambia la lista de elementos. El código completo se muestra a continuación.

@Composable
fun ListScreen(lists: List<TodoList>)
// Get a reference to the current FocusManager
val focusManager = LocalFocusManager.current
var focusDirectionToMove by remember mutableStateOf<FocusDirection?>(null)

// Redux dispatch - stay tuned for a blog about this
val dispatch = LocalDispatch.current

// When add or remove events are dispatched, move the focus
val wrappedDispatch: (Any) -> Any = action ->
when (action)
is Action.AddTodo,
is Action.AddTodoAsSibling ->
focusDirectionToMove = FocusDirection.Down
is Action.DeleteTodo ->
focusDirectionToMove = FocusDirection.Up

dispatch(action)

// My list of items
TodoListColumn(lists, wrappedDispatch)

// If we've previously asked to move the focus, do it when
// the lists parameter changes
LaunchedEffect(lists)
focusDirectionToMove?.let(focusManager::moveFocus)
focusDirectionToMove = null

LEER  Configure una luz de notificación externa simple para dispositivos Android, ideal para personas mayores | Alerta vía robot | Noviembre de 2021

Publicaciones relacionadas

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Botón volver arriba