目录
前言
在 Android 开发中,矩阵是一个功能强大并且应用广泛的神器,例如:用它来制作动画效果、改变图片大小、给图片加各类滤镜等。对于矩阵,Android 官方 SDK 为我们提供了一个强大的类 Matrix (还有 ColorMatrix )。由于在开发中接触矩阵的相关内容的机会较少,对其了解的也就很贫乏。现在有时间就记录下自己的理解
一、Matrix简介
Matrix中文是矩阵的意思。Matrix是Android SDK提供的一个矩阵类。官方文档对Matrix类的说明:
The Matrix class holds a 3x3 matrix for transforming coordinates.
大概意思是:Matrix类包含一个3x3矩阵,用于转换坐标。 Matrix类提供了让我们获得矩阵值的方法:toShortString()。代码:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Matrix matrix=new Matrix();
Log.i("TAG","matrix:"+matrix.toShortString());
}
日志结果:
可以看出Matrix初始值是一个单位矩阵。Matrix为这个数组中的每一个元素都定义了一个下标常量:
public static final int MSCALE_X = 0; //!< use with getValues/setValues
public static final int MSKEW_X = 1; //!< use with getValues/setValues
public static final int MTRANS_X = 2; //!< use with getValues/setValues
public static final int MSKEW_Y = 3; //!< use with getValues/setValues
public static final int MSCALE_Y = 4; //!< use with getValues/setValues
public static final int MTRANS_Y = 5; //!< use with getValues/setValues
public static final int MPERSP_0 = 6; //!< use with getValues/setValues
public static final int MPERSP_1 = 7; //!< use with getValues/setValues
public static final int MPERSP_2 = 8; //!< use with getValues/setValues
按照上面输出日志的结果和下标的对应关系,将它排列成矩阵的格式,如下:
Matrix 就是通过改变对应的矩阵元素的值来进行 Translate(平移)、Scale(缩放)、Rotate(旋转)、Skew(错切)操作的,。变换操作与矩阵中元素的对应关系如下:
Translate | 平移变换 | MTRANS_X, MTRANS_Y |
Rotate | 旋转变换 | MSCALE_X, MSCALE_Y , MSKEW_X , MSKEW_Y |
Scale | 缩放变换 | MSCALE_X, MSCALE_Y |
Skew | 错切变换 | MSKEW_X, MSKEW_Y |
从表中可以看出:只有最后一行的三个参数没有涉及到。其实它们是固定值,分别为0,0,1。为什么要这样?往下看。
二、Matrix的四种变换
Matrix提供了translate(平移)、scale(缩放)、rotate(旋转)、skew(错切)四种操作。由于所有的图形都是有像素点组成,因此我们只需要考察一个点相关变换即可。
ps:除平移变换(Translate)外,旋转变换(Rotate)、缩放变换(Scale)和错切变换(Skew)都可以围绕一个中心点来进行,如果不指定,在默认情况下是围绕(0, 0)来进行相应的变换的。
2.1、 Translate(平移)
将点 其移动到 ,如下图所示:
令,。则:
用矩阵表示为:
2.2、 scale(缩放)
理论上而言,对于一个像素点来说,不存在缩放的概念,但一个图像是由很多个像素点组成,将每个点的坐标进行相同比例的缩放后,整个图像也就有了缩放的效果。
矩阵表示为:
ps:k是要缩放的比例:负值无效,会不显示;0<k<1缩小;k>1放大
2.3、 skew(错切)
错切是一种比较特殊的线性变换,分为水平错切和垂直错切。错切变换的效果就是让所有点的x坐标(或者y坐标)保持不变,而对应的y坐标(或者x坐标)则按比例发生平移,且平移的大小和该点到x轴(或y轴)的垂直距离成正比。错切变换,属于等面积变换,即一个形状在错切变换的前后,其面积是相等的。
比如下图,各点的y坐标保持不变,但其x坐标则按比例发生了平移。这种情况将水平错切。
下图各点的x坐标保持不变,但其y坐标则按比例发生了平移。这种情况叫垂直错切。
假定一个点经过错切变换后得到,对于水平错切而言,应该有如下关系:
同理垂直错切:
复合错切:
矩阵表示为:
2.4、 旋转变换
假定有一个点 ,相对坐标原点顺时针旋转后的情形,同时假定P点离坐标原点的距离为r,如下图:
那么,
同样,用换成矩阵表示为:
2.5、小结
以上四种变换,矩阵形形式表示如下:
可以看出其实使用2x2的矩阵就可以完成四种变换了,那为什么Matix是3x3的矩阵呢?
从上面的矩阵表达式可以看出,平移是矩阵相加,旋转、缩放和错切则是矩阵相乘。能不能都用乘法来计算?,数学大神们为了统一算法引入了一样神器齐次坐标(自行百度),将平移的加法合并用乘法表示。所以,2 X 2 的矩阵经过一番变换后,如下。
所以,其实 2 X 2 的矩阵是足以表示的,不过是为了统一算法方便计算而合并写成了 3 X 3 的格式。这也就解释了为什么最后一行的三个值是固定的0,0,1。
三、setXXX、preXXX、postXXX
以上四种变换,针对每一种变换Matrix均提供pre,post和set三类操作。例如位移变换:setTranslate,preTranslate,postTranslate:官方文档对于preTranslate的说明,如下:
Preconcats the matrix with the specified translation. M' = M * T(dx, dy)
pre相当于矩阵的右乘。也叫后乘
postTranslate:
Postconcats the matrix with the specified translation. M' = T(dx, dy) * M
post相当于矩阵的左乘。也叫前乘
setTranslate:
Set the matrix to translate by (dx, dy).
直接设置矩阵的值覆盖之前的操作,会导致之前的操作失效。
在数学中矩阵乘法有几条规律:
- 任何矩阵与单位矩阵相乘都等于本身:M·A=A·M=A
- 不满足交换律:A·B≠B·A;
- 乘法结合律:(A·B)·C=A·(B·C);
在Matrix初始化后得到就是单位矩阵,即
我们将四种变换假定为T(平移)、Sc(缩放)、R(旋转)、Sk(错切)。由于setXXX很好理解这里不做说明。现在有一组变换
Matrix matrix = new Matrix();
matrix.preTranslate(100f, 100f);
matrix.postScale(0.5f, 0.5f);
matrix.preSkew(0.5f,0.5f);
matrix.postRotate(90);
matrix.postTranslate(-100f, -100f);
用矩阵表示就是:M′=T·R·Sc·M·T·Sk,带入坐标:
小结:
- 根据上述第一条规律,当只存在一种变换时,preXXX和post没有区别:Sc·M=M·Sc=Sc,(注意:M为单位矩阵)。此时上面的复合变换公式为:M′=T·R·Sc·T·Sk。
- 根据上述第二条规律,复合变换时preXXX和postXXX结果不同。
- 根据上述第三条规律,为了理解方便,从运算规则的角度可以改变计算顺序:
此时可以将整个复合变换看成是一个变换队列:postXXX就是往队列前排插入变换顺序靠后;preXXX是往队列后排插入变换顺序靠前。但是实际的代码执行顺序是不会变的。
ps:以上的小结只是个人理解,如果有错误的地方,后面会再修正。
总结
Matrix类中还存在很多其他的方法,可以列出一个方法表,如下:
方法类别 | 相关API | 摘要 |
---|---|---|
基本方法 | equals hashCode toString toShortString | 比较、 获取哈希值、 转换为字符串 |
数值操作 | set reset setValues getValues | 设置、 重置、 设置数值、 获取数值 |
数值计算 | mapPoints mapRadius mapRect mapVectors | 计算变换后的数值 |
设置(set) | setConcat setRotate setScale setSkew setTranslate | 设置变换 |
前乘(pre) | preConcat preRotate preScale preSkew preTranslate | 前乘变换 |
后乘(post) | postConcat postRotate postScale postSkew postTranslate | 后乘变换 |
特殊方法 | setPolyToPoly setRectToRect rectStaysRect setSinCos | 一些特殊操作 |
矩阵相关 | invert isAffine isIdentity | 求逆矩阵、 是否为仿射矩阵、 是否为单位矩阵 … |
在这篇文章中,作者已经有了详细的解释有兴趣的小伙伴们可以自己研究下。由于Google已经对与Matrix已经做了很好的封装,所以跳过这部分对实际开发影响并不会太大。但是对于Matrix的理解会使我们对自定view会更加的得心应手。
祝:工作顺利!
参考资料