原文 了解 Android Matrix 转换 https://www.zhangman523.cn/368.html
很多年前, 在学校我学习了矩阵. 我记不太清楚了, 但我记得的是在想,"但是...... 你对这些知识做了什么呢?"
快进几年, 我开始作为 Android 开发人员工作, 不得不使用 ImageView 的 scaleType - 如果你看过所有可能的类型, 你已经注意到其中一个是 matrix . 多年来, 我一直避开它, 使用其他规模类型或解决问题. 然而几周前我正在开发一种设计, 其中组件的背景图像应该与视图的左上角对齐, 而不执行任何缩放, 如下所示:
所以我看了一下 Matrix 文档:
Matrix 类包含一个 3x3 矩阵, 用于转换坐标
嗯...... 不是很有帮助. 幸运的是, 我并不是唯一一个不懂的人, Arnaud Bos https://twitter.com/arnaud_bos 写了一篇很棒的文章 , 详细解释了背后的数学 (警告: 如果你打算阅读它 - 可能需要一杯咖啡(或者两杯) 的时间). 如果你在那篇文章的中途迷路了, 我不能责怪你 - 这很复杂, 但好消息是你不必理解数学就能用 matrix(虽然它很有帮助.
我们怎样才能使用 Matrix?
正如我之前提到的, 我们必须在 ImageView 上设置 scaleType="matrix" . 但要真正能够使用它, 我们必须在代码中设置 imageMatrix :
- imageView.imageMatrix = Matrix().apply {
- // perform transformations
- }
现在我们有了这个 - 我们可以用它做什么? 矩阵支持一系列不同的变换, 如 translate , scale , rotate 和 skew . 如果这些听起来很熟悉, 因为它们 (大多数) 与 View, 动画或画布上的相同.
您会发现, 对于每个操作, 都有一 set , pre 版本. 稍后我会谈到这一点, 但是现在我们只使用 set 版本.
所以, 我们能做些什么?
Translating (移动)
设置 translation 意味着将图像移动到其他位置. 您所要做的就是使用 Matrix 上所需的 x 和 y 坐标调用 setTranslate :
- val dWidth = imageView.drawable.intrinsicWidth
- val dHeight = imageView.drawable.intrinsicHeight
- val vWidth = imageView.measuredWidth
- val vHeight = imageView.measuredHeight
- setTranslate(
- round((vWidth - dWidth) * 0.5f),
- round((vHeight - dHeight) * 0.5f)
- )
在这个例子中, 我们只是将 drawable 置于 View 中心, 这导致与在 ImageView 上设置 scaleType="center" 相同的行为. 那么让我们来看看 ImageView 如何做到这一点:
- mDrawMatrix.setTranslate(Math.round((vwidth - dwidth) * 0.5f),
- Math.round((vheight - dheight) * 0.5f));
它完全一样! 所以我们不知道它已经使用了矩阵变换.
Scaling (缩放)
Scaling(正如您可能已经猜到的那样)定义了图像的大小. 您可以定义两个值 - 一个用于 x 轴, 另一个用于 y 轴. 但是, 通过缩放, 您还可以设置轴心点.
枢轴点定义转换将保持不变的点. 默认情况下它是 0, 0 - 左上角 - 意味着图像将向右和向下延伸, 左上角保持不变 - 就像左边的上面的 gif 一样.
如果要从中心缩放图像(如右侧的 gif), 可以将轴设置为图像的中心, 如下所示:
setScale(0.5f, 0.5f, dWidth / 2f, dHeight / 2f)
这将使图像缩放到其大小的一半, 其中心的枢轴点. 如果你只想从左上角缩放它, 你可以省略最后两个参数, 如下所示:
setScale(0.5f, 0.5f)
但是你可以用缩放做更多的事情. 如果提供负比例值, 则基本上可以围绕轴 (或两个) 镜像图像. 相当漂亮!
Rotation (旋转)
你猜对了! 通过旋转, 您可以旋转图像. 在这里, 我们提供了我们想要旋转的角度, 以及一个类似于比例的可选枢轴点.
setRotate(45f,dWidth / 2f,dHeight / 2f)
将图像围绕图像中心旋转 45 度. 如果将它旋转 - 45 度, 它将向左旋转.
Skewing (倾斜)
倾斜可能是你以前没有听说过的转变. 倾斜将沿轴 (或两个) 拉伸您的图像, 就像上面的 GIF 一样. 我们来看一个例子:
setSkew(1f,0f,dWidth / 2f,dHeight / 2f)
这会使图像在 x 轴 (以及中心点周围) 偏斜 1, 这是图像的宽度, 导致图像倾斜 45 度, 就像上面的 gif 一样.
应用多个转换
我们现在可以 translate,scale,rotate 和 skew 图像, 但是如果我们想要将它们组合起来呢? 显而易见的事情可能是连续调用多个 set 方法. 但是, 这只会应用最后一次转换 - 所有以前的转换都将被覆盖. 这是因为 set 方法基本上重置了 Matrix.
但正如我之前提到的, 还有每个转换的 post 版本. 通过使用这些, 我们可以应用多个变换, 并真正利用 matrix 的魔力.
但是 pre 的区别是什么? 对于第一次转换, 使用三种版本中的哪一种没有区别, 但对于任何未来的转换, 它可以产生很大的不同.
假设我们想要将图像转换为视图的中心并将其缩放到一半大小. 这两个版本将产生预期的效果:
- val drawableLeft = round((vWidth - dWidth) * 0.5f)
- val drawableTop = round((vHeight - dHeight) * 0.5f)
- // Version 1
- setTranslate(drawableLeft, drawableTop)
- val (viewCenterX, viewCenterY) = vWidth / 2f to vHeight / 2f
- postScale(0.5f, 0.5f, viewCenterX, viewCenterY)
- // Version 2
- setTranslate(drawableLeft, drawableTop)
- val (drawableCenterX, drawableCenterY) = dWidth / 2f to dHeight / 2f
- preScale(0.5f, 0.5f, drawableCenterX, drawableCenterY)
请注意, 在第一个版本中, 我们使用 postScale 和视图的中心, 而在第二个版本中, 我们使用 preScale 和 drawable 的中心.
使用 postScale , 将在 translate 后应用比例转换. 由于图像已经在视图中居中, 我们必须使用视图的中心点作为枢轴.
- val (viewCenterX, viewCenterY) = vWidth / 2f to vHeight / 2f
- postScale(0.5f, 0.5f, viewCenterX, viewCenterY)
所以从头开始回顾这个例子 - 为什么应用 scaleType="matrix" 只是起作用? 使用默认 martix, 比例将为 1, 平移, 旋转和倾斜将为 0, 因此图像将在左上角绘制. 所以它完全符合我的需要!
下次您必须以默认 scaleType 不起作用的方式布局图像时 - 尝试使用 Matrix!
来源: https://juejin.im/post/5c0a24ea5188257abf1d4d71