Перетаскивание, пролистывание и броски

Модификатор draggable – это точка входа верхнего уровня для жестов перетаскивания в одной ориентации. Он сообщает расстояние перетаскивания в пикселях.

Обратите внимание, что этот модификатор похож на scrollable, поскольку он также только распознает жест. Вам нужно сохранить состояние и отобразить его на экране, например переместив элемент с помощью модификатора offset:

@Composable
private fun DraggableText() {
    var offsetX by remember { mutableFloatStateOf(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 { mutableFloatStateOf(0f) }
        var offsetY by remember { mutableFloatStateOf(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
                    }
                }
        )
    }
}

Элемент интерфейса, перетаскиваемый нажатием пальца.

Пролистывание

Модификатор swipeable позволяет перетаскивать элементы, которые при отпускании анимируются в направлении двух или более точек привязки, заданных в ориентации. Чаще всего этот метод используется для реализации функции "проведите, чтобы закрыть".

Обратите внимание, что этот модификатор не перемещает элемент, а только распознает жест. Вам нужно сохранить состояние и отобразить его на экране, например переместив элемент с помощью модификатора offset.

Состояние, в котором можно пролистывать, обязательно должно быть указано в модификаторе swipeable. Его можно создать и сохранить с помощью rememberSwipeableState(). Этот статус также предоставляет набор полезных методов для программной анимации к якорям (см. snapTo, animateTo, performFling и performDrag), а также свойства для отслеживания прогресса перетаскивания.

Для жеста пролистывания можно задать разные типы пороговых значений, например 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)
        )
    }
}

Элемент интерфейса, реагирующий на жест пролистывания