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
ラムダが呼び出され、現在のtodoItem
が渡されます。これは、ToDo アイテムの更新に対応しています。 - アイテムが最後から先頭にスワイプされた場合、
onRemove
ラムダが呼び出され、現在のtodoItem
が渡されます。これは、ToDo アイテムの削除に対応しています。 it != StartToEnd
: この行は、スワイプ方向がStartToEnd
でない場合true
を返し、それ以外の場合はfalse
を返します。false
を返すと、「切り替えが完了しました」のスワイプ後にSwipeToDismissBox
がすぐに消えなくなり、視覚的な確認やアニメーションが可能になります。
- アイテムが最初から最後までスワイプされると、
SwipeToDismissBox
を使用すると、各アイテムを横方向にスワイプできます。休止状態では、コンポーネントの内部コンテンツが表示されますが、ユーザーがスワイプするとコンテンツが移動し、backgroundContent
が表示されます。通常のコンテンツとbackgroundContent
の両方が、レンダリングする親コンテナの完全な制約を取得します。content
はbackgroundContent
の上に描画されます。この場合:backgroundContent
は、SwipeToDismissBoxValue
に基づく背景色を持つIcon
として実装されます。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 はそれに依存する UI の部分を再コンポーズします。mutableStateListOf()
内で、4 つのTodoItem
オブジェクトが、それぞれ「支払い」、「食料品の購入」、「ジムに行く」、「夕食をとる」という説明で初期化されます。
LazyColumn
は、todoItems
の縦方向にスクロールするリストを表示します。onToggleDone = { todoItem -> ... }
は、ユーザーがオブジェクトを完了済みとしてマークしたときにTodoListItem
内から呼び出されるコールバック関数です。todoItem
のisItemDone
プロパティが更新されます。todoItems
はmutableStateListOf
であるため、この変更により再コンポーズがトリガーされ、UI が更新されます。onRemove = { todoItem -> ... }
は、ユーザーがアイテムを削除したときにトリガーされるコールバック関数です。todoItems
リストから特定のtodoItem
が削除されます。これにより再コンポーズも行われ、表示されるリストからアイテムが削除されます。- 各
TodoListItem
にanimateItem
修飾子が適用されるため、アイテムが閉じられたときに修飾子の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
からスワイプすると、背景色がライトグレーから赤に徐々に変化します。- 1 つの色から次の色への遷移量は、
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 ソースファイルをご覧ください。