文章目录
- 前言
- 一.基础
- 1.1 Matrix
- 1.2 使用Matrix的准备知识
- 二.preXXX和postXXX
- 2.1 右乘和左乘
- 2.2 验证规律
- 三.坐标原点
- 结束
前言
Android绘制中最重要的要算Matrix类了,同时也是不太好理解的。以前也用过,但是掌握的也不是太好,刚好有时间好好的总结下。
一.基础
1.1 Matrix
Matrix既矩阵,数学里面很重要的工具,在安卓中用于做坐标转换,本篇博文主要记录一下Android中Matrix对于图片的preXXX,postXXX相关API的使用,对于矩阵的数学原理不做过多的深究。
Android关于Matrix最经典的一张图片:
那么对于上图的总结:
- 缩放(Scale)
对应 MSCALE_X 与 MSCALE_Y - 位移(Translate)
对应 MTRANS_X 与 MTRANS_Y - 错切(Skew)
对应 MSKEW_X 与 MSKEW_Y - 旋转(Rotate)
旋转没有专门的数值来计算,Matrix 会通过计算缩放与错切来处理旋转。
1.2 使用Matrix的准备知识
完全了解图片的坐标原点概念,对于使用Matrix非常重要,对这个概念,有以下两点总结:
- 所有的操作(旋转、平移、缩放、错切)默认都是以坐标原点为基准点的。
- 之前操作的坐标系状态会保留,并且影响到后续状态。
举个栗子:
canvas.drawBitmap(mBitmap, 0, 0 , null);
图片会在画布的左上角绘制,此时原点即为画布的左上角,也是图片自身的左上角。
红色箭头所指之处便是原点。
实际上坐标原点就是图片自身的左上角,对于这一点可以做个试验看一下:
试验代码:
private void drawMatrix(Canvas canvas){
// 原图
mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
// 从mBitmap中copy出来一张新的bitmap
Bitmap newBitmap = mBitmap.copy(Bitmap.Config.ARGB_8888, true);
// 居中位置
float px = (getWidth() - mBitmap.getWidth()) / 2.0f;
float py = (getHeight() - mBitmap.getHeight()) / 2.0f;
// 原图移动位置后再顺时针旋转90度
mLocationMatrix.preTranslate(px, py);
mLocationMatrix.preRotate(90);
// draw原图和新图
canvas.drawBitmap(mBitmap, mLocationMatrix, null);
// 新图居中显示
canvas.drawBitmap(newBitmap, px, py, null);
}
运行结果:
通过运行结果,可以看出中间的图片确实是绕着自身的左上角顺时针旋转了90度。
有了上面的概念,就可以继续学习Matrix的preXXX和postXXX相关API了。
二.preXXX和postXXX
2.1 右乘和左乘
对于我而言,学习Matrix最为头疼的是搞清楚诸如preTranslate和postTranslate,preRotate和postRotate等相关API的前后执行顺序关系。
想要知道这个顺序关系,需要知道涉及矩阵的数学运算,矩阵的右乘和左乘。
直接给出这个运算规律,你需要记住这个规则:
(重要)
// tips: 这里的M是原始矩阵,A是诸如平移,缩放等矩阵,M'是结果矩阵
preXXX : 右乘, M' = M*A (右乘是因为A在右边)
postXXX : 左乘, M' = A*M (左乘是因为A在左边)
令:T=平移,R=旋转,S=缩放
- 全是preXXX相关API:
matrix.preTranslate(px, py);
matrix.preRotate(90);
matrix.preScale(0.5f, 0.5f);
根据上面的规则,则可以写成:
M’ = M * T * R * S (右乘)
- 全是postXXX相关API:
matrix.postTranslate(px, py);
matrix.postRotate(90);
matrix.postScale(0.5f, 0.5f);
M’ = S * R * T * M (左乘)
- 混合操作:
matrix.postTranslate(px, py);
matrix.preRotate(90);
matrix.preScale(0.5f, 0.5f);
M’ = T * M * R * S
不过对于写代码,不建议混合使用,应该要么用前乘,要么用后乘。
那么知道这个有啥用呢?
当我们通过以上规则得到诸如M’ = M * T * R * S结果后,只要去掉M不看,再从左到右的顺序执行,这样的执行顺序就是使用postXXX和preXXX相关API的前后执行顺序。
所以执行的顺序:
- M’ = M * T * R * S的顺序就是先平移,再旋转,最后缩放。
- M’ = S * R * T * M的顺序就是先缩放,再旋转,最后平移。
2.2 验证规律
最后验证一下这个规律:
下面的这段代码是想将图片移动到canvas中间,并且还需要图片倒立展示。
mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
// 居中显示
float px = (getWidth() - mBitmap.getWidth()) / 2.0f;
float py = (getHeight() - mBitmap.getHeight()) / 2.0f;
// 先preRote
matrix.preRotate(180,
mBitmap.getWidth() / 2.0f,
mBitmap.getHeight() / 2.0f
);
// 后postTranslate
matrix.postTranslate(px, py);
canvas.drawBitmap(mBitmap, matrix, null);
上面的代码直观上给人的感觉是先旋转,再平移。实际上,根据上面的左乘和右乘的规律应该是:
M’ = T * M * R先平移到(px, py)的位置,最后按照图片中心点旋转180度。
运行结果:
成功的将图片居中并且倒立展示。
三.坐标原点
前面说了所有的操作(旋转、平移、缩放、错切)默认都是以坐标原点为基准点的。
下面来验证下:
// 相对图片中心点旋转180度
matrix.preRotate(180, mBitmap.getWidth() / 2.0f, mBitmap.getHeight() / 2.0f);
运行结果:
matrix在被初始化的时候实际上是一个单位矩阵
可以看到2和5位置上的值是0,这就意味着没有移动,那么图片的出生地就是canvas的左上角,也就是图片的左上角。旋转的时候是按照图片的中点去旋转角度的。
所以在写代码的时候,如果图片已经平移到canvas的其他位置,同时还要图片旋转一定角度,那么直接对于图片的宽高各自一半进行旋转即可。
类似以下代码:
matrix.preTranslate(px, py);
matrix.preRotate(180, mBitmap.getWidth() / 2.0f, mBitmap.getHeight() / 2.0f
);
结束
掌握了矩阵的左乘和右乘和坐标原点之后,写起和matrix相关的代码,将会变得非常轻松。祝大家学习进步,早日上岸!