我們在自定義 View 控件時(shí)隨處可見 Matrix 的身影,主要用于坐標(biāo)轉(zhuǎn)換映射,我們可以通過 Matrix 矩陣來控制視圖的變換。
Matrix 本質(zhì)上是一個(gè)如下圖所示的矩陣:

上面每個(gè)值都有其對應(yīng)的操作。
Matrix 提供了如下幾個(gè)操作:
縮放(Scale)
對應(yīng) MSCALE_X 與 MSCALE_Y位移(Translate)
對應(yīng) MTRANS_X 與 MTRANS_Y錯(cuò)切(Skew)
對應(yīng) MSKEW_X 與 MSKEW_Y旋轉(zhuǎn)(Rotate)
旋轉(zhuǎn)沒有專門的數(shù)值來計(jì)算,Matrix 會(huì)通過計(jì)算縮放與錯(cuò)切來處理旋轉(zhuǎn)。
數(shù)學(xué)原理
我們先來簡單的復(fù)習(xí)一下矩陣乘法規(guī)則。

我們在使用 Matrix 處理視圖變換時(shí)本質(zhì)上是通過矩陣映射坐標(biāo)。
所以上述的幾個(gè)操作都是對矩陣的操作,我們新建一個(gè) Matrix 后其矩陣為默認(rèn)狀態(tài),其值如下:

可以看到默認(rèn)狀態(tài)下的數(shù)據(jù)都是初始值,即不做任何變換處理,所有坐標(biāo)保持原樣。
縮放(Scale)
對于單個(gè)坐標(biāo)來說,縮放只要將其坐標(biāo)值值乘以縮放值即可。
假設(shè)對某個(gè)點(diǎn)寬度縮放 k1 倍,高度縮放 k2 倍,該點(diǎn)坐標(biāo)為 x0、y0,縮放后坐標(biāo)為 x、y,那么縮放的公式如下:

我們現(xiàn)在知道了縮放對應(yīng)矩陣中的兩個(gè)值的位置以及上面的公式,那現(xiàn)在在用矩陣來描述縮放操作:

等號(hào)左邊的矩陣就是計(jì)算后的縮放結(jié)果。
Matrix 中用于縮放操作的方法有如下兩個(gè):
void setScale(float sx, float sy);
void setScale(float sx, float sy, float px, float py);
前面兩個(gè)參數(shù) sx、sy,分別是寬和高的縮放比例。
第二個(gè)重載方法多了兩個(gè)參數(shù) px、py,這兩個(gè)參數(shù)用來描述縮放的樞軸點(diǎn),關(guān)于樞軸點(diǎn)的含義可以看下注釋:
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.
大概說樞軸點(diǎn)是指定轉(zhuǎn)換應(yīng)保持不變的坐標(biāo)。
當(dāng)我們不傳這兩個(gè)參數(shù)時(shí),樞軸點(diǎn)默認(rèn)為左上角的點(diǎn),縮放都是向下和向右,所以樞軸點(diǎn)可以大概的理解為縮放的錨點(diǎn),縮放從這個(gè)點(diǎn)開始向四周擴(kuò)散。
我們用矩陣來描述一下就能明白了。
初始化一個(gè)矩陣之后調(diào)用縮放方法:
Matrix matrix = new Matrix()
matrix.setScale(0.5F, 0.5F, 300F, 300F);
縮放 0.5 倍,樞軸點(diǎn)為 300,調(diào)用該方法后矩陣變換為:

實(shí)際上我們設(shè)置了樞軸點(diǎn)后 Matrix 會(huì)做一次位移操作,平移距離就是 s * p.
位移(Translate)
位移操作是指將坐標(biāo)(x0,y0)平移一定的距離,我們直接將坐標(biāo)加上平移的距離即可得到平移后的坐標(biāo):

用矩陣表示:

用于設(shè)置位移操作的只有一個(gè)方法:
void setTranslate(float dx, float dy);
錯(cuò)切(Skew)
錯(cuò)切我們平時(shí)用的不多,先用一張圖來幫助理解。

上圖是通過下面的代碼進(jìn)行錯(cuò)切的前后對比圖。
matrix.setSkew(0.3F, 0.3F);
分別設(shè)置了水平錯(cuò)切垂直錯(cuò)切的值為 0.3,效果就是上面的樣子。
錯(cuò)切公式如下:

矩陣描述:

錯(cuò)切操作的方法:
void setSkew(float kx, float ky);
void setSkew(float kx, float ky, float px, float py);
這里的兩個(gè)方法類似于縮放操作的兩個(gè) setScale 方法,包括最后兩個(gè)參數(shù)也具有相同的意義,這里就不多做介紹了。
旋轉(zhuǎn)(Rotate)
旋轉(zhuǎn)的公式比較復(fù)雜,證明也比較麻煩,這里不做詳細(xì)解釋了,因?yàn)槲乙矝]看懂,有興趣的同學(xué)可以自己去國外某知名視頻網(wǎng)站找到詳細(xì)的推導(dǎo)證明視頻,這里放一張剛剛看了但是沒看懂的截圖:

注意一下,Android 中的坐標(biāo)系與上圖的不同,所以結(jié)果有些區(qū)別,那么旋轉(zhuǎn)的公式如下:

矩陣描述:

用于控制旋轉(zhuǎn)的方法:
void setRotate(float degrees);
void setRotate(float degrees, float px, float py);
同上。
上面講了 Matrix 變換的數(shù)學(xué)原理,以及其中提供的幾個(gè)方法,不僅如此,Matrix 還提供復(fù)合變換的功能,下面來說一下。
Matrix 復(fù)合變換
復(fù)合變換是指矩陣同時(shí)實(shí)現(xiàn)兩種或以上變換,例如在平移的同時(shí)改變其大小。
Matrix 的復(fù)合變換實(shí)際上就是矩陣相乘,原理很簡單,但是因?yàn)榫仃囅喑瞬环辖粨Q律、且執(zhí)行順序?qū)Y(jié)果會(huì)有影響,所以想準(zhǔn)確的使用好符合變換需要了解其原理。
上面我們在介紹這幾種變換的同時(shí)也說了他們對應(yīng)的方法,可以看到他們都是 set 方法,但 Matrix 中實(shí)際上提供了三種操作,分別是:設(shè)置(set)、前乘(pre)以及后乘(post)。
所以上述介紹的幾個(gè) set 方法都有與之對應(yīng)的 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)有什么區(qū)別呢?
設(shè)置(set)
如果我們不需要考慮復(fù)合變換的情況,一般可以直接使用 set 方法,因?yàn)?set 方法可能會(huì)重置之前的 Matrix 狀態(tài),導(dǎo)致之前設(shè)置的變換失效。
前乘(pre)
前乘相當(dāng)于矩陣右乘:

假設(shè)當(dāng)前矩陣 M 為:

我們使用 pre 方法做一個(gè)平移操作:
matrix.preTranslate(100, 100);
變換過程如下:

后乘(post)
后乘相當(dāng)于矩陣左乘:

我們用上面的矩陣 M 舉個(gè)例子,同樣對其做一個(gè)平移操作,但是使用 post 方法:
matrix.postTranslate(100, 100);
變換過程如下:

這里的前乘后乘的概念主要是由于矩陣不符合乘法交換律引起的,我們使用時(shí)一定要注意,除此之外,調(diào)用順序的不同對其結(jié)果也有影響,所以我們在使用時(shí)需要先確定好矩陣的變換方式,過程之后,再?zèng)Q定如何使用這些方法。
如上便是 Matrix 的一些基本原理。