Matrix的中文是矩阵的意思,在Android中它起着坐标映射、变换的功能。意思就是说我们在自定义view的时候,有时需要对图表进行缩放、旋转、转移、错切等操作,就需要对图表的坐标进行一定的转换,此时就是Matrix在后台起着转换的作用。Matrix是一个3x3的矩阵,大概长成下面那样子,如图:
先举个例子,为什么要用Matrix进行坐标转换,比如通过Matrix对点A围绕原点旋转30°,我们只需要通过如下的操作:
Matrix matrix=new Matrix();
matrix.postRotate(30);
其实这个旋转操作本质上是点A在旋转前后的坐标值发生了改变而已,但是旋转30°以后的坐标值是不用我们去计算的,而是直接调用matrix的postRotate方法,matrix就已经帮我们算好了。至于它里面是怎么计算的,下面会讲到,这里只是想说明Matrix的作用,就是对坐标通过映射转换成我们需要的坐标值。
Matrix的基本变化有四种:translate(平移)、scale(缩放)、rotate(旋转)、skew(错切)。下面来看看这个几种变化分别是由哪些参数控制的:
由上面两个图表,可以清楚地看到这个3×3矩阵中,每个参数代表的意义,我们着重关注旋转、位移、缩放、错切这几个参数即可。下面就逐一分析这几个变换是怎么通过矩阵来实现的:
1.缩放(scale)
比如对一个图表在x和y轴分别缩放k1和k2倍,计算公式如下
x=x0 * k1、y=y0 * k2
等价于矩阵的如下表示:
可以看到缩放值k1和k2刚好在Matrix矩阵的对应位置,然后通过矩阵的乘法规则,就可以计算出缩放后坐标的x值和y值了,同时也验证了上面Matrix矩阵中MSACLE_X和MSCALE_Y参数所表示的意义。Matrix在内部是通过这样的矩阵算法去映射缩放前后的坐标值的。
2.旋转(rotate)
假如有一个点A(x0, y0),原来它跟X轴的夹角是α度,现在需要绕原点旋转θ 度, 旋转后为点 B(x, y),如下:
上面的纯数学计算方法,那么用Matrix如何表示呢,如下图:
图示如下:
3.平移(translate)
平移的坐标公式表示如下:
矩阵表示如下
4.错切(skew)
错切分水平错切、垂直错切、复合错切三种情形。
水平错切表示如下:
矩阵表示:
图示:
垂直错切:
矩阵表示:
图示:
复合错切:是上面两种错切的复合形式
矩阵表示:
图示:
上面分析Matrix的四种基本操作,我们了解它们的内部运作原理,本质上就是通过矩阵算法去映射得到坐标值。下面我们来看看Matrix的复合原理。
Matrix复合原理分析
我们在开发的过程中,往往都不是单一的操作,上面四种都是矩阵单一操作,那我们要进行复合操作的时候,就要通过矩阵复合操作了,常见的复合操作有三种,pre(前乘)、post(后乘)、set(设置),由于矩阵相乘不遵循乘法交换规则,所以关于前乘和后乘是有很大区别的。而set操作是直接覆盖掉前面的操作,重新开始。
下面我们通过例子来理解这几种复合操作:
下面出现的字母的意义解析一下,S代表缩放,T代表移动,M代表单位矩阵,R代表旋转,M'代表结果
pre(前乘):有如下伪代码
//首先构建一个单位矩阵
Matrix matrix=new Matrix();
//前乘旋转angle角度
matrix.preRotate(angle);
//前乘缩放scale
matrix.preScale(scale);
//前乘位移trans
matrix.preTranslate(trans);
就如上面的矩阵复合是怎么计算的呢?代码的执行顺序我们很明了,但是当前乘的时候,我们的矩阵是不断加在前面的,表示成这样:M' = M * R * S * T ;
后乘(post),有如下伪代码:
//首先构建一个单位矩阵
Matrix matrix=new Matrix();
//后乘旋转angle角度
matrix.postRotate(angle);
//后乘缩放scale
matrix.postScale(scale);
//后乘位移trans
matrix.postTranslate(trans);
我们的矩阵乘法是这样表示的:M' = T*S*R*M,即我们首先是一个单位矩阵M,然后R加在M的前面,S又加在R的前面,如此类推。由于矩阵不符合乘法交换法则,所以前乘和后乘的结果是一般都是不一样的。所以在开发的过程中,我们尽量使用一个乘法,要么是前乘要么是后乘,这样比较不容易混乱出错。下面我们通过一个例子来论证,前乘和后乘的区别,现有两个矩阵变换,代码如下:
//构建单位矩阵
Matrix matrix=new Matrix();
//前乘代码
matrix.preScale(0.5f,0.6f);
matrix.preTranslate(100,100);
//后乘代码
matrix.postScale(0.5f,0.6f);
matrix.postTranslate(100,100);
上面前乘用公式表示:M' = S * T ; 后乘公式表示:M' = T * S ;可以看到它们的相乘顺序是相反的,下面我们通过矩阵运算看看它们的结果是怎么样的:
后乘(post)的计算如下:
如此我们可以看到,它们的结果完全不一样,主要是因为矩阵是不符合乘法交换法则的,所以我们在运用复合操作的时候要特别注意这一点。
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 | 求逆矩阵、 是否为仿射矩阵、 是否为单位矩阵 ... |
关于矩阵的基本原理,我就先讲到这里,有更多的内容,可能会后续加入。
附:关于矩阵相乘的法则,比如C=AB,因为AB需要矩阵A的行依次乘以矩阵B的列,对应的元素相乘然后将它们的乘积相加。所以说矩阵A的列数和第矩阵B的行数一定要相等。并且两个矩阵的乘积C的行数为第一个矩阵A的行数,列数为第二个矩阵B的列数