Android View之Matrix

(一)Matrix的概念

1、矩阵的相关概念:
① Matrix是一个矩阵(接近底层)。主要作用是坐标变换(齐次坐标)、数值转换。
② 仿射(矩阵)变换是线性变换后进行平移变换(也是齐次空间的线性变换),仿射变换以矩阵的形式表示具有等比性质的齐次坐标。


2、矩阵的4个变换:
平移(translate)、缩放(scale)、旋转(rotate) 和 错切(skew)。
其中每种操作在Matrix中都有3类:前乘(pre)M'=M*S、后乘(post)M'=S*M、而设置(set)直接覆盖掉原来的值(可能导致之前的操作失效)。

注意:
①、为避免出错,矩阵乘法尽量使用pre或者post其中的一种。
②、Matrix matrix = new Matrix();// 创建一个单位矩阵
③、Matrix的主要作用范围:Canvas、Path、Bitmap等。
④、(x, y, 1) - 点;(x, y, 0) - 向量 // 最后一个参数是标志位


3、矩阵的几个性质:
① 乘法不满足交换律但满足结合律。
② 矩阵与单位矩阵相乘结果不变:即A*I=A、I*A=A。

(二)Matrix的用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// 打印(3*3)矩阵数值
matrix.toShortString();
// 比较两个矩阵的数值是否相同
matrix1.equals(matrix2);
// 是否为仿射矩阵
matrix.isAffine();
// 是否为单位矩阵
matrix.isIdentity();
// 计算matrix1的逆矩阵inverse
matrix1.invert(Matrix inverse);


// 重置为单位矩阵
// ([1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0])
matrix.reset();
// 将matrix设置为src。若src为null, 则matrix重置为单位矩阵。
matrix.set(Matrix src);
// 将matrix的数值赋值到values数组。
matrix.getValues(float[] values);
// 重新赋值。注意matrix为3*3的矩阵,所以这里取的是values前9位(数值)长度。
matrix.setValues(float[] values);



/** 1、矩阵的坐标变换(重点) */

// 矩阵按照(sx, sy)进行缩放(覆盖掉原来的值)。
matrix.setScale(float sx, float sy);
// 矩阵按照(sx, sy)进行缩放(前乘)。
matrix.preScale(float sx, float sy);
// 矩阵按照(sx, sy)进行缩放(后乘)。
matrix.postScale(float sx, float sy);
//... 其余translate、rotate、skew同理 ...//



/** 2、矩阵的数值转换(重点) */

// 测量并转换一组点基于当前matrix变换(规则)后的位置(坐标)。
// 由于是一组点的坐标,所以pts的长度一般是偶数。
matrix.mapPoints(float[] pts);
// dst:变换后的坐标组,src:原坐标组不变。
matrix.mapPoints(float[] dst, float[] src);
// 指定只计算一部分数值:dstIndex为目标起点坐标索引、srcIndex为原坐标组起点索引。
matrix.mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex, int pointCount);


// mapVectors 与 mapPoints 基本上是相同的,唯一区别是mapVectors不受位移(xxTranslate)方法影响,即xxTranslate方法对mapVectors无效。
// 由于是一组点的坐标,所以vecs的长度一般是偶数。
matrix.mapVectors(float[] vecs);
// dst:变换后的坐标组,src:原坐标组不变。
matrix.mapVectors(float[] dst, float[] src);
// 指定只计算一部分数值:dstIndex为目标起点坐标索引、srcIndex为原坐标组起点索引。
matrix.mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, int vectorCount);


// 测量并转换矩形的位置
matrix.mapRect(RectF rectF);
// src:原矩形不变,dst:变换后的矩形。
matrix.mapRect(RectF dst, RectF src);

// 测量并转换圆的半径(转换后圆可能成为椭圆)
newRaduis = matrix.mapRadius(float radius);



/** 3、矩阵的高级用法(重点) */

// 设置多边形转多边形:设置指定的矩阵, src坐标(点)组会映射到指定的dst坐标(点)组。
// src:原坐标(点)组。
// srcIndex:原坐标点起始索引。
// dst:目标坐标(点)组。
// dstIndex:目标坐标点起始索引。
// pointCount:测控点的数量,取值范围是: [0..4]
matrix.setPolyToPoly(float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount);


// 设置矩形到矩形:将原矩形的内容填充到目标矩形中。
// src:原矩形
// dst:目标矩形
// stf:原矩形的填充方式(FILL、START、CENTER、END)
matrix.setRectToRect(RectF src, RectF dst, ScaleToFit stf);


// 判断矩形经过变换后是否仍为矩形
matrix.rectStaysRect();

// 设置sinCos值,这个是控制Matrix旋转的。
// 这个不常用,可用setRotate(float degrees)代替。
mMatrix.setSinCos(float sinValue, float cosValue);

(三)Matrix的实例

(1) 多边形 -> 多边形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/**
* Matrix -- setPolyToPoly()的使用(粗略模拟折叠效果)
* pointCount的取值说明:
* <p>
* 0:reset(重置)
* <p>
* 1、translate(平移)
* <p>
* 2、可以进行 缩放、旋转、平移 变换
* <p>
* 3、可以进行 缩放、旋转、平移、错切 变换
* <p>
* 4、任意形变(常用)
*
* @author [*昨日重现*] lhy_ycu@163.com
* @since version 1.0
*/
public class MatrixSetPolyToPolyView extends View {
private Bitmap mBitmap;
private Matrix mMatrix;

// 记录触摸的最后一个点坐标
private PointF mLastPoint;

public MatrixSetPolyToPolyView(Context context) {
this(context, null);
}

public MatrixSetPolyToPolyView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}

private void init(Context context) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;// 缩小2倍
mBitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_logo, options);

mMatrix = new Matrix();
mLastPoint = new PointF(mBitmap.getWidth(), mBitmap.getHeight());
}

@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
mLastPoint.x = Math.min(event.getX(), mBitmap.getWidth());// 只更新X坐标
// mLastPoint.y = Math.min(event.getY(), mBitmap.getHeight());
invalidate();
break;
}
return true;
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mMatrix.reset();

// 4个点(上左A、上右B、右下C、左下D)
float[] src = {
0, 0,
mBitmap.getWidth(), 0,
mBitmap.getWidth(), mBitmap.getHeight(),
0, mBitmap.getHeight()
};

float[] dst = {
0, 0,
mLastPoint.x, 0,
mLastPoint.x, mLastPoint.y,
0, mBitmap.getHeight()
};

// 设置图形上点的映射
mMatrix.setPolyToPoly(src, 0, dst, 0, 3);
canvas.drawBitmap(mBitmap, mMatrix, null);
}

}

(2) 矩形 -> 矩形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
* Matrix -- setRectToRect()的使用
* <p>
* 将原矩形的内容填充到目标矩形中
*
* @author [*昨日重现*] lhy_ycu@163.com
* @since version 1.0
*/
public class MatrixSetRectToRect extends View {
private Bitmap mBitmap;
private Matrix mMatrix;

// 绘制区域
private int mWidth;
private int mHeight;

public MatrixSetRectToRect(Context context) {
this(context, null);
}

public MatrixSetRectToRect(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}

private void init(Context context) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 3;// 缩小3倍
mBitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_logo, options);

mMatrix = new Matrix();
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
this.mWidth = w;
this.mHeight = h;
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mMatrix.set(null);// 相当于mMatrix.reset();

RectF src = new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
RectF dst = new RectF(0, 0, mWidth, mHeight);
// 将src的内容填充到dst的底部
mMatrix.setRectToRect(src, dst, Matrix.ScaleToFit.END);

canvas.drawBitmap(mBitmap, mMatrix, null);
}

}
Hawky wechat
欢迎订阅我的微信公众号
坚持原创技术分享,您的支持将鼓励我继续创作!

分享