최데브는 오늘도 프로그래밍을 한다.

[안드로이드 컴포즈] 스와이프 삭제 구현 본문

Android/Android Compose

[안드로이드 컴포즈] 스와이프 삭제 구현

최데브 2024. 1. 14. 21:29
반응형

안드로이드 컴포즈에는 SwipeToDismiss 라는게 이미 만들어져 있다.

레이아웃을 옆으로 밀면 밀어낸 반대 방향에 버튼이 생기는 레이아웃 같은걸 만들때 쓴다.

대충 이런 기능

하지만? 나는 또 다른 디자인으로 만들고 싶었다. 찾아보면 이걸 사용해서도 방법은 있겠지만

그냥 내가 생각나는대로 만드는게 빠를거 같아서 만들어봤다.

 

혹시나 구글이 준비해준 SwipeToDismiss  가 맘에 안드는 사람들에게 귀찮음을 덜어줄 수 있었으면 한다.

만드려는 레이아웃은 이렇다.

움직이기 전(좌) , 움직인 후 (우)

 

 

순서는 아래와 같다.

 

1. 스와이프 했음을 인지하는 방법

2. 스와이프한 값을 이용해서 컴포넌트를 스와이프 한 방향으로 옮기는 방법

3. 삭제 버튼을 나오게 하는 방법

 

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun IntroduceComponent(
    isDimmed : (Boolean)->Unit
) {
    var isDim by remember { mutableStateOf(false) }
    var offsetX by remember { mutableStateOf(0.dp) }
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .padding(start = 22.dp)
            .pointerInput(Unit) {
                detectTransformGestures { _, pan, _, _ ->
                    val translationX = pan.x
                    onLeftSwipe(translationX) { it ->
                        if ((offsetX * -1) > 80.dp) {
                            offsetX = (offsetX + it).coerceAtLeast((-81).dp)
                            isDimmed(true)
                            isDim = true
                        } else {
                            offsetX = (offsetX + it).coerceAtMost(0.dp)
                            isDimmed(false)
                            isDim = false
                        }
                    }
                }
            }
    ) {
        // 내용물 (가장 마지막에 제공된 코드 참고)
    }
}

 

위 코드에서 핵심적으로 볼건  아래와 같다. 설명은 주석으로 대신했다.

   // isDim 이라고 표현했지만 삭제버튼이 나올정도로 스와이프를 했을때 true , 다시 원래로 돌아갔을때 false 
   //로 체크하는 변수다.
   var isDim by remember { mutableStateOf(false) } 
   //스와이프한 만큼 레이아웃을 옮겨주기 위해 offset 값을 담는 변수다.
   var offsetX by remember { mutableStateOf(0.dp) }
    
  // 스와이프할 내용과 등장시킬 버튼을 모두 담고있는 레이아웃의modifier 에 붙여서
  //x 값이 얼마나 변화했는지 체크한다.
 .pointerInput(Unit) {
                detectTransformGestures { _, pan, _, _ ->
                    val translationX = pan.x
                    onLeftSwipe(translationX) { it ->
                    //80으로 값을 비교하고 있는 이유는 스와이프 되는 최대 위치를 제한하고 싶어서다.
                        if ((offsetX * -1) > 80.dp) {
                            offsetX = (offsetX + it).coerceAtLeast((-81).dp)
                            isDimmed(true)
                            isDim = true
                        } else {
                        	//원래 위치로 돌아오는 로직
                            offsetX = (offsetX + it).coerceAtMost(0.dp)
                            isDimmed(false)
                            isDim = false
                        }
                    }
                }
            }
         
 //translationX < 0으로 한 이유는 왼쪽으로 이동하는 경우만 체크했기 때문이고
 //0.8을 곱한건 스와이프를 할때 레이아웃이 이동하는 속도를 감속하고 싶어서 적어줬다.
 //값은 원하는대로 조절해서 쓰면 된다.
 private fun onLeftSwipe(translationX: Float,callback : (Dp)->Unit) {
    if (translationX < 0) {
        callback((translationX * 0.8).dp)
    }else{
        callback((translationX * 0.8).dp)
    }
}

 

그리고 실제로 버튼과 스와이프 할 내용물의 코드는 이렇게 작성했다.

           //이 컴포넌트는 본인이 작성한 걸로 대체하면 된다.
           LinkGroupComponent(
                "만나서 반가워요\n링크zip을 소개할게요!",
                R.drawable.guide_image,
                LinkZipTheme.color.orangeFFE6C1,
                1L,
                modifier = Modifier.offset(x = offsetX) //offsetX을 이용해서 이동시킨다.
            ) {}
            //버튼이 등장해야함을 알려주는 isDim이 true 가 됐을때
            //삭제 버튼이 등장한다.
            if (isDim) {
                Box(
                    modifier = Modifier.width(80.dp).height(80.dp).offset(x = -24.dp).align(Alignment.CenterEnd)
                        .clip(RoundedCornerShape(12.dp))
                        .background(color = LinkZipTheme.color.redFB5B63)
                        .padding(
                            vertical = 10.dp
                        )
                        .clickable {

                        },
                ) {
                    Image(
                        painter = painterResource(id = R.drawable.delete),
                        contentDescription = "delete",
                        modifier = Modifier.align(Alignment.Center)
                    )
                }

            }

 

아직 미완성 코드긴 하지만 이런식으로 스와이프시 삭제버튼이 등장하는 기능을 만들었다.

삭제 버튼이 등장하는게 너무 딱딱해보인다면

AnimatedVisibility() 를 이용해보는것도 괜찮을거 같다.

반응형
Comments