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( 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
lambda,並傳遞目前的todoItem
。這會對應至更新待辦事項項目。 - 如果從結尾滑動至開頭,系統會呼叫
onRemove
lambda,並傳遞目前的todoItem
。這會對應至刪除待辦事項。 it != StartToEnd
:如果滑動方向不是StartToEnd
,這行程式碼會傳回true
,否則傳回false
。傳回false
可避免SwipeToDismissBox
在「切換完成」滑動後立即消失,讓使用者可透過視覺確認或動畫確認。
- 如果從開頭到結尾滑動項目,系統會呼叫
SwipeToDismissBox
可讓您在每個項目上啟用水平滑動互動功能。在其餘時間,它會顯示元件的內部內容,但當使用者開始滑動時,內容會移除,並顯示backgroundContent
。一般內容和backgroundContent
都會取得父項容器的完整約束條件,以便在其中顯示。content
會繪製在backgroundContent
上方。在這種情況下:backgroundContent
會實作為Icon
,並以SwipeToDismissBoxValue
為基礎設定背景顏色:- 滑動
StartToEnd
時的Blue
:切換待辦事項。 - 滑動
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 會重組依賴該項目的 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( 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 原始檔案。