前言
在 Android 開發(fā)中屯烦,矩陣是一個功能強大并且應用廣泛的神器狈癞,但是就是看不懂,線性代數(shù)白學了嚷缭。
準備
谷歌官方文檔是個好東西!
安卓自定義View進階-Matrix原理
深入理解 Android 中的 Matrix
開整
The Matrix class holds a 3x3 matrix for transforming coordinates.
說明這是一個 3x3的矩陣黄娘。
詳細的說明如下圖:
基本方法解析
我們根據(jù)安卓文檔上面的方法進項講解峭状。
(1) 構(gòu)造函數(shù)
1. public Matrix ()
Create an identity matrix 創(chuàng)建單位矩陣
2. public Matrix (Matrix src)
Create a matrix that is a (deep) copy of src 復制一個矩陣
/**
* 初始化克滴。
*/
private void initValue() {
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.matrix);
matrix = new Matrix();
paint = new Paint();
paint.setColor(Color.BLUE);
paint.setTextSize(30);
getMatrixValues();
}
private void getMatrixValues() {
float[] values = new float[9];
matrix.getValues(values);
matrixValues = toMatrixValues(values);
LoggerUtils.i("float[] values " + matrixValues);
postInvalidate();
}
/**
* 拼接為相應的value值
*
* @param values 數(shù)組
* @return 返回一個字符串
*/
public static String toMatrixValues(float[] values) {
if (values == null)
return "null";
int iMax = values.length - 1;
if (iMax == -1) {
return "[]";
}
StringBuilder b = new StringBuilder();
b.append("\n");
b.append('[');
for (int i = 0; ; i++) {
b.append(values[i]);
if (i == iMax) {
return b.append(']').toString();
}
b.append(", ");
if (i == 2) {
b.append("\n ");
}
if (i == 5) {
b.append("\n ");
}
}
}
打印出的值如下:
[1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0]
得到的效果圖如下:
(2) 矩陣屬性
public boolean isIdentity () //是否是單位矩陣
public boolean isAffine () //是否是仿射矩陣
仿射變換,看了优床,沒看懂劝赔。那就記住只要最后一行是0,0,1則是仿射矩陣。
(3) reset
public void reset() //重置矩陣為單位矩陣胆敞。
(4) 縮放(Scale)
對于單個坐標來說着帽,縮放只要將其坐標值值乘以縮放值即可。
假設(shè)對某個點寬度縮放 k1 倍移层,高度縮放 k2 倍仍翰,該點坐標為 x0、y0观话,縮放后坐標為 x予借、y,那么縮放的公式如下:
我們現(xiàn)在知道了縮放對應矩陣中的兩個值的位置以及上面的公式频蛔,那現(xiàn)在在用矩陣來描述縮放操作:
等號左邊的矩陣就是計算后的縮放結(jié)果灵迫。
Matrix 中用于縮放操作的方法有如下兩個:
public void setScale (float sx, float sy)
public void setScale (float sx, float sy, float px, float py);
前面兩個參數(shù) sx、sy晦溪,分別是寬和高的縮放比例瀑粥。
第二個重載方法多了兩個參數(shù) px、py三圆,這兩個參數(shù)用來描述縮放的樞軸點幅慌,關(guā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.
大概說樞軸點是指定轉(zhuǎn)換應保持不變的坐標狂打。
當我們不傳這兩個參數(shù)時,樞軸點默認為左上角的點,縮放都是向下和向右但汞,所以樞軸點可以大概的理解為縮放的錨點吼野,縮放從這個點開始向四周擴散巩那。
我們用矩陣來描述一下就能明白了悟狱。
- 調(diào)用2個參數(shù)縮放方法:
matrix.setScale(0.5f, 0.5f);
縮放 0.5 倍,調(diào)用該方法后矩陣變換為:
[0.5, 0.0, 0.0,
0.0, 0.5, 0.0,
0.0, 0.0, 1.0]
得到效果如下:
- 調(diào)用4個參數(shù)縮放方法:
matrix.setScale(0.5f, 0.5f, 100f, 200f);
縮放 0.5 倍磷籍,調(diào)用該方法后矩陣變換為:
[0.5, 0.0, 50.0,
0.0, 0.5, 100.0,
0.0, 0.0, 1.0]
實際上我們設(shè)置了樞軸點后 Matrix 會做一次位移操作适荣。
如圖所示:
5. 位移(Translate)
位移操作是指將坐標(x0,y0)平移一定的距離,我們直接將坐標加上平移的距離即可得到平移后的坐標:
用矩陣表示:
用于設(shè)置位移操作的只有一個方法:
public void setTranslate (float dx, float dy)
代碼如下:
matrix.setTranslate(50f, 100f);
得到的矩陣如下:
[1.0, 0.0, 100.0,
0.0, 1.0, 50.0,
0.0, 0.0, 1.0 ]
得到的圖像如下:
6. 錯切(Skew)
錯切不好講院领,我們先看一下公式弛矛,如下:
用矩陣表示:
錯切操作的方法:
void setSkew(float kx, float ky);
void setSkew(float kx, float ky, float px, float py);
我們用代碼驗證一下:
matrix.setSkew(1f, 0f);
得到的矩陣如下:
[1.0, 1.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0]
得到的圖像如下:
我們上面是設(shè)置的x,下面我們設(shè)置y:
matrix.setSkew(0f, 1f);
得到的矩陣如下:
[1.0, 0.0, 0.0,
1.0, 1.0, 0.0,
0.0, 0.0, 1.0]
得到的圖像如下:
如果x和y比然,我們都設(shè)置:
matrix.setSkew(1f, 1f);
得到的矩陣如下:
[1.0, 1.0, 0.0,
1.0, 1.0, 0.0,
0.0, 0.0, 1.0]
那么圖形會是什么樣子的呢丈氓??小伙伴可以試試。
7. 旋轉(zhuǎn)(Rotation)
我們分析圍繞坐標原點旋轉(zhuǎn):
假定有一個點万俗,相對坐標原點順時針旋轉(zhuǎn)后的情形湾笛,同時假定P點離坐標原點的距離為r,如下圖:
那么闰歪,
如果用矩陣嚎研,就可以表示為:
用矩陣表示:
我們用代碼驗證一下:
matrix.setRotate(30);
得到的矩陣如下:
[0.8660254, -0.5, 0.0,
0.5, 0.8660254, 0.0,
0.0, 0.0, 1.0]
得到的圖像如下:
8. 串連接(Concat)
Matrix類還提供了直接矩陣計算方式。Matrix a=new Matrix()相當于創(chuàng)建一個單位矩陣库倘。
- a.set(b)临扮,就是賦值a = b;
- a.preConCat(b)教翩,相當于前乘杆勇,即 a=a×b;
- a.postConCat(b)饱亿,相當于前乘蚜退,即 a=b×a;
- c.setConcat(a,b)彪笼,相當于c=a×b;
我們用代碼驗證:
public void showSetConcat() {
Matrix matrix1 = new Matrix();
matrix1.setTranslate(100, 100);
Matrix matrix2 = new Matrix();
matrix2.setScale(50, 200);
matrix.setConcat(matrix1, matrix2);
getMatrixValues();//showSetScale
}
得到的矩陣如下:
[50.0, 0.0, 100.0,
0.0, 200.0, 100.0,
0.0, 0.0, 1.0]
得到一個有位移关霸,放大的圖片,如下:
9. 正余弦的使用
我們看一下官方解釋:
public void setSinCos (float sinValue, float cosValue)
Set the matrix to rotate by the specified sine and cosine values.
還是說旋轉(zhuǎn)杰扫,那就和setRotate一樣的現(xiàn)象。
上面我們驗證了setRotate膘掰。
matrix.setRotate(30);
得到的矩陣如下:
[0.8660254, -0.5, 0.0,
0.5, 0.8660254, 0.0,
0.0, 0.0, 1.0]
我們使用setSinCos 驗證一下:
matrix.setSinCos(0.5f, 0.8660254f);
得到的矩陣如下:
[0.8660254, -0.5, 0.0,
0.5, 0.8660254, 0.0,
0.0, 0.0, 1.0]
得到的圖像如下:
setSinCos 和 setRotate 可以達到同樣的效果章姓。
Matrix 復合變換
上面我們在介紹這幾種變換的同時也說了他們對應的方法,可以看到他們都是 set 方法识埋,但 Matrix 中實際上提供了三種操作凡伊,分別是:設(shè)置(set)、前乘(pre)以及后乘(post)窒舟。
我們主要講幾個主要的 set 方法與之對應的 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);
設(shè)置(set)
如果我們不需要考慮復合變換的情況,一般可以直接使用 set 方法惠豺,因為 set 方法會重置之前的 Matrix 狀態(tài)银还,導致之前設(shè)置的變換失效。
前乘(pre)
前乘相當于矩陣右乘:
假設(shè)當前矩陣 M 為:
[1.3, 0.0, 0.0,
0.0, 1.3, 0.0,
0.0, 0.0, 1.0]
我們使用 pre 方法做一個平移操作:
matrix.preTranslate(100, 100);
變換過程如下:
后乘(post)
后乘相當于矩陣左乘:
我們用上面的矩陣 M 舉個例子,同樣對其做一個平移操作热监,但是使用 post 方法:
matrix.postTranslate(100, 100);
變換過程如下:
這里的前乘后乘的概念主要是由于矩陣不符合乘法交換律引起的捺弦,我們使用時一定要注意,除此之外,調(diào)用順序的不同對其結(jié)果也有影響列吼,所以我們在使用時需要先確定好矩陣的變換方式幽崩,過程之后,再決定如何使用這些方法寞钥。
總結(jié)
借用網(wǎng)上的一張圖慌申,來總結(jié):