Jetpack Compose: Detecting 2-finger swipe or drag gesture

1.1k Views Asked by At

I don't even know where to begin, nothing similar was ever implemented in Jetpack Compose. How can I make a composable detect 2-finger swipe (or drag) gesture ? Preferably (but not necessarily) without consuming them

2

There are 2 best solutions below

0
On BEST ANSWER

I came up with a solution that works consistently. I had to edit the source code for the detectVerticalDragGestures to count the pointers being used, then return the count as parameter in 'onVerticalDrag' like this:

suspend fun PointerInputScope.detectAdvancedVerticalDragGestures(
    onDragStart: (Offset) -> Unit = { },
    onDragEnd: () -> Unit = { },
    onVerticalDrag: (event: PointerEvent, change: PointerInputChange, dragAmount: Float, pointerCount: Int) -> Unit
) {
    awaitEachGesture {
        val down = awaitFirstDown(requireUnconsumed = false)

        var overSlop = 0f
        val drag = awaitVerticalPointerSlopOrCancellation(down.id, down.type) { change, over ->
            change.consume()
            overSlop = over
        }

        if (drag != null) {
            onDragStart.invoke(drag.position)


            //Here's the block of code I added to return count
            //By the way, IM not sure which pointer returns the drag amount
            //but i think it returns the drag amount for the pointer
            //that had the first down event (that touched the screen first)
            val changes = currentEvent.changes.takeLast(2)
            val count: Int = if (changes.size == 1) {
                1
            } else if (changes.size == 2) {
                if (changes.last().id != changes.first().id) {
                    2
                } else {
                    1
                }
            } else {
                0
            }

            onVerticalDrag.invoke(currentEvent, drag, overSlop, count)
            if (verticalDrag(drag.id) {
                    onVerticalDrag(currentEvent, it, it.positionChange().y, count)
                    it.consume()
                }
            ) {
                onDragEnd()
            }
        }
    }
}

This should also work for detectDragGestures or detectHorizontalDragGestures if you edit the same way I did. Hope it helped.

1
On

You can achieve multitouch by using transformable modifier on your composable. Something like this:

@Composable
fun TransformableSample() {
    // set up all transformation states
    var scale by remember { mutableStateOf(1f) }
    var rotation by remember { mutableStateOf(0f) }
    var offset by remember { mutableStateOf(Offset.Zero) }
    val state = rememberTransformableState { zoomChange, offsetChange, rotationChange ->
        scale *= zoomChange
        rotation += rotationChange
        offset += offsetChange
}
    Box(
        Modifier
            // apply other transformations like rotation and zoom
            // on the pizza slice emoji
            .graphicsLayer(
                scaleX = scale,
                scaleY = scale,
                rotationZ = rotation,
                translationX = offset.x,
                translationY = offset.y
            )
            // add transformable to listen to multitouch transformation events
            // after offset
            .transformable(state = state)
            .background(Color.Blue)
            .fillMaxSize()
    )
}

More info on Compose gestures can be found here.