Android自定義View(三) Matrix的原理解析

Matrix的中文是矩陣的意思,在Android中它起着座標映射、變換的功能。意思就是說我們在自定義view中,有時需要對圖表進行縮放、旋轉、轉移、錯切等操作,就需要對圖表的座標進行一定的轉換,此時就是Matrix在後臺起作用的。Matrix是一個3x3的矩陣,大概長成下面那樣子,如圖:

舉個例子,爲什麼要用Matrix進行座標轉換,比如我想對點A圍繞原點旋轉30°,此時通過Matrix就可以很容易的實現這一需求,操作如下

Matrix matrix=new Matrix();

matrix.postRotate(30);

其實這個旋轉操作本質上是點A在旋轉前後的座標值發生了改變而已,但是如果旋轉後的座標值要我們自己去計算的話就非常麻煩,而直接調用matrix的postRotate方法,matrix就已經幫我們算好了。至於它裏面是怎麼計算的,下面會講到,這裏只是想說明Matrix的作用,就是對座標通過映射轉換成我們需要的座標值

Matrix的基本變換有四種:translate(平移)、scale(縮放)、rotate(旋轉)、skew(錯切)。下面來看看這個幾種變換分別對應Matrix矩陣的哪些位置

由上面兩個圖表,可以清楚地看到Matrix是一個3×3矩陣,每個參數代表的一定的意義,我們着重關注旋轉、位移、縮放、錯切這幾個參數即可。下面就逐一分析一下這幾種矩陣變換內部是如何實現的

1.縮放(scale)

比如對一個圖表在x和y軸分別縮放k1和k2倍

用Matrix實現如下

        //首先構建一個單位矩陣
        Matrix matrix=new Matrix();
        matrix.setScale(k1,k2);

但從Matrix的內部原理來分析,它的計算公式如下

x=x0 * k1、y=y0 * k2

其中(x0、y0)代表變換前的座標,(x、y)代表變換後的座標

等價於矩陣的如下表示:

可以看到縮放值k1和k2剛好在Matrix矩陣的對應位置,然後通過矩陣的乘法規則,就可以計算出縮放後坐標的x值和y值了,同時也驗證了上面Matrix矩陣中MSACLE_X和MSCALE_Y參數所表示的意義

可以看到Matrix在內部就是這樣通過矩陣算法去映射縮放前後的座標值的

2.旋轉(rotate)

假如有一個點A(x0, y0),原來它跟X軸的夾角是α度,現在需要繞原點旋轉θ度

用Matrix實現如下

Matrix matrix=new Matrix();
matrix.setRotate(θ);

當然不管是縮放還是旋轉,矩陣計算的最終目的都是座標值的變換,假如旋轉後點A變換爲點 B(x, y),那麼點B的座標值x、y的計算如下:

上面的純數學計算方法,那麼用Matrix如何表示呢,如下圖:

圖示如下:

3.平移(translate)

假如對點對座標點(x0, y0)在X軸和Y軸方向分別平移△x和△y的大小

用Matrix的實現如下

Matrix matrix=new Matrix();
matrix.setTranslate(△x,△y);

如果平移後的座標爲(x, y ),則計算座標的公式如下:

矩陣表示如下

4.錯切(skew)

錯切分水平錯切、垂直錯切、複合錯切三種情形

水平錯切表示如下:

矩陣表示:

圖示:

垂直錯切:

 

矩陣表示:

圖示:

 

複合錯切:是上面兩種錯切的複合形式

矩陣表示:

圖示:

上面分析Matrix的四種基本操作,瞭解它們的內部運作原理,本質上就是通過矩陣算法去映射得到座標值。下面我們來看看Matrix的複合原理

Matrix複合原理分析

在開發的過程中,往往對座標的變換都不是單一操作,而上面四種情況都是矩陣的單一操作,如果要進行矩陣的複合操作,那麼就要用到複合操作方法了,常見的有:pre(前乘)、post(後乘)、set(設置),由於矩陣相乘不遵循乘法交換規則,所以關於前乘和後乘還是有很大區別的,而set操作是直接覆蓋掉前面的操作,重新開始

下面通過例子來理解這幾種複合操作:

先解析一下字母的意義,S代表縮放,T代表移動,M代表單位矩陣,R代表旋轉,M'代表結果

pre(前乘):有如下僞代碼

        //首先構建一個單位矩陣
        Matrix matrix=new Matrix();

        //前乘旋轉angle角度
        matrix.preRotate(angle);

        //前乘縮放scale
        matrix.preScale(scale);

        //前乘位移trans
        matrix.preTranslate(trans);

就如上面的矩陣複合是怎麼計算的呢?代碼的執行順序我們很明瞭,但是當前乘的時候,我們的矩陣是不斷加在前面的,表示成這樣:M' = M * R * S * T

後乘(post),有如下僞代碼:

        //首先構建一個單位矩陣
        Matrix matrix=new Matrix();

        //後乘旋轉angle角度
        matrix.postRotate(angle);

        //後乘縮放scale
        matrix.postScale(scale);

        //後乘位移trans
        matrix.postTranslate(trans);

用矩陣乘法表示:M' = T*S*R*M,即我們首先是一個單位矩陣M,然後R加在M的前面,S又加在R的前面,如此類推。由於矩陣不符合乘法交換法則,所以前乘和後乘得到的結果是一般是不一樣的。所以在開發的過程中,我們應該儘量使用一個乘法,即要麼都使用前乘要麼都使用後乘,這樣不容易混亂出錯

下面我們通過一個例子來論證,前乘和後乘的區別,現有兩個矩陣變換,代碼如下:

        //構建單位矩陣
        Matrix matrix=new Matrix();
        //前乘代碼
        matrix.preScale(0.5f,0.6f);
        matrix.preTranslate(100,100);
        
        //後乘代碼
        matrix.postScale(0.5f,0.6f);
        matrix.postTranslate(100,100);

上面前乘用公式表示:M' = S * T ; 後乘公式表示:M' = T * S ;可以看到它們的相乘順序是相反的,下面我們通過矩陣運算看看它們的結果是怎麼樣的:

前乘(pre)的計算如下:

後乘(post)的計算如下:

如此我們可以看到,它們的結果完全不一樣,主要是因爲矩陣是不符合乘法交換法則的,所以我們在運用複合操作的時候要特別注意這一點

 

Matrix的方法表:

方法類別 相關API 摘要
基本方法 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 求逆矩陣、 是否爲仿射矩陣、 是否爲單位矩陣 ...

 關於矩陣的基本原理,就先講到這裏,有更多的內容,可能會後續加入

附:關於矩陣相乘的法則,比如C=AB,因爲AB需要矩陣A的行依次乘以矩陣B的列,對應的元素相乘然後將它們的乘積相加。所以說矩陣A的列數和矩陣B的行數一定要相等。並且兩個矩陣的乘積C的行數等於第一個矩陣A的行數,列數等於第二個矩陣B的列數

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章