SwipeToDismissBox 元件可讓使用者向左或向右滑動,關閉或更新項目。
API 介面
使用 SwipeToDismissBox 可組合項,實作由滑動手勢觸發的動作。重要參數包括:
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( 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.") } ) } }
程式碼重點
swipeToDismissBoxState會管理元件狀態。與項目互動完成後,系統會觸發confirmValueChange回呼。回呼主體會處理各種可能的操作。回呼會傳回布林值,告知元件是否應顯示關閉動畫。具體如下:- 如果項目從頭滑動到尾,則會呼叫
onToggleDonelambda,並傳遞目前的todoItem。這對應於更新待辦事項。 - 如果項目從結尾滑動到開頭,則會呼叫
onRemovelambda,並傳遞目前的todoItem。這相當於刪除待辦事項。 it != StartToEnd:如果滑動方向不是StartToEnd,這一行會傳回true,否則會傳回false。傳回false可防止SwipeToDismissBox在「切換完成」滑動後立即消失,以便進行視覺確認或動畫。
- 如果項目從頭滑動到尾,則會呼叫
SwipeToDismissBox可在每個項目上啟用水平滑動互動。在靜止狀態下,它會顯示元件的內部內容,但當使用者開始滑動時,內容會移開,並顯示backgroundContent。一般內容和backgroundContent都會取得父項容器的完整限制,以便在其中算繪。content會繪製在backgroundContent之上。具體如下:backgroundContent是以Icon的形式實作,背景顏色則根據SwipeToDismissBoxValue決定:- 滑動
StartToEnd時為Blue,切換待辦事項。 - 滑動
EndToStart時,Red會刪除待辦事項。 Settled的背景不會顯示任何內容,也就是說,如果沒有滑動項目,背景就不會顯示任何內容。- 同樣地,顯示的
Icon會根據滑動方向調整: StartToEnd會在待辦事項完成時顯示CheckBox圖示,未完成時則顯示CheckBoxOutlineBlank圖示。EndToStart會顯示Delete圖示。
@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() ) } } }
程式碼重點
mutableStateListOf(...)會建立可觀察清單,用於存放TodoItem物件。當清單中新增或移除項目時,Compose 會重組依賴該項目的 UI 部分。- 在
mutableStateListOf()中,四個TodoItem物件會使用各自的說明進行初始化:「Pay bills」、「Buy groceries」、「Go to gym」和「Get dinner」。
- 在
LazyColumn會顯示todoItems的垂直捲動清單。onToggleDone = { todoItem -> ... }是在使用者將物件標示為完成時,從TodoListItem內叫用的回呼函式。它會更新todoItem的isItemDone屬性。由於todoItems是mutableStateListOf,因此這項變更會觸發重組,更新 UI。onRemove = { todoItem -> ... }是使用者移除項目時觸發的回呼函式。從todoItems清單中移除特定todoItem。這也會導致重組,且該項目會從顯示的清單中移除。- 系統會將
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( 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.") } ) } } }
程式碼重點
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 來源檔案。