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콜백을 트리거합니다. 콜백 본문은 가능한 다양한 작업을 처리합니다. 콜백은 구성요소에 닫기 애니메이션을 표시해야 하는지 여부를 알려주는 불리언을 반환합니다. 이 경우 다음 단계를 수행합니다.- 항목을 시작에서 끝으로 스와이프하면
onToggleDone람다를 호출하여 현재todoItem를 전달합니다. 이는 할 일 항목 업데이트에 해당합니다. - 항목을 끝에서 시작으로 스와이프하면
onRemove람다를 호출하여 현재todoItem를 전달합니다. 이는 할 일 항목을 삭제하는 것에 해당합니다. it != StartToEnd: 이 줄은 스와이프 방향이StartToEnd가 아니면true를 반환하고 그렇지 않으면false를 반환합니다.false를 반환하면 '완료 전환' 스와이프 후SwipeToDismissBox가 즉시 사라지지 않으므로 시각적 확인 또는 애니메이션이 가능합니다.
- 항목을 시작에서 끝으로 스와이프하면
SwipeToDismissBox는 각 항목에서 가로 스와이프 상호작용을 사용 설정합니다. 정지 상태에서는 구성요소의 내부 콘텐츠를 표시하지만 사용자가 스와이프를 시작하면 콘텐츠가 사라지고backgroundContent가 표시됩니다. 일반 콘텐츠와backgroundContent모두 상위 컨테이너의 전체 제약 조건을 가져와 자체적으로 렌더링합니다.content은backgroundContent위에 그려집니다. 이 경우 다음 단계를 수행합니다.backgroundContent는SwipeToDismissBoxValue에 따라 배경 색상이 있는Icon로 구현됩니다.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()내에서 4개의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 소스 파일을 참고하세요.