Thành phần SwipeToDismissBox cho phép người dùng đóng hoặc cập nhật một mục bằng cách vuốt mục đó sang trái hoặc phải.
Nền tảng API
Sử dụng composable SwipeToDismissBox để triển khai các hành động được kích hoạt bằng cử chỉ vuốt. Các tham số chính bao gồm:
state: Trạng tháiSwipeToDismissBoxStateđược tạo để lưu trữ giá trị do các phép tính trên mục vuốt tạo ra, kích hoạt các sự kiện khi được tạo.backgroundContent: Thành phần composable có thể tuỳ chỉnh, hiển thị phía sau nội dung mục và xuất hiện khi nội dung được vuốt.
Ví dụ cơ bản: Cập nhật hoặc đóng khi vuốt
Các đoạn mã trong ví dụ này cho thấy một cách triển khai thao tác vuốt để cập nhật mục khi vuốt từ đầu đến cuối hoặc đóng mục khi vuốt từ cuối đến đầu.
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( painter = rememberVectorPainter(if (todoItem.isItemDone) AppIcons.CheckBox else AppIcons.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( painter = rememberVectorPainter(AppIcons.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.") } ) } }
Các điểm chính về mã
swipeToDismissBoxStatequản lý trạng thái của thành phần. Thao tác này sẽ kích hoạt lệnh gọi lạiconfirmValueChangesau khi người dùng tương tác xong với mục. Nội dung của lệnh gọi lại xử lý các hành động có thể thực hiện. Lệnh gọi lại trả về một giá trị boolean cho thành phần biết liệu có nên hiển thị ảnh động đóng hay không. Trong trường hợp này:- Nếu mục được vuốt từ đầu đến cuối, thì mục đó sẽ gọi lambda
onToggleDone, truyềntodoItemhiện tại. Điều này tương ứng với việc cập nhật mục việc cần làm. - Nếu mục được vuốt từ cuối đến đầu, thì mục đó sẽ gọi hàm lambda
onRemove, truyềntodoItemhiện tại. Thao tác này tương ứng với việc xoá mục việc cần làm. it != StartToEnd: Dòng này trả vềtruenếu hướng vuốt không phải làStartToEndvàfalsenếu không phải. Việc trả vềfalsesẽ ngănSwipeToDismissBoxbiến mất ngay lập tức sau khi vuốt "bật/tắt xong", cho phép xác nhận hoặc tạo ảnh động trực quan.
- Nếu mục được vuốt từ đầu đến cuối, thì mục đó sẽ gọi lambda
SwipeToDismissBoxcho phép tương tác vuốt ngang trên mỗi mục. Khi ở trạng thái nghỉ, thành phần này sẽ hiển thị nội dung bên trong, nhưng khi người dùng bắt đầu vuốt, nội dung sẽ được di chuyển đi vàbackgroundContentsẽ xuất hiện. Cả nội dung thông thường vàbackgroundContentđều nhận được các quy tắc ràng buộc đầy đủ của vùng chứa mẹ để tự kết xuất.contentđược vẽ lên trênbackgroundContent. Trong trường hợp này:backgroundContentđược triển khai dưới dạngIconcó màu nền dựa trênSwipeToDismissBoxValue:Bluekhi vuốtStartToEnd— bật/tắt một việc cần làm.Redkhi vuốtEndToStart— xoá một mục việc cần làm.- Không có nội dung nào hiển thị trong nền cho
Settled— khi mục không được vuốt, không có nội dung nào hiển thị trong nền. - Tương tự,
Iconhiển thị sẽ điều chỉnh theo hướng vuốt: StartToEndcho thấy biểu tượngCheckBoxkhi việc cần làm đã hoàn tất và biểu tượngCheckBoxOutlineBlankkhi việc cần làm chưa hoàn tất.EndToStarthiển thị biểu tượngDelete.
@Composable 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() ) } } }
Các điểm chính về mã
mutableStateListOf(...)tạo một danh sách có thể quan sát, có thể chứa các đối tượngTodoItem. Khi một mục được thêm vào hoặc bị xoá khỏi danh sách này, Compose sẽ kết hợp lại các phần của giao diện người dùng phụ thuộc vào mục đó.- Bên trong
mutableStateListOf(), bốn đối tượngTodoItemđược khởi tạo bằng nội dung mô tả tương ứng: "Thanh toán hoá đơn", "Mua hàng tạp hoá", "Đi tập thể dục" và "Ăn tối".
- Bên trong
LazyColumnhiển thị danh sách cuộn theo chiều dọc củatodoItems.onToggleDone = { todoItem -> ... }là một hàm callback được gọi từ trongTodoListItemkhi người dùng đánh dấu một đối tượng là đã hoàn tất. Thành phần này cập nhật thuộc tínhisItemDonecủatodoItem. VìtodoItemslà mộtmutableStateListOf, nên thay đổi này sẽ kích hoạt quá trình kết hợp lại, cập nhật giao diện người dùng.onRemove = { todoItem -> ... }là một hàm callback được kích hoạt khi người dùng xoá mục. Thao tác này sẽ xoátodoItemcụ thể khỏi danh sáchtodoItems. Thao tác này cũng gây ra quá trình kết hợp lại và mục này sẽ bị xoá khỏi danh sách hiển thị.- Đối tượng sửa đổi
animateItemđược áp dụng cho từngTodoListItemđểplacementSpeccủa đối tượng sửa đổi được gọi khi mục đã bị loại bỏ. Thao tác này tạo ảnh động cho việc xoá mục cũng như sắp xếp lại các mục khác trong danh sách.
Kết quả
Video sau đây minh hoạ chức năng vuốt để đóng cơ bản trong các đoạn mã trước:
Hãy xem tệp nguồn GitHub để biết mã mẫu đầy đủ.
Ví dụ nâng cao: Tạo ảnh động cho màu nền khi vuốt
Các đoạn mã sau đây cho biết cách kết hợp ngưỡng vị trí để tạo ảnh động cho màu nền của một mục khi vuốt.
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( painter = rememberVectorPainter(if (todoItem.isItemDone) AppIcons.CheckBox else AppIcons.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( painter = rememberVectorPainter(AppIcons.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.") } ) } } }
Các điểm chính về mã
drawBehindvẽ trực tiếp vào canvas phía sau nội dung của thành phần kết hợpIcon.drawRect()vẽ một hình chữ nhật trên canvas và lấp đầy toàn bộ ranh giới của phạm vi vẽ bằngColorđược chỉ định.
- Khi vuốt, màu nền của mục sẽ chuyển đổi mượt mà bằng cách sử dụng
lerp.- Khi vuốt từ
StartToEnd, màu nền sẽ dần chuyển từ màu xám nhạt sang màu xanh dương. - Khi vuốt từ
EndToStart, màu nền sẽ dần chuyển từ màu xám nhạt sang màu đỏ. - Mức độ chuyển đổi từ màu này sang màu tiếp theo được xác định bằng
swipeToDismissBoxState.progress.
- Khi vuốt từ
OutlinedCardthêm một đường phân tách trực quan tinh tế giữa các mục trong danh sách.
@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() ) } } }
Các điểm chính về mã
- Để biết các điểm chính về mã này, hãy xem phần Điểm chính trong phần trước, trong đó mô tả một đoạn mã giống hệt.
Kết quả
Video sau đây cho thấy chức năng nâng cao với màu nền động:
Hãy xem tệp nguồn GitHub để biết mã mẫu đầy đủ.