드래그, 스와이프, 플링

draggable 수정자는 동작을 한 방향으로 드래그하는 상위 수준 진입점이며 드래그 거리를 픽셀 단위로 보고합니다.

이 수정자는 동작만 감지한다는 점에서 scrollable과 유사합니다. 예를 들어 offset 수정자를 통해 요소를 이동하여 상태를 유지하고 화면에 표시해야 합니다.

@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!"
    )
}

전체 드래그 동작을 제어해야 하는 경우 대신 pointerInput 수정자를 통해 드래그 동작 감지기를 사용해 보세요.

@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
                    }
                }
        )
    }
}

손가락 누르기로 드래그 중인 UI 요소

스와이프

swipeable 수정자를 사용하여, 손을 떼면 한 방향으로 정의된 두 개 이상의 앵커 포인트를 향해 애니메이션 처리되는 요소를 드래그할 수 있습니다. 일반적인 용도는 '스와이프하여 닫기' 패턴을 구현하는 것입니다.

이 수정자는 요소를 이동하지 않으며 동작만 감지합니다. 예를 들어 offset 수정자를 통해 요소를 이동하여 상태를 유지하고 화면에 표시해야 합니다.

스와이프 가능 상태는 swipeable 수정자에 필요하며 rememberSwipeableState()를 사용하여 만들고 저장할 수 있습니다. 이 상태는 또한 프로그래매틱 방식으로 앵커에 애니메이션 처리하는 데 유용한 메서드(snapTo, animateTo, performFling, performDrag 참고) 및 드래그 진행 상태를 확인할 수 있는 속성을 제공합니다.

스와이프 동작은 FixedThreshold(Dp)FractionalThreshold(Float)와 같은 다양한 기준점 유형을 갖도록 구성할 수 있으며 앵커 포인트 시작-끝 조합마다 다를 수 있습니다.

더 유연하게 작동하도록 경계를 지나 스와이프할 때 resistance를 구성할 수 있으며 또한 위치 thresholds에 도달하지 않은 경우에도 스와이프를 다음 상태로 애니메이션 처리하는 velocityThreshold도 구성할 수 있습니다.

@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun SwipeableSample() {
    val width = 96.dp
    val squareSize = 48.dp

    val swipeableState = rememberSwipeableState(0)
    val sizePx = with(LocalDensity.current) { squareSize.toPx() }
    val anchors = mapOf(0f to 0, sizePx to 1) // Maps anchor points (in px) to states

    Box(
        modifier = Modifier
            .width(width)
            .swipeable(
                state = swipeableState,
                anchors = anchors,
                thresholds = { _, _ -> FractionalThreshold(0.3f) },
                orientation = Orientation.Horizontal
            )
            .background(Color.LightGray)
    ) {
        Box(
            Modifier
                .offset { IntOffset(swipeableState.offset.value.roundToInt(), 0) }
                .size(squareSize)
                .background(Color.DarkGray)
        )
    }
}

스와이프 동작에 응답하는 UI 요소