깨알 개념/Android

[Android] Pointer input - Drag

interfacer_han 2025. 3. 23. 15:29

Pointer input 게시글 시리즈


#1 개요

#1-1 공식 문서

 

드래그, 스와이프, 플링  |  Jetpack Compose  |  Android Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. 드래그, 스와이프, 플링 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. draggable 수정자는 동작을 한 방

developer.android.com

위 공식 문서를 나의 언어로 정리했다.

 

#1-2 AnchoredDraggable

 

Swipeable에서 AnchoredDraggable로 이전  |  Jetpack Compose  |  Android Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. Swipeable에서 AnchoredDraggable로 이전 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 경고: 이 페이지와 여

developer.android.com

Swipeable이 AnchoredDraggable로 업그레이드되었다고 한다. 위 링크는 해당 업데이트를 전하는 페이지다.

 

#1-3 유튜브 영상

AnchoredDraggable에 대한 개론을 담은 공식 영상이다. 왜인지는 모르겠지만, 영상 썸네일에 안드로이드 캐릭터가 해적이 쓸 법한 안대를 쓰고 있다.

 

#2 드래그 (drag)

#2-1 개요

Modifier.draggable()은 컴포넌트에 드래그를 '가능하게' 한다. Modifier.draggable() 만으로 드래그가 '되는' 것은 아니다. Modifier.scrollable()과 같은 맥락이다. Modifier.scrollable()에 대해 다룬 아래 링크의 게시글을 읽으면 이해가 쉬울 것이다.

 

 

[Android] Pointer input - Scroll

#1 개요 스크롤  |  Jetpack Compose  |  Android Developers이 페이지는 Cloud Translation API를 통해 번역되었습니다. 스크롤 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세

kenel.tistory.com

위 게시글에서, 스크롤을 '가능하게' 하는 것은 Modifier.scrollable()이었다. 그리고 스크롤이 '되게' 하는 것은 Modifier.verticalScroll() 또는 Modifier.horizontalScroll()이었다. 이와 비슷하게 드래그를 '가능하게' 하는 것은 Modifier.draggable()이다. 그리고 스크롤이 '되게' 하는 것은 detectDragGestures()다.

 

#2-2 Modifier.draggable()가 사용된 코드 예시

@Composable
private fun DraggableText() {
    var offsetX by remember { mutableStateOf(0f) }
    Text(
        modifier = Modifier
            .offset { IntOffset(offsetX.roundToInt(), 0) }
            .draggable(
                orientation = Orientation.Horizontal,
                state = rememberDraggableState { delta ->
                    offsetX += delta
                }
            ),
        text = "Drag me!"
    )
}

 

#2-3 detectDragGestures()가 사용된 코드 예시

@Composable
private fun DraggableTextLowLevel() {
    Box(modifier = Modifier.fillMaxSize()) {
        var offsetX by remember { mutableStateOf(0f) }
        var offsetY by remember { mutableStateOf(0f) }

        Box(
            Modifier
                .offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) }
                .background(Color.Blue)
                .size(50.dp)
                .pointerInput(Unit) {
                    detectDragGestures { change, dragAmount ->
                        change.consume()
                        offsetX += dragAmount.x
                        offsetY += dragAmount.y
                    }
                }
        )
    }
}

 

#3 플링 (fling)

플링(fling)이란, 드래그 도중 손을 떼는 것을 의미한다. 스마트폰에서 웹 페이지를 스크롤할 때를 떠올려 보라. 손가락으로 화면을 드래그하다가 손을 갑자기 휙 떼면, 마치 스크롤에 관성이 있는 것처럼 여전히 스크롤되지 않는가? 그걸을 플링이라고 한다. 역방향으로 생각해보면, 플링은 반드시 드래그가 선행되어야 발생하는 이벤트라는 것도 알 수 있다.

 

#4 스와이프 (swipe)

https://developer.android.com/static/develop/ui/compose/images/gestures-swipe.gif

스와이프(swipe)란, 드래그나 플링 종료 시 컴포넌트가 (프로그래머에 의해 정해진) '앵커 포인트'로 이동하는 이벤트다. 역방향으로 생각해보면, 스와이프는 반드시 드래그 또는 플링이 선행되어야 발생하는 이벤트라는 것도 알 수 있다. 스와이프 동작을 위해선 Modifier.swipeable()이 필요하다. 그러나 #1-2에서 말했듯, Swipeable는 deprecated될 예정이다. 따라서 본 게시글에선 Modifier.swipeable() 대신 Modifier.anchoredDraggable()을 사용한다.

 

#5 AnchoredDraggable

#5-1 개요 

공식 문서에 있는 내용을 잘 읽고 소화하려했다. 하지만 늬앙스만 이해되었고 내부 기제를 정확히 이해할 순 없었다. 이런 겉핥기식 이해로는, 실제 구현을 할 수 없다. 따라서 미니 프로젝트를 통해 공식 문서의 코드를 따라해보겠다. 우선 AnchoredDraggableExample이라는 이름의 Jetpack Compose 프로젝트를 만들었다.

 

#5-2 기본 구조

...

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            AnchoredDraggableExampleTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Box(
                        modifier = Modifier
                            .fillMaxSize()
                            .padding(innerPadding)
                    ) {
                        SwipeableBox()
                    }
                }
            }
        }
    }
}

@Composable
fun SwipeableBox() {
    Box(
        modifier = Modifier
            .width(200.dp)
            .height(200.dp)
            .background(Color.LightGray)
    ) {
        Text("Swipe me")
    }
}

화면 전체를 차지하는 Box()에 스와이프를 적용할 작은 Box()를 두었다.

 

#5-3 AnchoredDraggableState

내용 추가 예정.