Android Matrix 方法詳解


一、簡介

Android android.graphics.Matrix 類是一個3 x 3的矩陣(方陣),上一張幾乎所有介紹Matrix的文章都會引用的Matrix內容圖:

android.graphics.Matrix.png
Matrix使用非常廣泛,平時我們使用的補間動畫、圖像變換、畫布的變換、大名鼎鼎的MPAndroidChart圖表庫等都使用了Matrix。在平時的開發當中,Matrix的使用有時可以起到事半功倍的效果。

二、相關方法

1、equals

比較兩個矩陣是否相等。

    Matrix matrix1 = new Matrix(); Matrix matrix2 = new Matrix(); matrix1.setTranslate(1,2); matrix2.setTranslate(2,2); // 輸出:matrix1 == matrix2:false System.out.println("matrix1 == matrix2:" + matrix1.equals(matrix2)); 

2、+號相連/toString/toShortString

將矩陣轉換為字符串。

    Matrix matrix = new Matrix(); // 輸出:+號相連:Matrix{[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]} System.out.println("+號相連:" + matrix); // 輸出:Matrix{[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]} System.out.println("toString:" + matrix.toString()); // 輸出:[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0] System.out.println("toShortString:" + matrix.toShortString()); 

3、getValues()、setValues()

當我們調用Matrix類的getValues(float[] values)、setValues(float[] values)方法時,可以將這個矩陣轉換成一個數組進行操作。轉換后的數組為:
[ MSCALE_X, MSKEW_X, MTRANS_X, MSKEW_Y, MSCALE_Y, MTRANS_Y, MPERSP_0, MPERSP_1, MPERSP_2]
為了方便操作這個數組,在android.graphics.Matrix類中,定義了MSCALE_X、MSKEW_X...變量,分別代表各自功能在數組中對應的下標,具體內容如下:

public static final int MSCALE_X = 0; //!< use with getValues/setValues public static final int MSKEW_X = 1; //!< use with getValues/setValues public static final int MTRANS_X = 2; //!< use with getValues/setValues public static final int MSKEW_Y = 3; //!< use with getValues/setValues public static final int MSCALE_Y = 4; //!< use with getValues/setValues public static final int MTRANS_Y = 5; //!< use with getValues/setValues public static final int MPERSP_0 = 6; //!< use with getValues/setValues public static final int MPERSP_1 = 7; //!< use with getValues/setValues public static final int MPERSP_2 = 8; //!< use with getValues/setValues 

方法示例:

    Matrix matrix = new Matrix(); // matrix = [1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0] System.out.println("matrix = " + matrix.toShortString()); float[] values = new float[9]; matrix.getValues(values); // matrix轉換成數組后 = [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0] System.out.println("matrix轉換成數組后 = " + Arrays.toString(values)); // 為matrix賦值 values [Matrix.MTRANS_X] = 2; values [Matrix.MTRANS_Y] = 3; matrix.setValues(values); // matrix = [1.0, 0.0, 2.0][0.0, 1.0, 3.0][0.0, 0.0, 1.0] System.out.println("matrix = " + matrix.toShortString()); 

4、setXXX/preXXX/postXXX

XXX可以是Translate、Scale、Rotate、Skew和Concat。其中Concat參數為Matrix,表示直接操作Matrix。由於縮放、旋轉、錯切可以繞中心操作,如果指定了中心,則變換步驟為:

  1. 將原點平移到該點。
  2. 做縮放、錯切、旋轉操作。
  3. 原點平移到原來的原點處。

方法參數轉換成了一個怎樣的矩陣?

// 下面代碼中參數(2,2) 轉換后的矩陣為 // [2.0, 0.0, 0.0] // [0.0, 2.0, 0.0] // [0.0, 0.0, 1.0] // 即根據XXX代表的功能修改矩陣中對應功能位置的值即可 matrix.postScale(2,2); 

setXXX

首先會將該Matrix重置為單位矩陣,即相當於首先會調用reset()方法,然后再設置該Matrix中對應功能的值。例:

    // [1.0, 0.0, 0.0] // [0.0, 1.0, 0.0] // [0.0, 0.0, 1.0] Matrix matrix = new Matrix(); // [1.0, 0.0, 0.0] [2.0, 3.0, 4.0] // [0.0, 1.0, 0.0] -> [2.0, 0.0, 0.0] // [0.0, 0.0, 1.0] [1.0, 1.0, 1.0] matrix.setValues(new float[]{2.0f,3.0f, 4.0f, 2.0f,0.0f, 0.0f, 1.0f,1.0f,1.0f}); // [2.0, 3.0, 4.0] [1.0, 0.0, 0.0] [2.0, 0.0, 0.0] // [2.0, 2.0, 0.0] -> [0.0, 1.0, 0.0] -> [0.0, 2.0, 0.0] // [1.0, 1.0, 1.0] [0.0, 0.0, 1.0] [0.0, 0.0, 1.0] matrix.setScale(2,2); 

preXXX

不會重置Matrix,相當於當前操作矩陣(A)左乘參數矩陣(B),即AB。例:

    // [1.0, 0.0, 0.0] // [0.0, 1.0, 0.0] // [0.0, 0.0, 1.0] Matrix matrix = new Matrix(); // [1.0, 0.0, 0.0] [2.0, 3.0, 4.0] // [0.0, 1.0, 0.0] -> [2.0, 0.0, 0.0] // [0.0, 0.0, 1.0] [1.0, 1.0, 1.0] matrix.setValues(new float[]{2.0f,3.0f, 4.0f, 2.0f,0.0f, 0.0f, 1.0f,1.0f,1.0f}); // [2.0, 3.0, 4.0] [2.0, 0.0, 0.0] [4.0, 6.0, 4.0] // [2.0, 0.0, 0.0](matrix) * [0.0, 2.0, 0.0] = [4.0, 0.0, 0.0](matrix) // [1.0, 1.0, 1.0] [0.0, 0.0, 1.0] [2.0, 2.0, 1.0] matrix.preScale(2,2); 

postXXX

不會重置Matrix,相當於當前操作矩陣(A)右乘參數矩陣(B),即BA,例:

    // [1.0, 0.0, 0.0] // [0.0, 1.0, 0.0] // [0.0, 0.0, 1.0] Matrix matrix = new Matrix(); // [1.0, 0.0, 0.0] [2.0, 3.0, 4.0] // [0.0, 1.0, 0.0] -> [2.0, 0.0, 0.0] // [0.0, 0.0, 1.0] [1.0, 1.0, 1.0] matrix.setValues(new float[]{2.0f,3.0f, 4.0f, 2.0f,0.0f, 0.0f, 1.0f,1.0f,1.0f}); // [2.0, 0.0, 0.0] [2.0, 3.0, 4.0] [4.0, 6.0, 8.0] // [0.0, 2.0, 0.0] * [2.0, 0.0, 0.0] (matrix) = [4.0, 0.0, 0.0](matrix) // [0.0, 0.0, 1.0] [1.0, 1.0, 1.0] [1.0, 1.0, 1.0] matrix.postScale(2,2); 

setContact

關於setContact(Matrix m1,Matrix m2)方法,需要單獨說下,它的參數為兩個Matrix對象,計算規則為:當前操作的Matrix對象 = m1 * m2;
例:

    // [1.0, 0.0, 0.0] // [0.0, 1.0, 0.0] // [0.0, 0.0, 1.0] Matrix matrix = new Matrix(); Matrix matrix1 = new Matrix(); Matrix matrix2 = new Matrix(); // [1.0, 0.0, 0.0] [2.0, 3.0, 4.0] // [0.0, 1.0, 0.0] -> [2.0, 0.0, 0.0] // [0.0, 0.0, 1.0] [1.0, 1.0, 1.0] matrix1.setValues(new float[]{2.0f,3.0f, 4.0f, 2.0f,0.0f, 0.0f, 1.0f,1.0f,1.0f}); // [1.0, 0.0, 0.0] [2.0, 5.0, 4.0] // [0.0, 1.0, 0.0] -> [3.0, 0.0, 0.0] // [0.0, 0.0, 1.0] [1.0, 2.0, 1.0] matrix2.setValues(new float[]{2.0f,5.0f, 4.0f, 3.0f,0.0f, 0.0f, 1.0f,2.0f,1.0f}); // [2.0, 3.0, 4.0] [2.0, 5.0, 4.0] [17.0, 18.0, 12.0] // [2.0, 2.0, 0.0](matrix1) * [3.0, 0.0, 0.0](matrix2) = [4.0, 10.0, 8.0 ] (matrix) // [1.0, 1.0, 1.0] [1.0, 2.0, 1.0] [6.0, 7.0, 5.0 ] matrix.setConcat(matrix1,matrix2); 

5、mapRadius/mapPoints/mapRect/mapVectors

可翻譯為將矩陣映射到(作用於)點、矩形、半徑、向量。

mapRadius

半徑的計算。例:

    // 一個半徑為100.0f的圓,放大1倍后,半徑也將增大一倍。據說用在畫布中的圓隨畫布大小變化時 float radius = 100.0f; float radiusAfterMatrix; Matrix matrixRadius = new Matrix(); matrixRadius.setScale(2,2); radiusAfterMatrix = matrixRadius.mapRadius(radius); // 輸出:radius=200.0 System.out.println("radius=" + radiusAfterMatrix); 

mapPoints

此方法有3個重載方法。點數組各值分別代表pts[x0,y0,x1,y1 ... xn,yn],因為一個點的確定需要x坐標和y坐標兩個值,所以,pts數組的長度一般為偶數,如果為奇數,則最后一個值不參與計算(長度為1將不計算)。下面給出具體例子,例子中將會詳細說明mapPoints方法。

    // ======================= // mapPoints(float[] pts) // ======================= // 運算后的結果會保存在pts數組中,原pts數組中的內容會被覆蓋 // 1.《點的移動》,對於任意點(Xn,Yn),x軸方向平移dx,y軸方向平移dy后有: // Xn = Xn + dx // Yn = Yn + dy float[] ptsTrans = {6,2}; Matrix matrixTrans = new Matrix(); matrixTrans.setTranslate(-2,2); matrixTrans.mapPoints(ptsTrans); // 輸出:trans=[4.0, 4.0] System.out.println("trans=" + Arrays.toString(ptsTrans)); // 2.《點的放大》,對於任意點(Xn,Yn),繞點(px,py)x軸、y軸方向分別放大sx倍、sy倍后,有: // Xn = Xn * sx + (px - px * sx) // Yn = Yn * sy + (py - sy * py) float[] ptsScale = {2,3}; Matrix matrixScale = new Matrix(); matrixScale.setScale(3,6,2,2); matrixScale.mapPoints(ptsScale); // 輸出:scale=[2.0, 8.0] System.out.println("scale=" + Arrays.toString(ptsScale)); // 3.《點的旋轉》,對於任意點(Xn,Yn),繞點(px,py)旋轉a度后,有: // Xn = (Xn - px) * cos(a) - (Yn - py) * sin(a) + px // Yn = (Xn - px) * sin(a) + (Yn - py) * cos(a) + py float[] ptsRotate = {6,6}; Matrix matrixRotate = new Matrix(); matrixRotate.preRotate(90,2,3); matrixRotate.mapPoints(ptsRotate); // 輸出:rotate=[-1.0,7.0] System.out.println("rotate=" + Arrays.toString(ptsRotate)); // 4.《點的錯切》,對於任意點(Xn,Yn),繞點(px,py)x軸、y軸方向分別錯切kx、ky后,有: // Xn = Xn + kx(Yn - py) // Yn = Yn + ky(Xn - px) float[] ptsSkew = {3,2}; Matrix matrixSkew = new Matrix(); matrixSkew.setSkew(2,3,6,8); matrixSkew.mapPoints(ptsSkew); // 輸出:skew=[-9.0,-7.0] System.out.println("skew=" + Arrays.toString(ptsSkew)); // =================================== // mapPoints(float[] dst, float[] src) // =================================== // 運算后的結果保存在dst數組中,原src數組中的內容會保留 float[] src = {2,3,3,3}; float[] dst = new float[src.length]; Matrix matrixDstSrc = new Matrix(); matrixDstSrc.setTranslate(2,3); matrixDstSrc.mapPoints(dst,src); // 輸出:dst=[4.0,6.0,5.0,6.0] System.out.println("dst=" + Arrays.toString(dst)); // 輸出:src=[2.0,3.0,3.0,3.0] System.out.println("src=" + Arrays.toString(src)); // ============================================================================== // mapPoints(float[] dst, ---- 計算結果存放數組 // int dstIndex, ---- dst數組存放計算結果時起始下標 // float[] src, ---- 計算的源數組 // int srcIndex, ---- 源數組計算時起始下標 // int pointCount ---- 從起始下標開始一共要計算多少個點 // ) // ============================================================================== // 運算后的結果保存在dst數組中 float[] src1 = {2,3,3,3,2,3}; float[] dst1 = new float[]{6,6,6,6,6,6}; Matrix matrixDstSrc1 = new Matrix(); matrixDstSrc1.setTranslate(1,1); // 1)從src1下標為2的位置開始計算,計算1個點,注意,是一個點,不是一個長度;計算的結果只保存計算的點,未計算的點將舍棄,即結果為:[4.0,4.0] // 2)將src1計算后的結果,從dst1下標為2的位置開始放置 // 注意,從存放數組開始的位置存放計算結果時,如果長度不夠,將拋出 ArrayIndexOutOfBoundsException 異常 matrixDstSrc1.mapPoints(dst1,5,src1,2,1); // 輸出:dst=[0.0,0.0,2.0,3.0,4.0,4.0] System.out.println("dst1=" + Arrays.toString(dst1)); // 輸出:src=[2.0,3.0,3.0,3.0,2.0,3.0] System.out.println("src1=" + Arrays.toString(src1)); 

mapRect

將矩形的4個點按Matrix中設定的值變換,返回值為變換后是否還為一個矩形。此方法有2個重載方法。

    // ============================================ // mapRect(RectF rect) // ============================================ // 結果存放在rect中,原rect將被覆蓋 RectF rectF = new RectF(100,100,200,200); // 輸出:rectFbefore = RectF(100.0, 100.0, 200.0, 200.0) System.out.println("rectFbefore = " + rectF); Matrix matrixRectF = new Matrix(); matrixRectF.setScale(2,2); matrixRectF.mapRect(rectF); // 輸出:rectFafter = RectF(200.0, 200.0, 400.0, 400.0) System.out.println("rectFafter = " + rectF); // ============================================ // mapRect(RectF dst,RectF src) // ============================================ // 結果存放在dst中,原src會保留。其它與mapRect(RectF rect)方法相同 

mapVectors

用法和mapPoints方法類似,此方法有3個重載方法,唯一不同的是mapVectors不受位移影響。例:

    float[] vector = {2,3}; float[] point = {2,3}; Matrix matrixTranslate = new Matrix(); matrixTranslate.setTranslate(2,3); matrixTranslate.mapVectors(vector); matrixTranslate.mapPoints(point); // 輸出:vector = [2.0,3.0] System.out.println("vector = " + Arrays.toString(vector)); // 輸出:point = [4.0,6.0] System.out.println("point = " + Arrays.toString(point)); 

6、invert

做相反的運算。得到變化前的狀態。例:圖形旋轉一定角度后再恢復旋轉前的狀態。matrixOri.invert(matrixInvert)方法可翻譯為:將matrixOri這個矩陣反轉后存放在matrixInvert這個矩陣中, matrixInvert這個矩陣中原來的值將被覆蓋。

    // ========================================== // 移動 // ========================================== // [1.0, 0.0, Δx] [1.0, 0.0, -Δx] // [0.0, 1.0, Δy] invert -> [0.0, 1.0, -Δy] // [0.0, 0.0, 1.0] [0.0, 0.0, 1.0] Matrix matrixTrans = new Matrix(); matrixTrans.setTranslate(2,3); // [1.0, 0.0, 2.0] // [0.0, 1.0, 3.0] // [0.0, 0.0, 1.0] System.out.println("matrixTrans = " + matrixTrans); matrixTrans.invert(matrixTrans); // [1.0, 0.0, -2.0] // [0.0, 1.0, -3.0] // [0.0, 0.0, 1.0] System.out.println("matrixTrans = " + matrixTrans); // ========================================== // 縮放 // ========================================== // [sx, 0, -px] [1/sx, 0, px/2] // [0, sy, -py] invert -> [0, 1/sy, py/2] // [0.0, 0.0, 1.0] [0.0, 0.0, 1.0] Matrix matrixScale = new Matrix(); matrixScale.setScale(2,2,12,7); // [2.0, 0.0, -12.0] // [0.0, 2.0, -7.0] // [0.0, 0.0, 1.0] System.out.println("matrixScale = " + matrixScale); matrixScale.invert(matrixScale); // [0.5, 0.0, 6.0] // [0.0, 0.5, 3.5] // [0.0, 0.0, 1.0] System.out.println("matrixScale = " + matrixScale); 

7、isIdentity

判斷一個矩陣是否為單位矩陣

    Matrix matrix = new Matrix(); // 輸出:matrix is identity:true System.out.println("matrix is identity:" + matrix.isIdentity()); matrix.setTranslate(1,2); // 輸出:matrix is identity:false System.out.println("matrix is identity:" + matrix.isIdentity()); 

8、setPolyToPoly

根據src坐標到dst坐標的變換關系,生成對應的Matrix矩陣。

    // ================================================================================================== // setPolyToPoly(float[] src, 變換前的點數組,內容為[x0, y0, x1, y1, ...] // int srcIndex, 第一個變化的點在src數組中的下標 // float[] dst, src變換后的點數組,內容為[x0‘, y0’, x1’, y1’, ...],與src數組一一對應 // int dstIndex, 變化后的第一個點在dst數組中存儲的位置 // int pointCount 一次一共需要變換多少個點,取值范圍[0,4] // ) // ================================================================================================== float[] src = {1,2}; float[] dst = {2,4}; // [1.0, 0.0, 0.0] // matrix = [0.0, 1.0, 0.0] // [0.0, 0.0, 1.0] Matrix matrix = new Matrix(); System.out.println("matrix = " + matrix.toShortString()); matrix.setPolyToPoly(src,0,dst,0,1); // [1.0, 0.0, 1.0] // matrix = [0.0, 1.0, 2.0] // [0.0, 0.0, 1.0] System.out.println("matrix = " + matrix.toShortString()); // 驗證這個生成的matrix是否正確 // [1.0, 0.0, 1.0] [1] [2.0] // [0.0, 1.0, 2.0] * [2] = [4.0] // [0.0, 0.0, 1.0] [1] [1.0] 

9、setRectToRect

將矩形填充到矩形中。其中在填充時可以指定4種填充模式,這4種模式用Matrix.ScaleToFit枚舉類型表示,關於這4種填充模式,借用一張官方demo圖:

ScaleToFit.png
    Matrix matrix = new Matrix(); RectF rectFSrc = new RectF(100,100,200,400); RectF rectFDst = new RectF(100,100,400,200); matrix.setRectToRect(rectFSrc,rectFDst, Matrix.ScaleToFit.FILL); 

三、總結

Matrix可以運用到很多地方,但基本的原理都是通過Matrix提供的API對Matrix中值的更改,然后再將這個Matrix作用於不同的對象(圖片、畫布等)。

本篇文章主要是對android.graphics.Matrix類中常用方法的講解,與網上其它講解Matrix的文章方向可能有所不同。由於文章很多細致的講解都在代碼的注釋中,請各位一定要關注文章中的代碼示例。文章中的代碼示例均可復制>>粘貼>>運行,可自行測試。

由於本人功力有限,文章中可能會有出錯的地方。如果各位發現有誤的地方請指證。當然,如果有什么地方不理解,也可以提出來。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM