如何在 compose 中的可组合项之间重定向触摸事件?

问题描述 投票:0回答:1

我正在尝试制作一个可缩放和平移(在某些条件下)并且可以在 LazyList 中滚动的可组合项。我尝试利用pointerInteropFilter。我首先等待 slop 来了解应该处理 touch LazyList 或转换的内容。然而,pointerInteropFilter 似乎没有做任何事情。

val MAX_ZOOM = 3f
val MIN_ZOOM = 1f
Box(Modifier.padding(innerPadding)) {
    LazyRow(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
        items(10) {

            var targetScale by remember { mutableStateOf(MIN_ZOOM) }
            var targetOffset by remember { mutableStateOf(Offset.Zero) }
            var slopReached by remember { mutableStateOf(false) }

            val transformableState =
                rememberTransformableState { zoomChange, offsetChange, _ ->
                    // until slope detected do nothing
                    if (!slopReached) return@rememberTransformableState
                    targetScale *= zoomChange
                    targetOffset = Offset(
                        targetOffset.x + offsetChange.x,
                        targetOffset.y + offsetChange.y
                    )

                }
            var slop by remember { mutableStateOf(Offset.Zero) }

            Box(Modifier.pointerInteropFilter() {
                // 2 fingers indicates it is zoom - must be handled by transformableState
                if (it.pointerCount >= 2) return@pointerInteropFilter false
                // zoomed image must be handled by transformableState
                if (targetScale != MIN_ZOOM) return@pointerInteropFilter false
                when (it.actionMasked) {
                    MotionEvent.ACTION_MOVE -> {
                        if (slopReached) {
                            // for unzoomed view
                            // horizontal gesture should be returned to LazyList
                            // vertical gesture should be handled by transformableState
                            return@pointerInteropFilter slop.x.absoluteValue > slop.y.absoluteValue

                        } else {
                            slop = Offset(
                                slop.x + it.x,
                                slop.y + it.y
                            )
                            slopReached =
                                slop.x.absoluteValue > 20 || slop.y.absoluteValue > 20
                            return@pointerInteropFilter false

                        }
                    }
                }
                true

            }) {
                Box(Modifier.transformable(transformableState)) {

                    Box(
                        Modifier
                            .background(Color.Red)
                            .graphicsLayer {
                                this.scaleX = targetScale
                                this.scaleY = targetScale
                                this.translationX = targetOffset.x
                                this.translationY = targetOffset.y
                            })
                }
            }
        }
    }
}
android-jetpack-compose touch-event
1个回答
0
投票

要在 LazyRow/Column 或任何可滚动内容中拥有可缩放/平移/旋转的可组合项,您需要有一个在特定条件下使用的转换手势。您可以编写一个像这个答案中那样的变换手势,并根据指针计数调用消费。

或者您可以编写一个不太复杂的触摸事件手势,例如

.pointerInput(Unit) {
    awaitEachGesture {
        // Wait for at least one pointer to press down
        awaitFirstDown()
        do {

            val event = awaitPointerEvent()
            // Calculate gestures and consume pointerInputChange
            var zoom = item.zoom.value
            zoom *= event.calculateZoom()
            // Limit zoom between 100% and 300%
            zoom = zoom.coerceIn(1f, 3f)

            item.zoom.value = zoom

            val pan = event.calculatePan()

            val currentOffset = if (zoom == 1f) {
                Offset.Zero
            } else {

                // This is for limiting pan inside Image bounds
                val temp = item.offset.value + pan.times(zoom)
                val maxX = (size.width * (zoom - 1) / 2f)
                val maxY = (size.height * (zoom - 1) / 2f)

                Offset(
                    temp.x.coerceIn(-maxX, maxX),
                    temp.y.coerceIn(-maxY, maxY)
                )
            }

            item.offset.value = currentOffset

            // When image is zoomed consume event and prevent scrolling
            if (zoom > 1f) {
                event.changes.forEach { pointerInputChange: PointerInputChange ->
                    pointerInputChange.consume()
                }

            }
        } while (event.changes.any { it.pressed })
    }
}

在此片段中没有 slopPass 检查,但您可以根据您的条件实现逻辑并调用pointerInputChang.consume()。可能是在缩放时,用户触摸了超过 1 个手指或按照您的逻辑倾斜。重要的是消耗可以防止其他滚动事件获得此手势。

© www.soinside.com 2019 - 2024. All rights reserved.