如何检测 Jetpack Compose 中变换手势的结束?

HRJ*_*HRJ 5 android android-jetpack-compose

我可以使用 很好地检测变换手势Modifier.detectTransformGesture(),如下面的简化示例所示:

Box(
  Modifier
    .pointerInput(Unit) {
        detectTransformGestures(
          onGesture = { _, pan, gestureZoom, gestureRotate ->
            // do something
          }
        )
    }
)
Run Code Online (Sandbox Code Playgroud)

但我想知道用户何时完成手势,以便我可以执行更多(计算密集型)操作。

我找不到任何线索。我尝试使用Modifier.transformableand TransformableStatewhich 确实有一个名为 的属性isTransformInProgress,但我不知道如何在回调中访问它:

val state = rememberTransformableState {
  // How do I access state.isTransformInProgress ?
}

// I can access it here
Text(if(state.isTransformInProgress) "transforming" else "not transforming")
Run Code Online (Sandbox Code Playgroud)

Dou*_*oug 6

如果使用时需要检查转换结束Modifier.transformable,可以isTransformInProgress使用LaunchedEffect

val state = rememberTransformableState {

}

LaunchedEffect(state.isTransformInProgress) {
    if (!state.isTransformInProgress) {
       // do what you need
    }
}
Run Code Online (Sandbox Code Playgroud)


Phi*_*hov 5

我可以想到两种变体,都是在研究源代码后得出的:

  1. 以相同的方式detectTransformGestures并行订阅事件:
fun Modifier.pointerInputDetectTransformGestures(
    panZoomLock: Boolean = false,
    isTransformInProgressChanged: (Boolean) -> Unit,
    onGesture: (centroid: Offset, pan: Offset, zoom: Float, rotation: Float) -> Unit
): Modifier {
    return pointerInput(Unit) {
        detectTransformGestures(
            panZoomLock = panZoomLock,
            onGesture = { offset, pan, gestureZoom, gestureRotate ->
                isTransformInProgressChanged(true)
                onGesture(offset, pan, gestureZoom, gestureRotate)
            }
        )
    }
        .pointerInput(Unit) {
            forEachGesture {
                awaitPointerEventScope {
                    awaitFirstDown(requireUnconsumed = false)
                    do {
                        val event = awaitPointerEvent()
                        val canceled = event.changes.any { it.consumed.positionChange }
                    } while (!canceled && event.changes.any { it.pressed })
                    isTransformInProgressChanged(false)
                }
            }
        }
}
Run Code Online (Sandbox Code Playgroud)
  1. 复制实现pointerInputDetectTransformGestures(它没有内部引用),并在那里添加所需的逻辑,如下所示:
suspend fun PointerInputScope.detectTransformGestures(
    panZoomLock: Boolean = false,
    onGesture: (centroid: Offset, pan: Offset, zoom: Float, rotation: Float) -> Unit,
    isTransformInProgressChanged: (Boolean) -> Unit,
) {
    forEachGesture {
        awaitPointerEventScope {
            var rotation = 0f
            var zoom = 1f
            var pan = Offset.Zero
            var pastTouchSlop = false
            val touchSlop = viewConfiguration.touchSlop
            var lockedToPanZoom = false
            var startGestureNotified = false // added

            awaitFirstDown(requireUnconsumed = false)
            do {
                val event = awaitPointerEvent()
                val canceled = event.changes.fastAny { it.positionChangeConsumed() }
                if (!canceled) {
                    val zoomChange = event.calculateZoom()
                    val rotationChange = event.calculateRotation()
                    val panChange = event.calculatePan()

                    if (!pastTouchSlop) {
                        zoom *= zoomChange
                        rotation += rotationChange
                        pan += panChange

                        val centroidSize = event.calculateCentroidSize(useCurrent = false)
                        val zoomMotion = abs(1 - zoom) * centroidSize
                        val rotationMotion = abs(rotation * PI.toFloat() * centroidSize / 180f)
                        val panMotion = pan.getDistance()

                        if (zoomMotion > touchSlop ||
                            rotationMotion > touchSlop ||
                            panMotion > touchSlop
                        ) {
                            pastTouchSlop = true
                            lockedToPanZoom = panZoomLock && rotationMotion < touchSlop
                        }
                    }

                    if (pastTouchSlop) {
                        val centroid = event.calculateCentroid(useCurrent = false)
                        val effectiveRotation = if (lockedToPanZoom) 0f else rotationChange
                        if (effectiveRotation != 0f ||
                            zoomChange != 1f ||
                            panChange != Offset.Zero
                        ) {
                            onGesture(centroid, panChange, zoomChange, effectiveRotation)
                            if (!startGestureNotified) { // notify first gesture sent
                                isTransformInProgressChanged(true) 
                                startGestureNotified = true
                            }
                        }
                        event.changes.fastForEach {
                            if (it.positionChanged()) {
                                it.consumeAllChanges()
                            }
                        }
                    }
                }
            } while (!canceled && event.changes.fastAny { it.pressed })
            isTransformInProgressChanged(false) // notify last finger is up
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

无论如何,您都必须检查新版本的 compose 中的任何更改才能更新您的代码(不知道它的可能性有多大)。

使用第二种方法至少您可以确定它不会崩溃,因为您不依赖于它们的实现。