自定义View(三) Matrix的原理解析

版权声明:本文为博主石月的原创文章,转载请注明出处 https://blog.csdn.net/liuxingrong666/article/details/83722847

Matrix的英文是矩阵的意思,在Android中它起着坐标映射、变换的功能。意思就是说我们在自定义view的时候,有时需要对图表进行缩放、旋转、转移、错切等操作,就需要对图表的坐标进行一定的转换,此时就是Matrix在后台起着转换的作用。Matrix是一个三行三列的矩阵,大概长成下面那样子,如图:

我们先举个例子,为什么要用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=s1x、y=s2y。那么用矩阵表示:

通过矩阵的乘法规则,我们可以得到想要的结果,验证了上面Matrix中参数所表示的意义。意思就是在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 ;可以看到它们的相乘顺序是相反的,下面我们通过矩阵运算看看它们的结果是怎么样的:

前乘(pre)的计算如下:

后乘(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的列数

猜你喜欢

转载自blog.csdn.net/liuxingrong666/article/details/83722847
今日推荐