【问题标题】:Drag Image to the bounds of the Box - Jetpack Compose将图像拖动到框的边界 - Jetpack Compose
【发布时间】:2021-10-12 09:25:52
【问题描述】:

我正在尝试在 Jetpack Compose 中创建一个可缩放图像。我已启用放大/缩小功能,但我不确定如何设置 translationX 属性的限制,以便无法将图像水平移动到框边界之外?有什么解决办法吗?

例子:

@Composable
fun ZoomableImage(
    painter: Painter
) {
    val scale = remember { mutableStateOf(1f) }
    var offsetX by remember { mutableStateOf(0f) }
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(MaterialTheme.colors.welcomeScreenBackgroundColor)
            .pointerInput(Unit) {
                detectTransformGestures { centroid, pan, zoom, rotation ->
                    scale.value *= zoom
                }
            },
        contentAlignment = Alignment.Center
    ) {
        Image(
            modifier = Modifier
                .pointerInput(Unit) {
                    detectHorizontalDragGestures { change, dragAmount ->
                        offsetX += dragAmount
                    }
                }
                .graphicsLayer(
                    translationX = offsetX,
                    scaleX = maxOf(1f, minOf(3f, scale.value)),
                    scaleY = maxOf(1f, minOf(3f, scale.value))
                ),
            contentDescription = "Image",
            painter = painter,
            contentScale = ContentScale.Fit
        )
    }
}

【问题讨论】:

    标签: android kotlin android-jetpack-compose


    【解决方案1】:

    我不确定这是否是个好主意,但您可以使用onPlace

    还有一个 WIP 示例:

    @OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class)
    @Composable
    fun ZoomableImage(
        painter: Painter,
        contentDescription: String?,
        modifier: Modifier = Modifier,
        onLongPress: ((Offset) -> Unit)? = null,
        onTap: ((Offset) -> Unit)? = null
    ) {
        val scope = rememberCoroutineScope()
    
        var layout: LayoutCoordinates? = null
    
        var scale by remember { mutableStateOf(1f) }
        var translation by remember { mutableStateOf(Offset.Zero) }
        val transformableState = rememberTransformableState { zoomChange, panChange, _ ->
            scale *= zoomChange
            translation += panChange.times(scale)
        }
    
        Box(
            modifier = modifier
                .clipToBounds()
                .transformable(state = transformableState)
                .pointerInput(Unit) {
                    detectTapGestures(
                        onLongPress = onLongPress,
                        onDoubleTap = {
                            val maxScale = 2f
                            val midScale = 1.5f
                            val minScale = 1f
                            val targetScale = when {
                                scale >= minScale -> midScale
                                scale >= midScale -> maxScale
                                scale >= maxScale -> minScale
                                else -> minScale
                            }
                            scope.launch {
                                transformableState.animateZoomBy(targetScale / scale)
                            }
                        },
                        onTap = onTap
                    )
                }
                .pointerInput(Unit) {
                    forEachGesture {
                        awaitPointerEventScope {
                            val down = awaitFirstDown(requireUnconsumed = false)
                            drag(down.id) {
                                if (layout == null) return@drag
                                val maxX = layout!!.size.width * (scale - 1) / 2f
                                val maxY = layout!!.size.height * (scale - 1) / 2f
                                val targetTranslation = (it.positionChange() + translation)
                                if (targetTranslation.x > -maxX && targetTranslation.x < maxX &&
                                    targetTranslation.y > -maxY && targetTranslation.y < maxY
                                ) {
                                    translation = targetTranslation
                                    it.consumePositionChange()
                                }
                            }
                        }
                    }
                }
        ) {
            Image(
                painter = painter,
                contentDescription = contentDescription,
                modifier = Modifier
                    .matchParentSize()
                    .onPlaced { layout = it }
                    .graphicsLayer(
                        scaleX = scale,
                        scaleY = scale,
                        translationX = translation.x,
                        translationY = translation.y
                    ),
                contentScale = ContentScale.Fit
            )
    
            LaunchedEffect(transformableState.isTransformInProgress) {
                if (!transformableState.isTransformInProgress) {
                    if (scale < 1f) {
                        val originScale = scale
                        val originTranslation = translation
                        AnimationState(initialValue = 0f).animateTo(
                            1f,
                            SpringSpec(stiffness = Spring.StiffnessLow)
                        ) {
                            scale = originScale + (1 - originScale) * this.value
                            translation = originTranslation * (1 - this.value)
                        }
                    } else {
                        if (layout == null) return@LaunchedEffect
                        val maxX = layout!!.size.width * (scale - 1) / 2f
                        val maxY = layout!!.size.height * (scale - 1) / 2f
                        val target = Offset(
                            translation.x.coerceIn(-maxX, maxX),
                            translation.y.coerceIn(-maxY, maxY)
                        )
                        AnimationState(
                            typeConverter = Offset.VectorConverter,
                            initialValue = translation
                        ).animateTo(target, SpringSpec(stiffness = Spring.StiffnessLow)) {
                            translation = this.value
                        }
                    }
                }
            }
        }
    }
    

    【讨论】:

    • 即使在水平 ViewPager 中也能像魅力一样工作。应该是可接受的答案。
    猜你喜欢
    • 2023-01-21
    • 1970-01-01
    • 2012-06-18
    • 1970-01-01
    • 1970-01-01
    • 2018-09-22
    • 1970-01-01
    • 2022-09-29
    • 2021-04-15
    相关资源
    最近更新 更多