将图像拖至框的边界 - Jetpack Compose

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

我正在尝试在 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
3个回答
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
                    }
                }
            }
        }
    }
}

0
投票

如果您只需要限制图像内的平移而不是动画回到所需的级别,那就非常简单。使用可组合大小和缩放级别来约束它

val maxX = (size.width * (zoom - 1) / 2f)
val maxY = (size.height * (zoom - 1) / 2f)

offset = newOffset
offset = Offset(
    newOffset.x.coerceIn(-maxX, maxX),
    newOffset.y.coerceIn(-maxY, maxY)

-max, +max 是范围,当图形层的 TransformOrigin 为 (0.5,0.5) (默认值)时,您可以将其设置在 -max 到 +max 之间。更改 TransformOrigin 时请注意移动范围

全面实施

var zoom by remember { mutableStateOf(1f) }
var offset by remember { mutableStateOf(Offset.Zero) }
var size by remember { mutableStateOf(IntSize.Zero) }

修改器

val imageModifier = Modifier
        .fillMaxSize()
        .aspectRatio(4/3f)
        .clipToBounds()
        .pointerInput(Unit) {
            detectTransformGestures(
                onGesture = { _, gesturePan, gestureZoom, _ ->

                val newScale = (zoom * gestureZoom).coerceIn(1f, 3f)
                val newOffset = offset + gesturePan
                zoom = newScale


                val maxX = (size.width * (zoom - 1) / 2f)
                val maxY = (size.height * (zoom - 1) / 2f)

                offset = Offset(
                    newOffset.x.coerceIn(-maxX, maxX),
                    newOffset.y.coerceIn(-maxY, maxY)
                )
                }
            )
        }
        .onSizeChanged {
            size = it
        }
        .graphicsLayer {
            translationX = offset.x
            translationY = offset.y
            scaleX = zoom
            scaleY = zoom
        }

0
投票

我已经将图像缩放到适合高度,如何将图像水平居中(使用graphicsLayer)。你能帮助我吗?。谢谢你。

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