Android Matrix 详解

我们在自定义 View 控件时随处可见 Matrix 的身影,主要用于坐标转换映射,我们可以通过 Matrix 矩阵来控制视图的变换。

Matrix 本质上是一个如下图所示的矩阵:

Matrix

上面每个值都有其对应的操作。
Matrix 提供了如下几个操作:

  • 缩放(Scale)
    对应 MSCALE_XMSCALE_Y

  • 位移(Translate)
    对应 MTRANS_XMTRANS_Y

  • 错切(Skew)
    对应 MSKEW_XMSKEW_Y

  • 旋转(Rotate)
    旋转没有专门的数值来计算,Matrix 会通过计算缩放与错切来处理旋转。

数学原理

我们先来简单的复习一下矩阵乘法规则。

我们在使用 Matrix 处理视图变换时本质上是通过矩阵映射坐标。
所以上述的几个操作都是对矩阵的操作,我们新建一个 Matrix 后其矩阵为默认状态,其值如下:

可以看到默认状态下的数据都是初始值,即不做任何变换处理,所有坐标保持原样。

缩放(Scale)

对于单个坐标来说,缩放只要将其坐标值值乘以缩放值即可。
假设对某个点宽度缩放 k1 倍,高度缩放 k2 倍,该点坐标为 x0、y0,缩放后坐标为 x、y,那么缩放的公式如下:

我们现在知道了缩放对应矩阵中的两个值的位置以及上面的公式,那现在在用矩阵来描述缩放操作:

等号左边的矩阵就是计算后的缩放结果。

Matrix 中用于缩放操作的方法有如下两个:

void setScale(float sx, float sy);
void setScale(float sx, float sy, float px, float py);

前面两个参数 sx、sy,分别是宽和高的缩放比例。
第二个重载方法多了两个参数 px、py,这两个参数用来描述缩放的枢轴点,关于枢轴点的含义可以看下注释:

Set the matrix to scale by sx and sy, with a pivot point at (px, py). The pivot point is the coordinate that should remain unchanged by the specified transformation.

大概说枢轴点是指定转换应保持不变的坐标。
当我们不传这两个参数时,枢轴点默认为左上角的点,缩放都是向下和向右,所以枢轴点可以大概的理解为缩放的锚点,缩放从这个点开始向四周扩散。
我们用矩阵来描述一下就能明白了。
初始化一个矩阵之后调用缩放方法:

Matrix matrix = new Matrix()
matrix.setScale(0.5F, 0.5F, 300F, 300F);

缩放 0.5 倍,枢轴点为 300,调用该方法后矩阵变换为:

实际上我们设置了枢轴点后 Matrix 会做一次位移操作,平移距离就是 s * p.

位移(Translate)

位移操作是指将坐标(x0,y0)平移一定的距离,我们直接将坐标加上平移的距离即可得到平移后的坐标:

用矩阵表示:

用于设置位移操作的只有一个方法:

void setTranslate(float dx, float dy);

错切(Skew)

错切我们平时用的不多,先用一张图来帮助理解。

上图是通过下面的代码进行错切的前后对比图。

matrix.setSkew(0.3F, 0.3F);

分别设置了水平错切垂直错切的值为 0.3,效果就是上面的样子。
错切公式如下:

矩阵描述:

错切操作的方法:

void setSkew(float kx, float ky);
void setSkew(float kx, float ky, float px, float py);

这里的两个方法类似于缩放操作的两个 setScale 方法,包括最后两个参数也具有相同的意义,这里就不多做介绍了。

旋转(Rotate)

旋转的公式比较复杂,证明也比较麻烦,这里不做详细解释了,因为我也没看懂,有兴趣的同学可以自己去国外某知名视频网站找到详细的推导证明视频,这里放一张刚刚看了但是没看懂的截图:

注意一下,Android 中的坐标系与上图的不同,所以结果有些区别,那么旋转的公式如下:

矩阵描述:

用于控制旋转的方法:

void setRotate(float degrees);
void setRotate(float degrees, float px, float py);

同上。

上面讲了 Matrix 变换的数学原理,以及其中提供的几个方法,不仅如此,Matrix 还提供复合变换的功能,下面来说一下。

Matrix 复合变换

复合变换是指矩阵同时实现两种或以上变换,例如在平移的同时改变其大小。

Matrix 的复合变换实际上就是矩阵相乘,原理很简单,但是因为矩阵相乘不符合交换律、且执行顺序对结果会有影响,所以想准确的使用好符合变换需要了解其原理。

上面我们在介绍这几种变换的同时也说了他们对应的方法,可以看到他们都是 set 方法,但 Matrix 中实际上提供了三种操作,分别是:设置(set)、前乘(pre)以及后乘(post)。

所以上述介绍的几个 set 方法都有与之对应的 pre 及 post 方法,方法列表如下:

//scale
boolean preScale(float sx, float sy);
boolean preScale(float sx, float sy, float px, float py);
boolean postScale(float sx, float sy);
boolean postScale(float sx, float sy, float px, float py);

//translate
boolean preTranslate(float dx, float dy);
boolean postTranslate(float dx, float dy);

//skew
boolean preSkew(float kx, float ky);
boolean preSkew(float kx, float ky, float px, float py);
boolean postSkew(float kx, float ky);
boolean postSkew(float kx, float ky, float px, float py);

//rotate
boolean preRotate(float degrees);
boolean preRotate(float degrees, float px, float py);
boolean postRotate(float degrees);
boolean postRotate(float degrees, float px, float py);

那么这三种方法(set、pre、post)有什么区别呢?

设置(set)

如果我们不需要考虑复合变换的情况,一般可以直接使用 set 方法,因为 set 方法可能会重置之前的 Matrix 状态,导致之前设置的变换失效。

前乘(pre)

前乘相当于矩阵右乘:

假设当前矩阵 M 为:

我们使用 pre 方法做一个平移操作:

matrix.preTranslate(100, 100);

变换过程如下:

后乘(post)

后乘相当于矩阵左乘:

我们用上面的矩阵 M 举个例子,同样对其做一个平移操作,但是使用 post 方法:

matrix.postTranslate(100, 100);

变换过程如下:

这里的前乘后乘的概念主要是由于矩阵不符合乘法交换律引起的,我们使用时一定要注意,除此之外,调用顺序的不同对其结果也有影响,所以我们在使用时需要先确定好矩阵的变换方式,过程之后,再决定如何使用这些方法。

如上便是 Matrix 的一些基本原理。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容