Android Matrix矩阵

Matrix

Matrix是一个矩阵,主要功能是坐标映射,数值转换,在View,图片,动画效果等各个方面均有运用。
基本变换有4种:

  • 平移(Translate)
  • 缩放(Scale)
  • 旋转(Rotate)
  • 错切(Skew)

矩阵运算

矩阵加减法:

就是相同位置的数字相加


矩阵相加.png

矩阵减法也类似

矩阵乘以一个常数

就是所有位置都乘以这个数:

矩阵乘常数.png

矩阵乘以矩阵

矩阵乘以矩阵.png

计算规则是,第一个矩阵第一行的每个数字(2和1),各自乘以第二个矩阵第一列对应位置的数字(1和1),然后将乘积相加( 2 x 1 + 1 x 1),得到结果矩阵左上角的那个值3。也就是说,结果矩阵第m行与第n列交叉位置的那个值,等于第一个矩阵第m行与第二个矩阵第n列,对应位置的每个值的乘积之和

矩阵的本质就是线性方程式,两者是一一对应关系

线性方程式.png

矩阵的最初目的,只是为线性方程组提供一个简写形式。


矩阵.png

四种变换

1、缩放(Scale)

缩放.png

2.错切(Skew)

错切.png

3.旋转(Rotate)

假定一个点 A(x0, y0) ,距离原点距离为 r, 与水平轴夹角为 α 度, 绕原点旋转 θ 度, 旋转后为点 B(x, y) 如下:

旋转.png

4.平移(Translate)

平移.png

Matrix复合原理

常用的四大变换操作,每一种操作在Matrix均有三类,前乘(pre),后乘(post)和设置(set)
前乘(pre):前乘相当于矩阵的右乘
后乘(post):前乘相当于矩阵的左乘
设置(set):设置使用的不是矩阵乘法,而是直接覆盖掉原来的数值
例如:

Matrix m = new Matrix();
m.reset();
m.preTranslate(tx, ty);
m.preScale(sx, sy);
左乘.png
Matrix m = new Matrix();
m.reset();
m.postScale(sx, sy); 
m.postTranslate(tx, ty);
右乘.png

pre 和 post 就是右乘或者左乘的区别,pre 和 post 不能影响程序执行顺序,而程序每执行一条语句都会得出一个确定的结果,从实际上来说,由于矩阵乘法满足结合律,所以不论你说是靠右先执行还是靠左先执行,从结果上来说都没有错

Matrix方法

1.Matrix ()

2.Matrix (Matrix src)

3.void set (Matrix src)

4.void reset ()
重置当前Matrix(将当前Matrix重置为单位矩阵)。

5.void setValues (float[] values)
setValues的参数是浮点型的一维数组,长度需要大于9,拷贝数组中的前9位数值赋值给当前Matrix。

6.void getValues (float[] values)
getValues和setValues是一对方法,参数也是浮点型的一维数组,长度需要大于9,将Matrix中的数值拷贝进参数的前9位中。

7.boolean invert (Matrix inverse)
求矩阵的逆矩阵,简而言之就是计算与之前相反的矩阵,如果之前是平移200px,则求的矩阵为反向平移200px,如果之前是缩小到0.5f,则结果是放大到2倍

8.isAffine
判断矩阵是否是仿射矩阵, 基本上这个一直是true,因为我们所使用的变换基本上都是放射变换。

9.isIdentity
判断是否为单位矩阵,什么是单位矩阵呢,新创建的Matrix和重置后的Matrix都是单位矩阵,不过,只要随意操作一步,就不在是单位矩阵了。

10.setConcat
Matrix类还提供了直接矩阵计算方式。Matrix a=new Matrix()相当于创建一个单位矩阵。

  • a.set(b),就是赋值a = b;
  • a.preConCat(b),相当于前乘,即 a=a×b;
  • a.postConCat(b),相当于前乘,即 a=b×a;
  • c.setConcat(a,b),相当于c=a×b;

11.void setTranslate(float dx, float dy)
设置平移效果,参数分别是x,y上的平移量。

12.void setScale(float sx, float sy, float px, float py)
void setScale(float sx, float sy)

两个方法都是设置缩放到matrix中,sx,sy代表了缩放的倍数,px,py代表缩放的中心。

13.void setRotate(float degrees, float px, float py)
void setRotate(float degrees)
和上面类似

14.void setSkew(float kx, float ky, float px, float py)
void setSkew(float kx, float ky)
错切,这里kx,ky分别代表了x,y上的错切因子,px,py代表了错切的中心

15.mapPoints
计算一组点基于当前Matrix变换后的位置,(由于是计算点,所以参数中的float数组长度一般都是偶数的,若为奇数,则最后一个数值不参与计算)

(1) void mapPoints (float[] pts)
void mapPoints (float[] pts) 方法仅有一个参数,pts数组作为参数传递原始数值,计算结果仍存放在pts中

// 初始数据为三个点 (0, 0) (80, 100) (400, 300) 
float[] pts = new float[]{0, 0, 80, 100, 400, 300};

// 构造一个matrix,x坐标缩放0.5
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 1f);

// 输出pts计算之前数据
Log.i(TAG, "before: "+ Arrays.toString(pts));

// 调用map方法计算
matrix.mapPoints(pts);

// 输出pts计算之后数据
Log.i(TAG, "after : "+ Arrays.toString(pts));
before: [0.0, 0.0, 80.0, 100.0, 400.0, 300.0]
after : [0.0, 0.0, 40.0, 100.0, 200.0, 300.0]

(2)void mapPoints (float[] dst, float[] src)

(3)void mapPoints (float[] dst, int dstIndex,float[] src, int srcIndex, int pointCount)

16.float mapRadius (float radius)
测量半径,由于圆可能会因为画布变换变成椭圆,所以此处测量的是平均半径。

float radius = 100;
float result = 0;

// 构造一个matrix,x坐标缩放0.5
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 1f);

Log.i(TAG, "mapRadius: "+radius);

result = matrix.mapRadius(radius);

Log.i(TAG, "mapRadius: "+result);

17.boolean mapRect (RectF rect)

boolean mapRect (RectF dst, RectF src)
测量矩形变换后位置。

RectF rect = new RectF(400, 400, 1000, 800);

// 构造一个matrix
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 1f);
matrix.postSkew(1,0);

Log.i(TAG, "mapRadius: "+rect.toString());

boolean result = matrix.mapRect(rect);

Log.i(TAG, "mapRadius: "+rect.toString());
Log.e(TAG, "isRect: "+ result);

18.mapVectors
测量向量。
mapVectorsmapPoints基本上是相同的,可以直接参照上面的mapPoints使用方法。
而两者唯一的区别就是 mapVectors 不会受到位移的影响,由于向量的平移前后是相等的,这符合向量的定律

float[] src = new float[]{1000, 800};
float[] dst = new float[2];

// 构造一个matrix
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 1f);
matrix.postTranslate(100,100);

// 计算向量, 不受位移影响
matrix.mapVectors(dst, src);
Log.i(TAG, "mapVectors: "+Arrays.toString(dst));

// 计算点
matrix.mapPoints(dst, src);
Log.i(TAG, "mapPoints: "+Arrays.toString(dst));

//打印:
mapVectors: [500.0, 800.0]
mapPoints: [600.0, 900.0]

19.setPolyToPoly

boolean setPolyToPoly (
        float[] src,    // 原始数组 src [x,y],存储内容为一组点
        int srcIndex,   // 原始数组开始位置
        float[] dst,    // 目标数组 dst [x,y],存储内容为一组点
        int dstIndex,   // 目标数组开始位置
        int pointCount) // 测控点的数量 取值范围是: 0到4

pointCount的意义
0 相当于reset
1 相当于translate
2 可以进行 缩放、旋转、平移 变换
3 可以进行 缩放、旋转、平移、错切 变换
4 可以进行 缩放、旋转、平移、错切以及任何形变

20.setRectToRect

boolean setRectToRect (RectF src,           // 源区域
                RectF dst,                  // 目标区域
                Matrix.ScaleToFit stf)      // 缩放适配模式

将源矩形的内容填充到目标矩形中,然而在大多数的情况下,源矩形和目标矩形的长宽比是不一致的,到底该如何填充呢,这个填充的模式就由第三个参数 stf 来确定
ScaleToFit 是一个枚举类型,共包含了四种模式

CENTER 居中,对src等比例缩放,并最大限度的填充变换后的矩形,将其居中放置在dst中。
START 顶部,对src等比例缩放,并最大限度的填充变换后的矩形,将其放置在dst的左上角。左上对齐
END 底部,对src等比例缩放,并最大限度的填充变换后的矩形,将其放置在dst的右下角。 右下对齐
FILL 充满,拉伸src的宽和高,使其完全填充满dst。

21.rectStaysRect
判断矩形经过变换后是否仍为矩形

Matrix实用技巧

1.获取View在屏幕上的绝对位置

@Override
protected void onDraw(Canvas canvas) {
    float[] values = new float[9];
    int[] location1 = new int[2];

    Matrix matrix = canvas.getMatrix();
    matrix.getValues(values);

    location1[0] = (int) values[2];
    location1[1] = (int) values[5];
    Log.i(TAG, "location1 = " + Arrays.toString(location1));

    int[] location2 = new int[2];
    this.getLocationOnScreen(location2);
    Log.i(TAG, "location2 = " + Arrays.toString(location2));
}

2.利用setPolyToPoly制造3D效果

推荐阅读更多精彩内容

  • CSDN博客 img cquwentao android matrix 最全方法详解与进阶(完整篇) 发表于201...
    北风知我意阅读 4,208评论 0 0
  • 效果图: Github链接:https://github.com/boycy815/PinchImageView ...
    CQ_TYL阅读 1,944评论 0 0
  • 一、矩阵的定义 二、矩阵与矩阵的乘法 矩阵的乘法满足以下运算律:结合律,分配律,但是矩阵乘法不满足交换律。更详细的...
    Calllanna阅读 1,915评论 0 1
  • 这应该是目前最详细的一篇讲解Matrix的中文文章了,在上一篇文章Matrix原理中,我们对Matrix做了一个简...
    吕侯爷阅读 1,431评论 1 12
  • Matrix主要用于对图像的图形处理。前面学习的ColorMatirx主要是图像色彩的处理 学习资料 Androi...
    英勇青铜5阅读 9,802评论 8 48
  • 花不知何时会开, 雨不知何时会停, 你不知何时会来。
    弄花鱼沉阅读 118评论 0 0
  • 在我的设想里,在多瑞亚斯时,Oropher和 Amdír是朋友,年轻的Thranduil和Amroth是玩伴,家国...
    V匿匿阅读 102评论 0 0
  • 早上7点半出发武汉,预约达美陈医生复查。情况理想,上午去舵落口会见曹总,商量合作事宜。得知李波公司在附近,便去他公...
    顾国胜阅读 123评论 0 0
  • 文/胡妍 东经98°05′-98°45′、北纬24°38′-25°52′,脉络状的经纬网框住的这个小小的城市,亿万...
    Olivia主编君阅读 127评论 0 0
  • 过一种野蛮的生活不好吗?
    兴尽而返阅读 63评论 0 0