Android自定义View——Matrix学习笔记

Posted by Csming on 2017-03-14

Matrix是什么

Matrix是一个矩阵,用于坐标映射,数制转换

  • 可以运用于View、图片、动画效果等各个方面
  • 画布操作是对Matrix的封装;Matrix更接近底层

基本原理

最根本的作用就是坐标转换
(这涉及到了图形学的知识)

我们所用到的变换均属于仿射变换,仿射变换是 线性变换(缩放,旋转,错切) 和 平移变换(平移) 的复合;

  • **基本变换有4种: **平移(translate)、缩放(scale)、旋转(rotate) 和 错切(skew)

最后一行三个参数通常为0 0 1;(齐次矩阵)

Matrix方法表

基本方法 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 求逆矩阵、 是否为仿射矩阵、 是否为单位矩阵

Matrix方法详解

构造函数

1
2
3
4
5
6
7
Matrix ()
Matrix matrix = new Matrix();
//无参构造函数 初始化为单位矩阵

Matrix (Matrix src)
Matrix matrix = new Matrix(src);
//创建一个Matrix,并对src深拷贝(新的matrix和src是两个对象,但内部数值相同)。

方法

基本方法

1.equals
2.hashCode
3.toString //将Matrix转换为字符串: Matrix{[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]}
4.toShortString //将Matrix转换为短字符串: [1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]

数值操作

1.void set(Matrix src) // 若数值为空则同reset
2.void reset()
3.void setValues(float[] values)//setValues的参数是浮点型的一维数组,长度需要大于9,拷贝数组中的前9位数值赋值给当前Matrix。

4.void getValues(float[] values)

数值计算

  • 1.mapPoints
1
2
3
4
5
6
7
void mapPoints (float[] pts)//pts数组作为参数传递原始数值,计算结果仍存放在pts中

void mapPoints (float[] dst, float[] src)//src作为参数传递原始数值,计算结果存放在dst中,src不变

void mapPoints (float[] dst, int dstIndex,float[] src, int srcIndex, int pointCount)//可以指定只计算一部分数值
//dstIndex:目标数据存储位置起始坐标;srcIndex:元数据存储起始坐标
//pointCount:计算的点个数

计算一组点基于当前Matrix变换后的位置

  • 2.mapRadius
1
float mapRadius(float radius)

测量半径;由于圆可能因为画布变成椭圆;故此处测量的是平均半径;

  • 3.mapRect
1
2
3
boolean mapRect (RectF rect)//测量rect并将测量结果放入rect中,返回值是判断矩形经过变换后是否仍为矩形

boolean mapRect (RectF dst, RectF src)//测量src并将测量结果放入dst中,返回值是判断矩形经过变换后是否仍为矩形

测量矩阵变换后的位置

  • 4.mapVectors
1
2
3
4
5
void mapVectors (float[] vecs)

void mapVectors (float[] dst, float[] src)

void mapVectors (float[] dst, int dstIndex, float[] src, int srcIndex, int vectorCount)

mapVectors 与 mapPoints 基本上是相同的
mapVectors不会受到位移的影响

set、pre 与 post

对于四种基本变换 平移(translate)、缩放(scale)、旋转(rotate)、 错切(skew) 它们每一种都三种操作方法,分别为 设置(set)、 前乘(pre) 和 后乘 (post)

  • 一开始从Canvas中获取到到Matrix并不是初始矩阵,而是经过偏移后到矩阵,且偏移距离就是距离屏幕左上角的位置
  • 构造Matrix时使用的是矩阵乘法,前乘(pre)与后乘(post)结果差别很大
  • 受矩阵乘法影响,后面的执行的操作可能会影响到之前的操作

特殊方法

  • 1.setPolyToPoly
1
2
3
4
5
6
7
8
9
10
11
boolean setPolyToPoly (
float[] src, // 原始数组 src [x,y],存储内容为一组点
int srcIndex, // 原始数组开始位置
float[] dst, // 目标数组 dst [x,y],存储内容为一组点
int dstIndex, // 目标数组开始位置
int pointCount) // 控制点的数量 取值范围是: 0到4
//0 相当于reset
//1 相当于translate
//2 可以进行 缩放、旋转、平移 变换
//3 可以进行 缩放、旋转、平移、错切 变换
//4 可以进行 缩放、旋转、平移、错切以及任何形变

自由变换中的扭曲
setPolyToPoly最多可以支持4个点,这四个点通常为图形的四个角,可以通过这四个角将视图从矩形变换成其他形状

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

将源矩形的内容填充到目标矩形中,然而在大多数的情况下,源矩形和目标矩形的长宽比是不一致的,这个填充的模式就由第三个参数 stf 来确定

ScaleToFit 是一个枚举类型,共包含了四种模式

CENTER 居中,对src等比例缩放,将其居中放置在dst中。

START 顶部,对src等比例缩放,将其放置在dst的左上角。

END 底部,对src等比例缩放,将其放置在dst的右下角。

FILL 充满,拉伸src的宽和高,使其完全填充满dst。

  • 3.rectStaysRect
    判断矩形经过变换后是否仍为矩形,假如Matrix进行了平移、缩放则画布仅仅是位置和大小改变,矩形变换后仍然为矩形,但Matrix进行了非90度倍数的旋转或者错切,则矩形变换后就不再是矩形了

  • 4.setSinCos
    设置sinCos值,这个是控制Matrix旋转的

1
2
3
4
5
6
7
8
9
// 方法一
void setSinCos (float sinValue, // 旋转角度的sin值
float cosValue) // 旋转角度的cos值

// 方法二
void setSinCos (float sinValue, // 旋转角度的sin值
float cosValue, // 旋转角度的cos值
float px, // 中心位置x坐标
float py) // 中心位置y坐标

矩阵相关

  • 1.invert
    求矩阵的逆矩阵
1
boolean invert (Matrix inverse)
  • 2.isAffine
    判断矩阵是否是仿射矩阵

  • 3.isIdentity
    判断是否为单位矩阵

Matrix的技巧

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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));
}

Matrix Camera

因为之前学习过图形学的知识,还有unity3d;于是Camera理解起来就很轻松;Camera相当于用户在3D世界中的一个视角,好比用户的眼睛;

我们的手机屏幕是一个2D的平面,所以也没办法直接显示3D的信息,因此我们看到的所有3D效果都是3D在2D平面的投影而已

Camera主要作用就是这个,将3D信息转换为2D平面上的投影,实际上这个类更像是一个操作Matrix的工具类,使用Camera和Matrix可以在不使用OpenGL的情况下制作出简单的3D效果

Camera常用方法

基本方法 save、restore 保存、 回滚

常用方法 getMatrix、applyToCanvas 获取Matrix、应用到画布

平移 translate 位移

旋转 rotat (API 12)、rotateX、rotateY、rotateZ 各种旋转

相机位置 setLocation (API 12)、getLocationX (API 16)、getLocationY (API 16)、getLocationZ (API 16) 设置与获取相机位置

Camera则主要作用于3D空间

原文中有一部分是关于3D的知识;**

  • 1.Camera使用的3D坐标系是左手坐标系;和2D坐标系的不同就是:2D坐标系的Y轴向下,而3D坐标系的Y轴向上;此外3D坐标系的Z轴垂直于屏幕向里

  • 2.三维投影,通常为正交投影和透视投影;属于图形学的知识

正交投影就是我们数学上学过的 “正视图、正视图、侧视图、俯视图” 这些东西。

透视投影则更像拍照片,符合近大远小的关系,有立体感,我们此处使用的就是透视投影。

  • 3.摄像机;Android 上面观察View的摄像机默认位置在屏幕左上角,而且是距屏幕有一段距离的

摄像机的位置默认是 (0, 0, -576)。其中 -576= -8 x 72
当距离为 -10 的时候,实际距离为 -720 像素

方法详解

基本方法

1
2
3
camera.save();		// 保存状态
... // 具体操作
camera.retore(); // 回滚状态

常用方法

  • 1.getMatrix
1
void getMatrix(Matrix matrix)

计算当前状态下矩阵对应的状态,计算后赋值给matrix

  • 2.applyToCanvas
1
void applyToCanvas(Canvas canvas)

计算当前状态下矩阵对应的状态,计算后赋值给canvas

平移

  • 1.void translate (float x, float y, float z)
    和2D平移类似,只不过是多出来了一个维度,从只能在2D平面上平移到在3D空间内平移

  • 2.沿X轴平移

1
2
camera.translate(x,0,0);
matrix.postTranslate(x,0);
  • 3.沿Y轴平移
1
2
3
4
5
Camera camera = new Camera();
camera.translate(0, 100, 0);
Matrix matrix = new Matrix();
camera.getMatrix(matrix);
matrix.postTranslate(0,100);
1
2
camera.translate(0, -y, 0);
matrix.postTranslate(0, y);
  • 4.沿Z轴平移

**当View和摄像机在同一条直线上时: **此时沿z轴平移相当于缩放的效果,缩放中心为摄像机所在(x, y)坐标,当View接近摄像机时,看起来会变大,远离摄像机时,看起来会变小,近大远小

**当View和摄像机不在同一条直线上时: **当View远离摄像机的时候,View在缩小的同时也在不断接近摄像机在屏幕投影位置(通常情况下为Z轴,在平面上表现为接近坐标原点)。相反,当View接近摄像机的时候,View在放大的同时会远离摄像机在屏幕投影位置

旋转

旋转是Camera制作3D效果的核心,不过它制作出来的并不能算是真正的3D,而是伪3D,因为View是没有厚度的。

1
2
3
4
5
6
void rotate (float x, float y, float z);

// 控制View绕单个坐标轴旋转
void rotateX (float deg);
void rotateY (float deg);
void rotateZ (float deg);

旋转中心

旋转中心默认是坐标原点,对于图片来说就是左上角位置

在3D中没办法指定操作中心
//可以曲线救国;比如强行将旋转中心移动到Camera位置相同的地方

1
2
3
4
Matrix temp = new Matrix();		// 临时Matrix变量
this.getMatrix(temp); // 获取Matrix
temp.preTranslate(-centerX, -centerY); // 使用pre将旋转中心移动到和Camera位置相同。
temp.postTranslate(centerX, centerY); // 使用post将图片(View)移动到原来的位置

相机位置

使用translate和rotate来控制拍摄对象,也可以移动相机自身的位置

1
2
3
4
5
void setLocation (float x, float y, float z); // (API 12) 设置相机位置,默认位置是(0, 0, -8)

float getLocationX (); // (API 16) 获取相机位置的x坐标,下同
float getLocationY ();
float getLocationZ ();

相机和View的z轴距离不能为0

虚拟相机前后均可以拍摄

摄像机右移等于View左移


出处:http://www.gcssloop.com/customview/Matrix_Basic