拖动、滑动和轻甩

拖动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
                    }
                }
        )
    }
}

A UI element being dragged by a finger press

滑动

可滑动swipeable修饰符可让您拖动元素,这些元素在释放时会朝着通常是两个或更多在特定方向上定义的锚点动画。其常见用法是实现“滑动以关闭”模式。

需要注意的是,此修饰符不会移动元素,它仅检测手势。您需要持有状态并将其呈现在屏幕上,例如,通过 offset 修饰符移动元素。

可滑动状态在 swipeable 修饰符中是必需的,可以使用 rememberSwipeableState() 创建和记住此状态。此状态还提供了一组有用的方法,可用于以编程方式动画到锚点(请参阅 snapToanimateToperformFlingperformDrag),以及用于观察拖动进度的属性。

滑动手势可以配置为具有不同的阈值类型,例如 FixedThreshold(Dp)FractionalThreshold(Float),并且每个锚点从-到组合的阈值都可以不同。

为了获得更大的灵活性,您可以配置在滑动超出边界时的 resistance,以及 velocityThreshold,即使未达到位置 thresholds 也可将滑动动画到下一个状态。

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

A UI element responding to a swipe gesture