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回呼。回呼主體會處理各種可能的動作。回呼會傳回布林值,告知元件是否應顯示關閉動畫。在這種情況下:- 如果從開頭到結尾滑動項目,系統會呼叫
onToggleDonelambda,並傳遞目前的todoItem。這會對應至更新待辦事項項目。 - 如果從結尾滑動至開頭,系統會呼叫
onRemovelambda,並傳遞目前的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 原始檔案。