You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Jetpack Compose中实现图片点击切换缩放状态的动画?

Jetpack Compose 实现点击缩放图片动画(匹配Android官方效果)

核心实现思路

通过Compose的状态管理、动画API和手势处理,实现点击图片时以点击点为中心平滑放大,再次点击恢复原始尺寸的效果,完全匹配Android官方展示的缩放动效:

  • 用状态变量记录缩放状态
  • 捕捉点击位置作为缩放锚点
  • 结合animateContentSize和graphicsLayer实现尺寸与缩放的同步动画

完整代码实现

import androidx.compose.animation.animateContentSize
import androidx.compose.animation.spring
import androidx.compose.foundation.Image
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.IntSize

@Composable
fun ZoomableImage(
    imageResId: Int,
    initialSize: Int,
    zoomedSize: Int,
    modifier: Modifier = Modifier
) {
    // 记录缩放状态
    var isZoomed by remember { mutableStateOf(false) }
    // 记录图片自身尺寸
    var imageSize by remember { mutableStateOf(IntSize.Zero) }
    // 记录点击位置相对于图片的坐标
    var tapOffset by remember { mutableStateOf(Offset.Zero) }

    Box(
        modifier = modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        Image(
            painter = painterResource(id = imageResId),
            contentDescription = "可缩放图片",
            modifier = Modifier
                // 设置当前尺寸
                .size(if (isZoomed) zoomedSize else initialSize)
                // 尺寸过渡动画,使用spring实现弹性效果
                .animateContentSize(
                    animationSpec = spring(
                        dampingRatio = 0.8f,
                        stiffness = 300f
                    )
                )
                // 获取图片实际尺寸
                .onGloballyPositioned { coordinates ->
                    imageSize = coordinates.size
                }
                // 控制缩放锚点和缩放比例
                .graphicsLayer {
                    if (isZoomed) {
                        // 计算点击位置的相对比例,作为缩放原点
                        val xRatio = tapOffset.x / imageSize.width
                        val yRatio = tapOffset.y / imageSize.height
                        transformOrigin = androidx.compose.ui.graphics.TransformOrigin(xRatio, yRatio)
                        // 计算缩放比例
                        val scaleFactor = zoomedSize.toFloat() / initialSize.toFloat()
                        scaleX = scaleFactor
                        scaleY = scaleFactor
                    } else {
                        // 恢复原始缩放比例
                        scaleX = 1f
                        scaleY = 1f
                    }
                }
                // 捕捉点击手势,切换缩放状态并记录点击位置
                .detectTapGestures { offset ->
                    tapOffset = offset
                    isZoomed = !isZoomed
                }
        )
    }
}

// 调用示例
@Composable
fun ZoomableImageDemo() {
    ZoomableImage(
        imageResId = R.drawable.your_image,
        initialSize = 200,
        zoomedSize = 400
    )
}

关键细节说明

  • 锚点控制:通过graphicsLayer的transformOrigin设置缩放原点为点击位置的相对比例,确保放大/缩小始终围绕点击点进行,和官方效果一致
  • 动画参数:使用spring动画配置dampingRatio和stiffness,模拟官方的弹性过渡效果,避免生硬的线性动画
  • 状态同步:animateContentSize负责尺寸的平滑变化,graphicsLayer的缩放比例同步跟进,保证动画的一致性
  • 适配扩展:如果需要全屏缩放,可将zoomedSize替换为父布局的可用尺寸,或修改Modifier.size为fillMaxSize并调整缩放比例计算逻辑

内容的提问来源于stack exchange,提问作者Fawzi Abd-Elaal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 22:53:09