Компонент 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 .