Matrix 矩阵,在高等代数或高等数学中有介绍,在安卓方面主要用于图像处理,通过一系列的矩阵运算实现平移、旋转、缩放等操作。在android中Matrix是一个3行3列的方阵
\begin{bmatrix}
scale_x & skew_x & trans_x \\
skew_y & scale_y & trans_y \\
perep_0 & persp_1 & persp_2
\end{bmatrix}
代表了如下含义 scale是缩放,skew是错切,trans是平移,presp是透视,我们把这个方阵分为4个部分,左上角的2x2的方阵$\begin{pmatrix} scale_x & skew_x \\ skew_y &scale_y \end{pmatrix}$ 右上角2x1的矩阵$\begin{pmatrix} trans_x \\trans_y \end{pmatrix}$ 左下角1x2的矩阵$\begin{pmatrix} persp_0 &persp_1 \end{pmatrix}$ 右下角的1x1的方阵$\begin{pmatrix} persp_2 \end{pmatrix}$各大区域的作用我们稍后讲解
原理
手机屏幕上出了x轴y轴外,还有z轴代表物体离我们的远近,默认是1代表不缩放,用矩阵表示$\begin{pmatrix} x\\ y\\ 1\end{pmatrix}$
现在我们来看矩阵是如何起作用的下面以坐标旋转为例

不难看出
$x_0=rcosα$
$y_0=rsinα$
$x=rcos(α+θ)=rcosαcosθ-rsinαsinθ=x_0cosθ-y_0sinθ$
$y=rsin(α+θ)=rsinαcosθ+rcosαsinθ=y_0cosθ+x_0sinθ$
换成矩阵便是
$\begin{Bmatrix} x \\y \\1\end{Bmatrix}=\begin{Bmatrix} cosθ & -sinθ & 0 \\sinθ & cosθ & 0 \\0 & 0 & 1\end{Bmatrix}\begin{Bmatrix} x_0 \\y_0 \\1\end{Bmatrix}$
可以看出矩阵的4块切分也是有矩阵乘法运算决定的,由于左上角的四个值可以和x,y做乘法运算,所以可以影响到旋转操作,而右上角只能做加法,所以只影响到平移,右下角是管z轴的,自然就是进行缩放操作了,左下角一般不管它。
Matrix 基本操作方法
- 构造方法12public Matrix()public Matrix(Matrix src)
两个构造方法,第一个产生一个单位阵,第二个根据提供的矩阵创建有个矩阵
- isIdentity与isAffine12public boolean isIdentity() //是否是单位阵public boolean isAffine() //是否为放射阵
单位阵大家都知道,放射阵可能不大了解,首先来看什么是放射变化,仿射变换其实就是二维坐标到二维坐标的线性变换,可以通过一系列的原子变换的复合来实现,原子变换就包括:平移、缩放、翻转、旋转和错切,所以,只要最后一行是0,0,1则是仿射矩阵。
- rectStaysRect1public boolean rectStaysRect()
否可以将一个矩形依然变换为一个矩形,当矩阵是单位矩阵,或者只进行平移,缩放,以及旋转90度的倍数的时候,返回true。
- reset1public void reset()
重置矩阵为单位矩阵。
- setTranslate1public void setTranslate(float dx, float dy)
设置平移,参数分别是x,y上的平移量
- setScale12public void setScale(float sx, float sy, float px, float py)public void setScale(float sx, float sy)
设置缩放,sx,sy代表了缩放的倍数,px,py代表缩放的中心
- setRotate12public void setRotate(float degrees, float px, float py)public void setRotate(float degrees)
设置旋转,degrees旋转角度,px,py代表旋转的中心
- setSinCos12public void setSinCos(float sinValue, float cosValue, float px, float py)public void setSinCos(float sinValue, float cosValue)
咋一看比较懵逼,其实就是上面坐标旋转的例子,
$\begin{Bmatrix} x \\y \\1\end{Bmatrix}=\begin{Bmatrix} cosθ & -sinθ & 0 \\sinθ & cosθ & 0 \\0 & 0 & 1\end{Bmatrix}\begin{Bmatrix} x_0 \\y_0 \\1\end{Bmatrix}$
sinValue对应sin值,cosValue对应cos值,px,py为中心坐标
- setSkew12public void setSkew(float kx, float ky, float px, float py)public void setSkew(float kx, float ky)
错切,kx,ky分别代表了x,y上的错切因子,px,py代表了错切的中心
- setConcat1public boolean setConcat(Matrix a,Matrix b)
当前matrix的值能否变为a和b的乘积
11.setRectToRect
将rect变换成rec,ScaleToFit 有如下四个值
- FILL 可能会变换矩形的长宽比,保证变换和目标矩阵长宽一致
- START 保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。左上对齐
- CENTER 保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠
- END 保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。右下对齐
12.setPolyToPoly1public boolean setPolyToPoly(float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount)
通过指定的0-4个点,原始坐标以及变化后的坐标,来得到一个变换矩阵。如果指定0个点则没有效果
- 一个点——平移1234float[] src = {0, 0};int DX = 300;float[] dst = {0 + DX, 0 + DX};matrix.setPolyToPoly(src, 0, dst, 0, 1);
水平、垂直移动300像素
- 两个点——旋转或缩放12345int bw = bitmap.getWidth();int bh = bitmap.getHeight();float[] src = {bw / 2, bh / 2, bw, 0};float[] dst = {bw / 2, bh / 2, bw / 2 + bh / 2, bh / 2 + bw / 2};matrix.setPolyToPoly(src, 0, dst, 0, 2);

旋转90度
|
|

缩放为原来一半
- 三个点——错切123456Matrix matrix = new Matrix();int bw = bitmap.getWidth();int bh = bitmap.getHeight();float[] src = {0,0, 0, bh,bw,bh};float[] dst = {0, 0, 200, bh-100, bw + 200, bh-100};matrix.setPolyToPoly(src, 0, dst, 0, 3);

- 四个点——透视123456Matrix matrix = new Matrix();int bw = bitmap.getWidth();int bh = bitmap.getHeight();float[] src = {0, 0, 0, bh, bw, bh, bw, 0};float[] dst = {100, 100, 0, bh, bw, bh, bw - 100, 100};matrix.setPolyToPoly(src, 0, dst, 0, 4);

13.invert
如果能反转就返回true并将反转后的值写入inverse,否则返回false。当前矩阵*inverse=单位矩阵
反转之后,其实是对效果的一种反转
14.mapPoints
映射点的值到指定的数组中,这个方法可以在矩阵变换以后,给出指定点的值
dst:指定写入的数组
dstIndex:写入的起始索引,x,y两个坐标算作一对
src:指定要计算的点
srcIndex:要计算的点的索引
pointCount:需要计算的点的个数,每个点有两个值,x和y。
15.mapVectors
src中指定的矩形的左上角和右下角的两个点的坐标,写入dst中。
16.mapRect
17.mapRadius
返回一个圆圈半径的平均值,将matrix作用于一个指定radius半径的圆,随后返回的平均半径
进阶方法
上面有关变换的set方法设置后可以带来不同的效果,但是新的会覆盖掉以前的,也就是先后调用setTranslate、setScale,生效的是setScale,那么怎么才能同时生效呢。主要通过以下两种方法
- preXXX 前乘
- postXXX 后乘
我们知道矩阵是不满足交换律的,因此前乘与后乘差别很大的
举例说明1234Matrix matrix=new Matrix();matrix.setTranslate(100,100);matrix.preScale(0.5f,0.5f);System.out.print(matrix.toString());
可以看到结果是$\begin{matrix} 0.5 &0 &100 \\ 0 & 0.5 & 100 \\ 0 & 0 & 1 \end{matrix}$,其实是这么算的,前乘就代表Matrix在前面
$$\begin{vmatrix} 1 & 0 & 100 \\ 0 & 1 & 100 \\ 0 & 0 & 1 \end{vmatrix} \times \begin{vmatrix} 0.5 & 0 & 0 \\ 0 & 0.5 & 0 \\ 0 & 0 & 1 \end{vmatrix}=\begin{vmatrix} 0.5 &0 &100 \\ 0 & 0.5 & 100 \\ 0 & 0 & 1 \end{vmatrix}$$
那么后乘结果当然就不一样了,矩阵的位置发生了变化
$$\begin{vmatrix} 0.5 & 0 & 0 \\ 0 & 0.5 & 0 \\ 0 & 0 & 1 \end{vmatrix} \times \begin{vmatrix} 1 & 0 & 100 \\ 0 & 1 & 100 \\ 0 & 0 & 1 \end{vmatrix}=\begin{vmatrix} 0.5 &0 & 50 \\ 0 & 0.5 & 100 \\ 0 & 0 & 1 \end{vmatrix}$$
明显发觉水平平移少了一半,因此在使用过程中,要注意前乘与后乘的区别,不然可能达不到想要的效果。