Jetpack Compose:实现图片的 Pinch Zoom 从未如此简单

Jetpack Compose 实现图片的 Pinch Zoom 效果,代码非常简单,我们看一下:

kotlin 复制代码
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposePinchZoomRotateTheme {
                var scale by remember { mutableStateOf(1f) }
                var rotation by remember { mutableStateOf(1f) }
                var offset by remember { mutableStateOf(Offset.Zero) }

                BoxWithConstraints(
                    modifier = Modifier.fillMaxWidth().aspectRatio(1280f / 959f)
                ) {
                    val state = rememberTransformableState { zoomChange, panChange, rotationChange ->
                        scale = (scale * zoomChange).coerceIn(1f, 5f)
						rotation += rotationChange 
						
                        val extraWidth = (scale - 1) * constraints.maxWidth
                        val extraHeight = (scale - 1) * constraints.maxHeight

                        val maxX = extraWidth / 2
                        val maxY = extraHeight / 2

                        offset = Offset(
                            x = (offset.x + scale * panChange.x).coerceIn(-maxX, maxX),
                            y = (offset.y + scale * panChange.y).coerceIn(-maxY, maxY),
                        )
                    }
                    Image(
                        painter = painterResource(R.drawable.kermit),
                        contentDescription = null,
                        modifier = Modifier.fillMaxWidth()
                            .graphicsLayer {
                                scaleX = scale
                                scaleY = scale
                                rotationZ = rotation
                                translationX = offset.x
                                translationY = offset.y
                            }
                            .transformable(state)
                    )
                }
            }
        }
    }
}

同样的功能还可以使用 detectTransformGestures API 来实现,这里就不详细介绍了,具体可以参考我的 Jetpack Compose 专栏中的另一篇文章: Jetpack Compose中的手势操作和事件处理

下面尝试解释一下上面代码中 rememberTransformableState 的回调函数中所做的事情,为了简化,我们可以先将上面 rememberTransformableState 的回调代码写成下面这样:

kotlin 复制代码
val state = rememberTransformableState { zoomChange, panChange, rotationChange ->
	scale = (scale * zoomChange).coerceIn(1f, 5f)
	rotation += rotationChange 
	offset += panChange
}

这样其实就可以实现缩放、旋转、平移效果,只不过有一点点小瑕疵:

我们发现图片平移时周围会出现空白,所以 rememberTransformableState 回调函数中相比上面简化版本多余的代码就是为了处理这一点点瑕疵所做的修正工作,也就是下面代码:

kotlin 复制代码
val state = rememberTransformableState { zoomChange, panChange, rotationChange ->
	scale = (scale * zoomChange).coerceIn(1f, 5f)
	rotation += rotationChange 
	
	val extraWidth = (scale - 1) * constraints.maxWidth
	val extraHeight = (scale - 1) * constraints.maxHeight
	
	val maxX = extraWidth / 2
	val maxY = extraHeight / 2
	
	offset = Offset(
	    x = (offset.x + scale * panChange.x).coerceIn(-maxX, maxX),
	    y = (offset.y + scale * panChange.y).coerceIn(-maxY, maxY),
	)
}

我们先看下面这两行:

kotlin 复制代码
val extraWidth = (scale - 1) * constraints.maxWidth
val extraHeight = (scale - 1) * constraints.maxHeight

这两行是干嘛呢,请看图:

由于初始 scale1f,当我们放大之后,用 scale - 1f 去乘以 constraints.maxWidth 得到的extraWidth 其实就是放大后的矩形宽度比原始矩形宽度多出的部分,显然此时左右移动的时候,左边和右边不应该超出 extraWidth 的一半,因为再多一点的话就会看到空白区域了。对于高度也是同理。

因此剩余的代码就好理解了:

kotlin 复制代码
val maxX = extraWidth / 2
val maxY = extraHeight / 2

offset = Offset(
   x = (offset.x + scale * panChange.x).coerceIn(-maxX, maxX),
   y = (offset.y + scale * panChange.y).coerceIn(-maxY, maxY),
)

这里使用coerceIn限制了偏移量的移动范围,在往左边拖动的时候,不能超过左边的extraWidth 的一半,往右边拖动的时候,同样不能超过右边的extraWidth 的一半,上下拖同理。

Compose 实现图片的手势操作比传统 View 简便太多了,在传统 View 体系中,如果要对 ImageView 控件做同样的事情,那可要费老劲了。。往往我们需要自定义 View,然后还要封装成一个工具库来使用,例如下面列出两个开源库,你可以查看它们的源码的数量级来对比一下Jetpack Compose:

可见 Jetpack Compose 短短几十行代码就实现了传统 View 体系中动辄需要上千行的代码,简直不要太简洁,不仅清晰易懂,而且还易维护。Compose 的简洁性让我们可以有时间和精力挑战更加艰巨的任务。所以还没有用 Compose 的千万不要换 Compose ,因为换了你就会发现:TM再也不想用 View 控件来开发界面了😂

相关推荐
大熊猫侯佩6 个月前
变幻莫测:CoreData 中 Transformable 类型面面俱到(六)
类型转换·data·coredata·编码解码·nsobject·transformable·nssecurecoding
人间有清欢8 个月前
Android开发补充内容
android·okhttp·rxjava·retrofit·hilt·jetpack compose
wangz768 个月前
kotlin、jetpack compose、Android加速度传感器调用
android·kotlin·jetpack compose·加速度传感器
wangz768 个月前
kotlin,Android,血压记录程序
android·开发语言·kotlin·jetpack compose
wangz769 个月前
kotlin,Jetpack Compose使用Scaffold布局,包含底部导航栏
android·kotlin·jetpack compose·navigationbar
wangz769 个月前
kotlin,jetpack compose,使用DataStore保存数据,让程序下次启动时自动获取
android·kotlin·datastore·jetpack compose
wangz769 个月前
kotlin,jetpack compose 最简导航(navigation)案例学习
kotlin·navigation·jetpack compose
wangz7610 个月前
Android 下用kotlin写一个sqlite
android·sqlite·kotlin·jetpack compose
刘争Stanley1 年前
Jetpack Compose赋能:以速破局,高效打造非凡应用
android·ide·kotlin·敏捷流程·jetpack compose