拖曳、滑動及快速滑過

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 修飾符需要 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)
        )
    }
}

回應滑動手勢的 UI 元素