Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 安卓自定義View進階-Matrix原理

安卓自定義View進階-Matrix原理

編輯:關於Android編程

前言

本文內容偏向理論,和 AndroidNote/blob/master/CustomView/Advance/%5B3%5DCanvas_Convert.md">畫布操作 有重疊的部分,本文會讓你更加深入的了解其中的原理。

本篇的主角Matrix,是一個一直在後台默默工作的勞動模范,雖然我們所有看到View背後都有著Matrix的功勞,但我們卻很少見到它,本篇我們就看看它是何方神聖吧。

由於Google已經對這一部分已經做了很好的封裝,所以跳過本部分對實際開發影響並不會太大,不想深究的粗略浏覽即可,下一篇中將會詳細講解Matrix的具體用法和技巧。

Matrix簡介

Matrix是一個矩陣,主要功能是坐標映射,數值轉換。

它看起來大概是下面這樣:

\vce/qsq8tcSjrLWrztLDx9Tav6q3orXEyrG68s2os6Oyu7vhyrnTw9Xi0rvX+LHqz7WjrLb4ysfKudPDxNrI3cf4tcTX+LHqz7WhozwvcD4NCjxwPtLUz8LNvM6qwP2jrM7Sw8e1xMTayN3H+LrNxsHEu9f4serPtbu5z+Cy7tK7uPbNqNaqwLi809K7uPax6sziwLi1xL7gwOujrMv50tTBvdXfyseyu9bYus+1xKOsztLDx9TaxNrI3cf4tcTX+LHqz7XW0LXExNrI3dfu1tW75tbGtcTKsbryv8+2qNKq16q7u86qyrW8yrXEzu/A7df4serPtcC0u+bWxqOsTWF0cml41Nq0y7SmtcTX99PDvs3Kx9equ7vV4tCpyv3WtaGjPC9wPg0KPHA+Jmd0OzxiciAvPg0KvNnJ6M2o1qrAuLjftsjOqjIwz/HL2KOstby6vcC4uN+2yM6qNDDP8cvYLMTHw7TO0sPH1NrE2sjdx/i1xCgwo6wwKc671sO75tbG0ru49rXjo6zX7tbVvs3Sqtequ6/OqtTayrW8ytf4serPtdbQtcQoMKOsNjApzrvWw7vm1sbSu7j2teOhozwvcD4NCjxwPjxpbWcgYWx0PQ=="" src="/uploadfile/Collfiles/20160804/20160804092844741.jpg" title="\" />

以上是僅作為一個簡單的示例,實際上不論2D還是3D,我們要將圖形顯示在屏幕上,都離不開Matrix,所以說Matrix是一個在背後辛勤工作的勞模。

Matrix特點

作用范圍更廣,Matrix在View,圖片,動畫效果等各個方面均有運用,相比與之前講解等畫布操作應用范圍更廣。

更加靈活,畫布操作是對Matrix的封裝,Matrix作為更接近底層的東西,必然要比畫布操作更加靈活。

封裝很好,Matrix本身對各個方法就做了很好的封裝,讓開發者可以很方便的操作Matrix。

難以深入理解,很難理解中各個數值的意義,以及操作規律,如果不了解矩陣,也很難理解前乘,後乘。

常見誤解

1.認為Matrix最下面的一行的三個參數(MPERSP_0、MPERSP_1、MPERSP_2)沒有什麼太大的作用,在這裡只是為了湊數。

實際上最後一行參數在3D變換中有著至關重要的作用,這一點會在後面中Camera一文中詳細介紹。

2.最後一個參數MPERSP_2被解釋為scale

的確,更改MPERSP_2的值能夠達到類似縮放的效果,但這是因為齊次坐標的緣故,並非這個參數的實際功能。

Matrix基本原理

Matrix 是一個矩陣,最根本的作用就是坐標轉換,下面我們就看看幾種常見變換的原理:

我們所用到的變換均屬於仿射變換,仿射變換是 線性變換(縮放,旋轉,錯切) 和 平移變換(平移) 的復合,由於這些概念對於我們作用並不大,此處不過多介紹,有興趣可自行了解。

基本變換有4種: 平移(translate)、縮放(scale)、旋轉(rotate) 和 錯切(skew)。

下面我們看一下四種變換都是由哪些參數控制的。

\

從上圖可以看到最後三個參數是控制透視的,這三個參數主要在3D效果中運用,通常為(0, 0, 1),不在本篇討論范圍內,暫不過多敘述,會在之後對文章中詳述其作用。

由於我們以下大部分的計算都是基於矩陣乘法規則,如果你已經把線性代數還給了老師,請參考一下這裡:
維基百科-矩陣乘法

1.縮放(Scale)

\

用矩陣表示:

你可能注意到了,我們坐標多了一個1,這是使用了齊次坐標系的緣故,在數學中我們的點和向量都是這樣表示的(x, y),兩者看起來一樣,計算機無法區分,為此讓計算機也可以區分它們,增加了一個標志位,增加之後看起來是這樣:

(x, y, 1) - 點

(x, y, 0) - 向量

另外,齊次坐標具有等比的性質,(2,3,1)、(4,6,2)…(2N,3N,N)表示的均是(2,3)這一個點。(將MPERSP_2解釋為scale這一誤解就源於此)。

圖例:

2.錯切(Skew)

錯切存在兩種特殊錯切,水平錯切(平行X軸)和垂直錯切(平行Y軸)。

水平錯切

\

用矩陣表示:

圖例:

垂直錯切

\

用矩陣表示:

圖例:

復合錯切

水平錯切和垂直錯切的復合。

\

用矩陣表示:

圖例:

3.旋轉(Rotate)

假定一個點 A(x0, y0) ,距離原點距離為 r, 與水平軸夾角為 α 度, 繞原點旋轉 θ 度, 旋轉後為點 B(x, y) 如下:

\

用矩陣表示:

圖例:

4.平移(Translate)

此處也是使用齊次坐標的優點體現之一,實際上前面的三個操作使用 2x2 的矩陣也能滿足需求,但是使用 2x2 的矩陣,無法將平移操作加入其中,而將坐標擴展為齊次坐標後,將矩陣擴展為 3x3 就可以將算法統一,四種算法均可以使用矩陣乘法完成。

\

用矩陣表示:

圖例:

Matrix復合原理

其實Matrix的多種復合操作都是使用矩陣乘法實現的,從原理上理解很簡單,但是,使用矩陣乘法也有其弱點,後面的操作可能會影響到前面到操作,所以在構造Matrix時順序很重要。

我們常用的四大變換操作,每一種操作在Matrix均有三類,前乘(pre),後乘(post)和設置(set),由於矩陣乘法不滿足交換律,所以前乘(pre),後乘(post)和設置(set)的區別還是很大的。

前乘(pre)

前乘相當於矩陣的右乘:

\

這表示一個矩陣與一個特殊矩陣前乘後構造出結果矩陣。

後乘(post)

前乘相當於矩陣的左乘:

\

這表示一個矩陣與一個特殊矩陣後乘後構造出結果矩陣。

設置(set)

設置使用的不是矩陣乘法,而是直接覆蓋掉原來的數值,所以,使用設置可能會導致之前的操作失效

組合

我們使用Matrix最終目的就是讓視圖顯示為我們想要的狀態,為此我們可能需要多種操作結合使用。

我發現很多講解Matrix的文章喜歡用繞某一個點縮放(旋轉)的示例來講解,如下:

那麼我們如果想讓它基於圖片中心縮放,應該該怎麼辦?要用到組合變換,
  1)先將圖片由中心平移到原點,這是應用變換 T
  2)對圖應用縮放變換 S 
  3)再將圖片平移回到中心,應用變換 -T


對應代碼:
  matrix.postScale(0.5f, 0.5f);  
  matrix.preTranslate(-pivotX, -pivotY);  
  matrix.postTranslate(pivotX, pivotY);  

PS: 此段文字引用自其它文章。

首先,這個思路是沒有任何問題的,也是實現繞某一點操作的核心原理,但這可能會對一部分小白造成誤解,認為只能這樣實現,然而查看一下Matrix的方法表就能知道四大操作都可以指定中心點,所以,上面的三行代碼用一行就能完成:

matrix.postScale(0.5f, 0.5f, pivotX, pivotY);

組合操作構造Matrix時,個人建議盡量全部使用後乘或者全部使用前乘,這樣操作順序容易確定,出現問題也比較容易排查。
當然,由於矩陣乘法不滿足交換律,前乘和後乘的結果是不同的,使用時應結合具體情景分析使用。

Pre與Post的區別

主要區別其實就是矩陣的乘法順序不同,pre相當於矩陣的右乘,而post相當於矩陣的左乘。

以下觀點存在歧義,故做刪除標注:


在圖像處理中,越靠近右邊的矩陣越先執行,所以pre操作會先執行,而post操作會後執行。

在實際操作中,我們每一步操作都會得出准確的計算結果,但是為什麼還會用存在先後的說法? 難道真的能夠用pre和post影響計算順序? 實則不然,下面我們用一個例子說明:

Matrix matrix = new Matrix();
matrix.postScale(0.5f, 0.8f);
matrix.preTranslate(1000, 1000);
Log.e(TAG, "MatrixTest:3" + matrix.toShortString());

在上面的操作中,如果按照正常的思路,先縮放,後平移,縮放操作執行在前,不會影響到後續的平移操作,但是執行結果卻發現平移距離變成了(500, 800)。

在上面例子中,計算順序是沒有問題的,先計算的縮放,然後計算的平移,而縮放影響到平移則是因為前一步縮放後的結果矩陣右乘了平移矩陣,這是符合矩陣乘法的運算規律的,也就是說縮放操作雖然在前卻影響到了平移操作,相當於先執行了平移操作,然後執行的縮放操作,因此才有pre操作會先執行,而post操作會後執行這一說法

下面我們用不同對方式來構造一個矩陣:

假設我們需要先縮放再平移。

注意:

1.由於矩陣乘法不滿足交換律,請保證使用初始矩陣(Initial Matrix),否則可能導致運算結果不同。 2.注意構造順序,順序是會影響結果的。 3.Initial Matrix是指new出來的新矩陣,或者reset後的矩陣,是一個單位矩陣。

1.僅用pre:

Matrix m = new Matrix();
m.reset();
m.preTranslate(tx, ty); //使用pre,越靠後越先執行。
m.preScale(sx, sy);

用矩陣表示:

\

2.僅用post:

Matrix m = new Matrix();
m.reset();
m.postScale(sx, sy);  //使用post,越靠前越先執行。
m.postTranslate(tx, ty);

用矩陣表示:

\

3.混合:

Matrix m = new Matrix();
m.reset();
m.preScale(sx, sy);  
m.postTranslate(tx, ty);

或:

Matrix m = new Matrix();
m.reset();
m.postTranslate(tx, ty);
m.preScale(sx, sy);  

由於此處只有兩步操作,且指定了先後,所以代碼上交換並不會影響結果。

用矩陣表示:

\

注意: 由於矩陣乘法不滿足交換律,請保證初始矩陣為空,如果初始矩陣不為空,則導致運算結果不同。

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 求逆矩陣、 是否為仿射矩陣、 是否為單位矩陣 …

總結

對於Matrix重在理解,理解了其中的原理之後用起來將會更加得心應手。

學完了本篇之後,推薦配合鴻洋大大的視頻課程
打造個性的圖片預覽與多點觸控 食用,定然能夠讓你對Matrix對理解更上一層樓。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved