El componente SwipeToDismissBox
permite que un usuario descarte o actualice un elemento deslizándolo hacia la izquierda o la derecha.
Plataforma de la API
Usa el elemento componible SwipeToDismissBox
para implementar acciones que se activan con gestos de deslizamiento. Entre los parámetros clave, se incluyen los siguientes:
state
: Es el estadoSwipeToDismissBoxState
creado para almacenar el valor que producen los cálculos en el elemento de deslizamiento, que activa eventos cuando se produce.backgroundContent
: Es un elemento componible personalizable que se muestra detrás del contenido del elemento y que se revela cuando se desliza el contenido.
Ejemplo básico: Actualiza o descarta con un deslizamiento
Los fragmentos de este ejemplo muestran una implementación de deslizamiento que actualiza el elemento cuando se desliza de un extremo al otro o lo descarta cuando se desliza de un extremo al otro.
data class TodoItem( val itemDescription: String, var isItemDone: Boolean = false )
@Composable fun TodoListItem( todoItem: TodoItem, onToggleDone: (TodoItem) -> Unit, onRemove: (TodoItem) -> Unit, modifier: Modifier = Modifier, ) { val swipeToDismissBoxState = rememberSwipeToDismissBoxState( confirmValueChange = { if (it == StartToEnd) onToggleDone(todoItem) else if (it == EndToStart) onRemove(todoItem) // Reset item when toggling done status it != StartToEnd } ) SwipeToDismissBox( state = swipeToDismissBoxState, modifier = modifier.fillMaxSize(), backgroundContent = { when (swipeToDismissBoxState.dismissDirection) { StartToEnd -> { Icon( if (todoItem.isItemDone) Icons.Default.CheckBox else Icons.Default.CheckBoxOutlineBlank, contentDescription = if (todoItem.isItemDone) "Done" else "Not done", modifier = Modifier .fillMaxSize() .background(Color.Blue) .wrapContentSize(Alignment.CenterStart) .padding(12.dp), tint = Color.White ) } EndToStart -> { Icon( imageVector = Icons.Default.Delete, contentDescription = "Remove item", modifier = Modifier .fillMaxSize() .background(Color.Red) .wrapContentSize(Alignment.CenterEnd) .padding(12.dp), tint = Color.White ) } Settled -> {} } } ) { ListItem( headlineContent = { Text(todoItem.itemDescription) }, supportingContent = { Text("swipe me to update or remove.") } ) } }
Puntos clave sobre el código
swipeToDismissBoxState
administra el estado del componente. Activa la devolución de llamadaconfirmValueChange
una vez que se completa la interacción con el elemento. El cuerpo de la devolución de llamada controla las diferentes acciones posibles. La devolución de llamada muestra un valor booleano que le indica al componente si debe mostrar una animación de descarte. En este caso:- Si se desliza el elemento de un extremo al otro, llama a la lambda
onToggleDone
y pasa eltodoItem
actual. Esto corresponde a la actualización del elemento de tareas pendientes. - Si se desliza el elemento de un extremo al otro, llama a la lambda
onRemove
y pasa eltodoItem
actual. Esto corresponde a borrar el elemento de tareas pendientes. it != StartToEnd
: Esta línea muestratrue
si la dirección del deslizamiento no esStartToEnd
yfalse
de lo contrario. Devolverfalse
evita queSwipeToDismissBox
desaparezca de inmediato después de un deslizamiento de "activar/desactivar", lo que permite una confirmación o animación visual.
- Si se desliza el elemento de un extremo al otro, llama a la lambda
SwipeToDismissBox
habilita interacciones de deslizamiento horizontal en cada elemento. En reposo, muestra el contenido interno del componente, pero cuando un usuario comienza a deslizar el dedo, el contenido se aleja y aparece elbackgroundContent
. Tanto el contenido normal como elbackgroundContent
obtienen las restricciones completas del contenedor superior para renderizarse. Elcontent
se dibuja sobre elbackgroundContent
. En este caso:backgroundContent
se implementa como unIcon
con un color de fondo basado enSwipeToDismissBoxValue
:Blue
cuando se deslizaStartToEnd
: Activa o desactiva una tarea pendiente.Red
cuando se deslizaEndToStart
: Borra una tarea pendiente.- No se muestra nada en segundo plano para
Settled
; cuando no se desliza el elemento, no se muestra nada en segundo plano. - Del mismo modo, el
Icon
que se muestra se adapta a la dirección del deslizamiento: StartToEnd
muestra un ícono deCheckBox
cuando se completa la tarea y un ícono deCheckBoxOutlineBlank
cuando no se completa.EndToStart
muestra un íconoDelete
.
@Composable private fun SwipeItemExample() { val todoItems = remember { mutableStateListOf( TodoItem("Pay bills"), TodoItem("Buy groceries"), TodoItem("Go to gym"), TodoItem("Get dinner") ) } LazyColumn { items( items = todoItems, key = { it.itemDescription } ) { todoItem -> TodoListItem( todoItem = todoItem, onToggleDone = { todoItem -> todoItem.isItemDone = !todoItem.isItemDone }, onRemove = { todoItem -> todoItems -= todoItem }, modifier = Modifier.animateItem() ) } } }
Puntos clave sobre el código
mutableStateListOf(...)
crea una lista observable que puede contener objetosTodoItem
. Cuando se agrega o quita un elemento de esta lista, Compose recompone las partes de la IU que dependen de él.- Dentro de
mutableStateListOf()
, se inicializan cuatro objetosTodoItem
con sus respectivas descripciones: “Pagar facturas”, “Comprar comestibles”, “Ir al gimnasio” y “Comer”.
- Dentro de
LazyColumn
muestra una lista detodoItems
con desplazamiento vertical.onToggleDone = { todoItem -> ... }
es una función de devolución de llamada que se invoca desdeTodoListItem
cuando el usuario marca un objeto como terminado. Actualiza la propiedadisItemDone
detodoItem
. ComotodoItems
es unmutableStateListOf
, este cambio activa una recomposición y actualiza la IU.onRemove = { todoItem -> ... }
es una función de devolución de llamada que se activa cuando el usuario quita el elemento. Quita eltodoItem
específico de la listatodoItems
. Esto también causa una recomposición, y el elemento se quitará de la lista que se muestra.- Se aplica un modificador
animateItem
a cadaTodoListItem
para que se llame aplacementSpec
del modificador cuando se descarte el elemento. Esto anima la eliminación del elemento, así como la reorganización de otros elementos de la lista.
Resultado
En el siguiente video, se muestra la funcionalidad básica de deslizar para descartar de los fragmentos anteriores:
Consulta el archivo fuente de GitHub para ver el código de muestra completo.
Ejemplo avanzado: Anima el color de fondo al deslizar el dedo
En los siguientes fragmentos, se muestra cómo incorporar un umbral de posición para animar el color de fondo de un elemento cuando se desliza el dedo.
data class TodoItem( val itemDescription: String, var isItemDone: Boolean = false )
@Composable fun TodoListItemWithAnimation( todoItem: TodoItem, onToggleDone: (TodoItem) -> Unit, onRemove: (TodoItem) -> Unit, modifier: Modifier = Modifier, ) { val swipeToDismissBoxState = rememberSwipeToDismissBoxState( confirmValueChange = { if (it == StartToEnd) onToggleDone(todoItem) else if (it == EndToStart) onRemove(todoItem) // Reset item when toggling done status it != StartToEnd } ) SwipeToDismissBox( state = swipeToDismissBoxState, modifier = modifier.fillMaxSize(), backgroundContent = { when (swipeToDismissBoxState.dismissDirection) { StartToEnd -> { Icon( if (todoItem.isItemDone) Icons.Default.CheckBox else Icons.Default.CheckBoxOutlineBlank, contentDescription = if (todoItem.isItemDone) "Done" else "Not done", modifier = Modifier .fillMaxSize() .drawBehind { drawRect(lerp(Color.LightGray, Color.Blue, swipeToDismissBoxState.progress)) } .wrapContentSize(Alignment.CenterStart) .padding(12.dp), tint = Color.White ) } EndToStart -> { Icon( imageVector = Icons.Default.Delete, contentDescription = "Remove item", modifier = Modifier .fillMaxSize() .background(lerp(Color.LightGray, Color.Red, swipeToDismissBoxState.progress)) .wrapContentSize(Alignment.CenterEnd) .padding(12.dp), tint = Color.White ) } Settled -> {} } } ) { OutlinedCard(shape = RectangleShape) { ListItem( headlineContent = { Text(todoItem.itemDescription) }, supportingContent = { Text("swipe me to update or remove.") } ) } } }
Puntos clave sobre el código
drawBehind
dibuja directamente en el lienzo detrás del contenido del elemento componibleIcon
.drawRect()
dibuja un rectángulo en el lienzo y completa todos los límites del alcance de dibujo con elColor
especificado.
- Cuando se desliza el dedo, el color de fondo del elemento cambia de forma fluida con
lerp
.- Para un deslizamiento desde
StartToEnd
, el color de fondo cambia gradualmente de gris claro a azul. - Para un deslizamiento desde
EndToStart
, el color de fondo cambia gradualmente de gris claro a rojo. - La cantidad de transición de un color al siguiente se determina mediante
swipeToDismissBoxState.progress
.
- Para un deslizamiento desde
OutlinedCard
agrega una separación visual sutil entre los elementos de la lista.
@Composable private fun SwipeItemWithAnimationExample() { val todoItems = remember { mutableStateListOf( TodoItem("Pay bills"), TodoItem("Buy groceries"), TodoItem("Go to gym"), TodoItem("Get dinner") ) } LazyColumn { items( items = todoItems, key = { it.itemDescription } ) { todoItem -> TodoListItemWithAnimation( todoItem = todoItem, onToggleDone = { todoItem -> todoItem.isItemDone = !todoItem.isItemDone }, onRemove = { todoItem -> todoItems -= todoItem }, modifier = Modifier.animateItem() ) } } }
Puntos clave sobre el código
- Para obtener información sobre los puntos clave de este código, consulta Puntos clave en una sección anterior, que describe un fragmento de código idéntico.
Resultado
En el siguiente video, se muestra la funcionalidad avanzada con color de fondo animado:
Consulta el archivo fuente de GitHub para ver el código de muestra completo.