Android Matrix 詳解

我們在自定義 View 控件時(shí)隨處可見 Matrix 的身影,主要用于坐標(biāo)轉(zhuǎn)換映射,我們可以通過 Matrix 矩陣來控制視圖的變換。

Matrix 本質(zhì)上是一個(gè)如下圖所示的矩陣:

Matrix

上面每個(gè)值都有其對應(yīng)的操作。
Matrix 提供了如下幾個(gè)操作:

  • 縮放(Scale)
    對應(yīng) MSCALE_XMSCALE_Y

  • 位移(Translate)
    對應(yīng) MTRANS_XMTRANS_Y

  • 錯(cuò)切(Skew)
    對應(yīng) MSKEW_XMSKEW_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 的一些基本原理。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容