Jetpack Compose | 밀어서 삭제 (SwipeToDismiss) 구현

J

SwipeToDismiss(밀어서 삭제) 방식은 리스트에서 아이템을 삭제하거나, 좋아요나 플레이리스트 등 특정 그룹에 추가하는 기능을 구현할 때 용이하다.

Material 라이브러리에서는 이를 쉽게 제작할 수 있도록 SwipeToDismiss 라는 Composable 함수를 지원한다.

작성 방법이 쉽기 때문에 금방 따라 작성할 수 있다.

val dismissState = rememberDismissState(confirmStateChange = {               
    if (it == DismissValue.DismissedToStart) {                              
        /**                                                                 
         * Dismiss 되었을 때 작업할 코드 작성                                          
         */                                                                 
    }                                                                       
    true                                                                     
})                                                                          
                                                                            
SwipeToDismiss(state = dismissState, directions = setOf(                    
    DismissDirection.EndToStart                                             
), dismissThresholds = { direction ->                                       
    FractionalThreshold(0.4f)                                               
}, background = {                                                            
    val color by animateColorAsState(                                       
        when (dismissState.targetValue) {                                   
            DismissValue.Default -> Color.White                             
            else -> Color.Red                                               
        }                                                                   
    )                                                                       
    val scale by animateFloatAsState(                                       
        if (dismissState.targetValue == DismissValue.Default) 0.75f else 1f 
    )                                                                       
    Box(                                                                    
        modifier = Modifier                                                 
            .fillMaxSize()                                                  
            .background(color)                                              
            .padding(horizontal = Dp(20f)),                                 
        contentAlignment = Alignment.CenterEnd                              
    ) {                                                                      
        Icon(                                                               
            Icons.Default.Delete,                                           
            contentDescription = "Delete Icon",                             
            modifier = Modifier.scale(scale)                                
        )                                                                   
    }                                                                       
}, dismissContent = {                                                        
    Card(                                                                   
        shape = RoundedCornerShape(0.dp),                                   
        elevation = animateDpAsState(                                       
            if (dismissState.dismissDirection != null) 4.dp else 0.dp       
        ).value                                                             
    ) {                                                                     
        /**                                                                 
         * 원본 콘텐츠 구성가능한함수 작성                                                
         */                                                                 
    }                                                                       
})                                                                          

state : 앞선 코드에서 선언한 DismissState 에서 confirmStateChange 콜백 함수를 통해 SwipeToDismiss Composable 함수의 Dismiss 상태를 추적할 수 있다.

DismissValue.DismissedToStart : 오른쪽에서 왼쪽으로 Dismiss 됨

DismissValue.DismissedToEnd : 왼쪽에서 오른쪽으로 Dismiss 됨

Default : Dismiss 되지 않은 상태

의도한 방향으로 Dismiss 된 경우 리스트 아이템 삭제 등 작업을 수행하면 된다.

dismissThresholds : Dismiss 되는 임계치 이다. 0.0f ~ 1.0f, ex) 0.5f 로 설정한 경우 50% 이상 Swipe 된 경우 Dismiss 된 것으로 간주

M3 에서는 dismissThresholds 인자가 없다. DismissState에 다음과 같이 positionalThreshold를 지정하면 된다.

val dismissState =
    androidx.compose.material3.rememberDismissState(positionalThreshold = {
        it * 0.4f
    }, confirmValueChange = {})

background : Dismiss 된 경우 후방에 위치하는(본 컨텐츠 뒤에 위치하는) Composable 함수. 이전에 선언한 DismissState에 따라 애니메이션을 삽입 할 수 있다.

dismissContent : 본 컨텐츠가 되는 Composable 함수 (Dismiss 되기 전까지 보여지는 컨텐츠)

아이템 삭제시 자연스럽게 하기

DismissedToStart등 State 반응시 즉시 아이템을 제거하는 등의 작업을 하게 되면 UI 가 부자연스럽다.

SwipeToDismiss의 modifier에 animateItemPlacement()를 적용하면 리스트에서 아이템이 제거 되었을 때 보다 자연스럽게 반응한다.

또한 다음과 같이 일정 시간 동안 딜레이를 준 뒤 본 작업을 수행 하는 것이 더 보기 좋다.

val dismissState = rememberDismissState(confirmStateChange = {               
    if (it == DismissValue.DismissedToStart) {                              
        CoroutineScope(Dispatchers.Main).launch {                            
            delay(400)                                                      
            viewModel.remove(resource)                                      
        }                                                                   
    }                                                                       
    true                                                                     
})                                                                          

About the author

Gyeongrok Kim

Add comment

Your sidebar area is currently empty. Hurry up and add some widgets.