Компонент SwipeToDismissBox
позволяет пользователю закрыть или обновить элемент, проведя по нему пальцем влево или вправо.
API поверхность
Используйте SwipeToDismissBox
composable для реализации действий, которые запускаются жестами смахивания. Ключевые параметры включают:
-
state
: СостояниеSwipeToDismissBoxState
, созданное для хранения значения, полученного в результате вычислений для элемента смахивания, которое запускает события при создании. -
backgroundContent
: настраиваемый компонуемый элемент, отображаемый позади содержимого элемента, который становится виден при пролистывании содержимого.
Простой пример: обновление или удаление при смахивании
Фрагменты в этом примере демонстрируют реализацию смахивания, которая либо обновляет элемент при смахивании от начала до конца, либо закрывает элемент при смахивании от конца до начала.
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.") } ) } }
Ключевые моменты кодекса
-
swipeToDismissBoxState
управляет состоянием компонента. Он запускает обратный вызовconfirmValueChange
после завершения взаимодействия с элементом. Тело обратного вызова обрабатывает различные возможные действия. Обратный вызов возвращает логическое значение, которое сообщает компоненту, следует ли отображать анимацию отклонения. В этом случае:- Если элемент пролистывается от начала до конца, он вызывает лямбду
onToggleDone
, передавая текущийtodoItem
. Это соответствует обновлению элемента todo. - Если элемент смахивается с конца в начало, вызывается лямбда-функция
onRemove
, передающая текущийtodoItem
. Это соответствует удалению элемента todo. -
it != StartToEnd
: Эта строка возвращаетtrue
если направление смахивания неStartToEnd
, иfalse
в противном случае. Возвращениеfalse
предотвращает немедленное исчезновениеSwipeToDismissBox
после смахивания «переключение выполнено», позволяя визуальное подтверждение или анимацию.
- Если элемент пролистывается от начала до конца, он вызывает лямбду
-
SwipeToDismissBox
позволяет горизонтальное взаимодействие смахивания для каждого элемента. В состоянии покоя он показывает внутреннее содержимое компонента, но когда пользователь начинает смахивание, содержимое перемещается и появляетсяbackgroundContent
. И обычное содержимое, иbackgroundContent
получают полные ограничения родительского контейнера для рендеринга.content
рисуется поверхbackgroundContent
. В этом случае:-
backgroundContent
реализован какIcon
с фоновым цветом на основеSwipeToDismissBoxValue
: -
Blue
при смахиванииStartToEnd
— переключение между задачами. -
Red
при смахиванииEndToStart
— удаление элемента списка дел. - Для
Settled
ничего не отображается на заднем плане — когда элемент не перемещается, на заднем плане ничего не отображается. - Аналогично, отображаемый
Icon
адаптируется к направлению проведения пальцем: -
StartToEnd
отображает значокCheckBox
, когда задача выполнена, и значокCheckBoxOutlineBlank
, когда задача не выполнена. -
EndToStart
отображает значокDelete
.
-
@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() ) } } }
Ключевые моменты кодекса
-
mutableStateListOf(...)
создает наблюдаемый список, который может содержать объектыTodoItem
. Когда элемент добавляется или удаляется из этого списка, Compose перекомпоновывает части пользовательского интерфейса, которые зависят от него.- Внутри
mutableStateListOf()
инициализируются четыре объектаTodoItem
с соответствующими описаниями: «Оплатить счета», «Купить продукты», «Сходить в спортзал» и «Приготовить ужин».
- Внутри
-
LazyColumn
отображает вертикально прокручиваемый списокtodoItems
. -
onToggleDone = { todoItem -> ... }
— это функция обратного вызова, вызываемая изTodoListItem
, когда пользователь отмечает объект как выполненный. Она обновляет свойствоisItemDone
дляtodoItem
. ПосколькуtodoItems
— этоmutableStateListOf
, это изменение запускает перекомпозицию, обновляя пользовательский интерфейс. -
onRemove = { todoItem -> ... }
— это функция обратного вызова, которая активируется, когда пользователь удаляет элемент. Она удаляет конкретныйtodoItem
из спискаtodoItems
. Это также вызывает перекомпозицию, и элемент будет удален из отображаемого списка. - Модификатор
animateItem
применяется к каждомуTodoListItem
, так чтоplacementSpec
модификатора вызывается, когда элемент был отклонен. Это анимирует удаление элемента, а также переупорядочивание других элементов в списке.
Результат
В следующем видео демонстрируется базовая функциональность смахивания для закрытия из предыдущих фрагментов:
Полный пример кода смотрите в исходном файле GitHub .
Расширенный пример: анимация цвета фона при смахивании
В следующих фрагментах показано, как использовать позиционный порог для анимации цвета фона элемента при проведении пальцем.
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.") } ) } } }
Ключевые моменты кодекса
-
drawBehind
рисует непосредственно на холсте за содержимым компонуемогоIcon
.-
drawRect()
рисует прямоугольник на холсте и заполняет все границы области рисования указаннымColor
.
-
- При проведении пальцем цвет фона элемента плавно меняется с помощью
lerp
.- При проведении пальцем от
StartToEnd
цвет фона постепенно меняется со светло-серого на синий. - При проведении пальцем от
EndToStart
цвет фона постепенно меняется со светло-серого на красный. - Величина перехода от одного цвета к другому определяется параметром
swipeToDismissBoxState.progress
.
- При проведении пальцем от
-
OutlinedCard
добавляет тонкое визуальное разделение между элементами списка.
@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() ) } } }
Ключевые моменты кодекса
- Основные положения этого кода см. в разделе «Основные положения» предыдущего раздела, в котором описывается идентичный фрагмент кода.
Результат
В следующем видео показаны расширенные функциональные возможности с анимированным фоновым цветом:
Полный пример кода смотрите в исходном файле GitHub .