前言
根據(jù)Gcssloop所學(xué)習(xí)的自定義View整理與筆記赋续。
這一節(jié)理論比較多,一定要耐心~
知識(shí)喚醒
一.Matrix初識(shí)
1. 基本變換
** 最后三個(gè)參數(shù)是控制透視的刑桑,這三個(gè)參數(shù)主要在3D效果中運(yùn)用薄榛,通常為(0, 0, 1)**
上面的矩陣便是Matrix的數(shù)據(jù)結(jié)構(gòu)岔激,Matrix其實(shí)就是一個(gè)矩陣。
1. 前乘pre遇八、后乘post矛绘、設(shè)置set
- 前乘pre相當(dāng)于矩陣的右乘:M'=M*S;
- 后乘post相當(dāng)于矩陣的左乘:M'=S*M刃永;
- 設(shè)置set直接覆蓋掉原來(lái)的數(shù)值货矮。
這里,針對(duì)前乘和后乘詳細(xì)的說(shuō)一下斯够,莫暈??
前乘后乘是要一步步的執(zhí)行的囚玫,而我們之前說(shuō)過(guò)pre越靠后的先執(zhí)行,是一種快速推測(cè)結(jié)果的方式读规,并不是計(jì)算的順序抓督,計(jì)算順序可以參考Matrix詳解。
二. Matrix方法
方法類別 | 相關(guān)API | 摘要 |
---|---|---|
基本方法 | equals hashcode toString toShortString | |
數(shù)值操作 | set reset setValues getValues | 設(shè)置 重置 設(shè)置數(shù)值 獲取數(shù)值 |
數(shù)值計(jì)算 | mapPoints mapRadius mapRect mapVectors | 計(jì)算變換后的數(shù)值 |
設(shè)置set | setConcat setRotate setScale setSkew setTranslate | 設(shè)置變換 |
前乘pre | preConcat preRotate preScale preSkew preTranslate | 前乘變換 |
后乘post | postConcat postRotate postScale postSkew postTranslate | 后乘變換 |
特殊方法 | setPolyToPoly setRectToRect rectStaysRect setSinCos | 特殊操作 |
矩陣相關(guān) | invert isAffine isIdentity | 求逆矩陣 是否為仿射矩陣 是否為單位矩陣 |
1. 構(gòu)造方法
- 無(wú)參構(gòu)造
Matrix matrix = new Matrix();
創(chuàng)造出來(lái)的是單位矩陣束亏,如下:
- 有參構(gòu)造
Matrix matrix = new Matrix(src);
創(chuàng)建一個(gè)Matrix铃在,并對(duì)src深拷貝(理解為新的matrix和src是兩個(gè)對(duì)象,但內(nèi)部數(shù)值相同即可)
2. 基本方法
- equals:比較兩個(gè)Matrix的數(shù)值是否相同
- hashCode:獲取Matrix的哈希值
- toString: 將Matrix轉(zhuǎn)換為字符串:Matrix{[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]}
- toShortString:將Matrix轉(zhuǎn)換為短字符串[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]
3. 數(shù)值操作碍遍,控制Matrix里面的數(shù)值
- void set (Matrix src):沒有返回值定铜,有一個(gè)參數(shù),作用是將參數(shù)src中的數(shù)值復(fù)制到當(dāng)前Matrix中怕敬,如果參數(shù)為空揣炕,則重置當(dāng)前Matrix,相當(dāng)于reset东跪。
- void reset ():重置當(dāng)前Matrix畸陡,即將當(dāng)前Matrix重置為單位矩陣。
- void setValues (float[] values):參數(shù)是浮點(diǎn)型的一維數(shù)組虽填,長(zhǎng)度需要大于9丁恭,拷貝數(shù)組中的前9位數(shù)值賦值給當(dāng)前Matrix。
- void getValues (float[] values):getValues和setValues是一對(duì)方法卤唉,參數(shù)也是浮點(diǎn)型的一維數(shù)組涩惑,長(zhǎng)度需要大于9仁期,將Matrix中的數(shù)值拷貝進(jìn)參數(shù)的前9位中
4. 數(shù)值計(jì)算
- mapPoints
計(jì)算一組點(diǎn)基于當(dāng)前Matrix變換后的位置桑驱,(由于是計(jì)算點(diǎn)竭恬,所以參數(shù)中的float數(shù)組長(zhǎng)度一般都是偶數(shù)的,若為奇數(shù),則最后一個(gè)數(shù)值不參與計(jì)算)熬的。
(1) void mapPoints (float[] pts): pts數(shù)組作為參數(shù)傳遞原始數(shù)值痊硕,計(jì)算結(jié)果仍存放在pts中。
// 初始數(shù)據(jù)為三個(gè)點(diǎn) (0, 0) (80, 100) (400, 300)
float[] pts = new float[]{0, 0, 80, 100, 400, 300};
// 構(gòu)造一個(gè)matrix押框,x坐標(biāo)縮放0.5
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 1f);
// 輸出pts計(jì)算之前數(shù)據(jù)
Log.i(TAG, "before: "+ Arrays.toString(pts));
// 調(diào)用map方法計(jì)算
matrix.mapPoints(pts);
// 輸出pts計(jì)算之后數(shù)據(jù)
Log.i(TAG, "after : "+ Arrays.toString(pts));
//結(jié)果:
//before: [0.0, 0.0, 80.0, 100.0, 400.0, 300.0]
//after : [0.0, 0.0, 40.0, 100.0, 200.0, 300.0]
(2) void mapPoints (float[] dst, float[] src):src作為參數(shù)傳遞原始數(shù)值岔绸,計(jì)算結(jié)果存放在dst中,src不變,如果原始數(shù)據(jù)需要保留則一般使用這種方法橡伞。
// 初始數(shù)據(jù)為三個(gè)點(diǎn) (0, 0) (80, 100) (400, 300)
float[] src = new float[]{0, 0, 80, 100, 400, 300};
float[] dst = new float[6];
// 構(gòu)造一個(gè)matrix盒揉,x坐標(biāo)縮放0.5
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 1f);
// 輸出計(jì)算之前數(shù)據(jù)
Log.i(TAG, "before: src="+ Arrays.toString(src));
Log.i(TAG, "before: dst="+ Arrays.toString(dst));
// 調(diào)用map方法計(jì)算
matrix.mapPoints(dst,src);
// 輸出計(jì)算之后數(shù)據(jù)
Log.i(TAG, "after : src="+ Arrays.toString(src));
Log.i(TAG, "after : dst="+ Arrays.toString(dst));
//結(jié)果
//before: src=[0.0, 0.0, 80.0, 100.0, 400.0, 300.0]
//before: dst=[0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
//after : src=[0.0, 0.0, 80.0, 100.0, 400.0, 300.0]
//after : dst=[0.0, 0.0, 40.0, 100.0, 200.0, 300.0]
(3) void mapPoints (float[] dst, int dstIndex,float[] src, int srcIndex, int pointCount) 可以指定只計(jì)算一部分?jǐn)?shù)值。
- dst:目標(biāo)數(shù)據(jù)兑徘;
- dstIndex:目標(biāo)數(shù)據(jù)存儲(chǔ)位置起始下標(biāo)刚盈;
- src:源數(shù)據(jù);
- srcIndex:源數(shù)據(jù)存儲(chǔ)位置起始下標(biāo)挂脑;
- pointCount:計(jì)算的點(diǎn)個(gè)數(shù)
/**
* 將第二藕漱、三個(gè)點(diǎn)計(jì)算后存儲(chǔ)進(jìn)dst最開始位置。
**/
// 初始數(shù)據(jù)為三個(gè)點(diǎn) (0, 0) (80, 100) (400, 300)
float[] pts = new float[]{0, 0, 80, 100, 400, 300};
// 構(gòu)造一個(gè)matrix崭闲,x坐標(biāo)縮放0.5
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 1f);
// 輸出pts計(jì)算之前數(shù)據(jù)
Log.i(TAG, "before: "+ Arrays.toString(pts));
// 調(diào)用map方法計(jì)算
matrix.mapPoints(pts);
// 輸出pts計(jì)算之后數(shù)據(jù)
Log.i(TAG, "after : "+ Arrays.toString(pts));
//結(jié)果
//before: src=[0.0, 0.0, 80.0, 100.0, 400.0, 300.0]
//before: dst=[0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
//after : src=[0.0, 0.0, 80.0, 100.0, 400.0, 300.0]
//after : dst=[40.0, 100.0, 200.0, 300.0, 0.0, 0.0]
2.float mapRadius (float radius):測(cè)量半徑肋联,由于圓可能會(huì)因?yàn)楫嫴甲儞Q成橢圓,所以測(cè)量的是平均半徑
float radius = 100;
float result = 0;
// 構(gòu)造一個(gè)matrix刁俭,x坐標(biāo)縮放0.5
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 1f);
Log.i(TAG, "mapRadius: "+radius);
result = matrix.mapRadius(radius);
Log.i(TAG, "mapRadius: "+result);
//結(jié)果
//mapRadius: 100.0
//mapRadius: 70.71068
3.mapRect:測(cè)量矩形變換后的位置
(1)boolean mapRect (RectF rect): 測(cè)量rect并將測(cè)量結(jié)果放入rect中橄仍,返回值是判斷矩形經(jīng)過(guò)變換后是否仍為矩形。
RectF rect = new RectF(400, 400, 1000, 800);
// 構(gòu)造一個(gè)matrix
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 1f);
matrix.postSkew(1,0);
Log.i(TAG, "mapRadius: "+rect.toString());
boolean result = matrix.mapRect(rect);
Log.i(TAG, "mapRadius: "+rect.toString());
Log.e(TAG, "isRect: "+ result);
//結(jié)果,使用了錯(cuò)切牍戚,所以返回結(jié)果為false
//mapRadius: RectF(400.0, 400.0, 1000.0, 800.0)
//mapRadius: RectF(600.0, 400.0, 1300.0, 800.0)
//isRect: false
(2) boolean mapRect (RectF dst, RectF src) 測(cè)量src并將測(cè)量結(jié)果放入dst中沙兰,返回值是判斷矩形經(jīng)過(guò)變換后是否仍為矩形。
4.mapVectors:測(cè)量向量,類似mapPoints翘魄,唯一的區(qū)別就是mapVectors不會(huì)受到位移的影響鼎天。
void mapVectors (float[] vecs)
void mapVectors (float[] dst, float[] src)
void mapVectors (float[] dst, int dstIndex, float[] src, int srcIndex, int vectorCount)
float[] src = new float[]{1000, 800};
float[] dst = new float[2];
// 構(gòu)造一個(gè)matrix
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 1f);
matrix.postTranslate(100,100);
// 計(jì)算向量, 不受位移影響
matrix.mapVectors(dst, src);
Log.i(TAG, "mapVectors: "+Arrays.toString(dst));
// 計(jì)算點(diǎn)
matrix.mapPoints(dst, src);
Log.i(TAG, "mapPoints: "+Arrays.toString(dst));
//結(jié)果
//mapVectors: [500.0, 800.0]
//mapPoints: [600.0, 900.0]
5. 特殊方法
- setPolyToPoly:poly全稱是Polygon,多邊形的意思
boolean setPolyToPoly (
float[] src, // 原始數(shù)組 src [x,y]暑竟,存儲(chǔ)內(nèi)容為一組點(diǎn)
int srcIndex, // 原始數(shù)組開始位置
float[] dst, // 目標(biāo)數(shù)組 dst [x,y]斋射,存儲(chǔ)內(nèi)容為一組點(diǎn)
int dstIndex, // 目標(biāo)數(shù)組開始位置
int pointCount) // 測(cè)控點(diǎn)的數(shù)量 取值范圍是: 0到4,setPolyToPoly最多可以支持4個(gè)點(diǎn),這四個(gè)點(diǎn)通常為圖形的四個(gè)角,可以通過(guò)這四個(gè)角將視圖從矩形變換成其他形狀
舉個(gè)栗子
//初始化
private void initBitmapAndMatrix() {
bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.bear);
matrix = new Matrix();
src = new float[]{0, 0, //左上角
bitmap.getWidth(), 0,//右上角
0, bitmap.getHeight(),//左下角
bitmap.getWidth(), bitmap.getHeight()//右下角
};
dst = src.clone();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
float x = event.getX();
float y = event.getY();
// 根據(jù)觸控位置改變dst
for (int i = 0; i < 8; i = i + 2) {
if (Math.abs(dst[i] - x) <= 150 && Math.abs(dst[i + 1] - y) <= 150) {
dst[i] = x - 100; //canvas.translate(100, 100)但荤,所以要-100
dst[i + 1] = y - 100;
break;
}
}
invalidate();
}
return true;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.RED);
canvas.translate(100, 100);
matrix.reset();
//將四個(gè)點(diǎn)的位置變換至dst的位置
matrix.setPolyToPoly(src, 0, dst, 0, 4);
//繪制圖片
canvas.drawBitmap(bitmap, matrix, null);
//繪制四個(gè)點(diǎn)的位置
for (int i = 0; i < 8; i = i + 2) {
canvas.drawPoint(dst[i], dst[i + 1], paint);
}
}
效果如下:
至于1罗岖、2、3個(gè)點(diǎn)的效果腹躁,可以點(diǎn)擊這里查看桑包,這里就不講解了,其實(shí)纺非,一個(gè)點(diǎn)的話哑了,就是只能控制一個(gè)點(diǎn)赘方,額。
- setRectToRect:將源矩形的內(nèi)容填充到目標(biāo)矩形中
boolean setRectToRect (RectF src, // 源區(qū)域
RectF dst, // 目標(biāo)區(qū)域
Matrix.ScaleToFit stf) // 縮放適配模式
Matrix.ScaleToFit stf: ScaleToFit 是一個(gè)枚舉類型弱左,共包含了四種模式:
CENTER 居中窄陡,對(duì)src等比例縮放,將其居中放置在dst中拆火。
START 頂部跳夭,對(duì)src等比例縮放,將其放置在dst的左上角们镜。
END 底部币叹,對(duì)src等比例縮放,將其放置在dst的右下角模狭。
FILL 充滿套硼,拉伸src的寬和高,使其完全填充滿dst胞皱。
舉個(gè)栗子??
int width, height;
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
RectF src = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
RectF dst = new RectF(100, 0, width - 100, height);
//居中顯示
matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
canvas.drawBitmap(bitmap, matrix, null);
}
3.rectStaysRect:判斷矩形經(jīng)過(guò)變換后是否仍為矩形
4.setSinCos:設(shè)置sinCos值邪意,這個(gè)是控制Matrix旋轉(zhuǎn)的,由于Matrix已經(jīng)封裝好了Rotate方法反砌,所以這個(gè)并不常用
// 方法一
void setSinCos (float sinValue, // 旋轉(zhuǎn)角度的sin值
float cosValue) // 旋轉(zhuǎn)角度的cos值
// 方法二
void setSinCos (float sinValue, // 旋轉(zhuǎn)角度的sin值
float cosValue, // 旋轉(zhuǎn)角度的cos值
float px, // 中心位置x坐標(biāo)
float py) // 中心位置y坐標(biāo)
舉個(gè)栗子:
Matrix matrix = new Matrix();
// 旋轉(zhuǎn)90度
// sin90=1
// cos90=0
matrix.setSinCos(1f, 0f);
Log.i("rotation", "setSinCos:" + matrix.toShortString());
// 重置
matrix.reset();
// 旋轉(zhuǎn)90度
matrix.setRotate(90);
Log.i("rotation", "setRotate:" + matrix.toShortString());
//結(jié)果:
//setSinCos:[0.0, -1.0, 0.0][1.0, 0.0, 0.0][0.0, 0.0, 1.0]
//setRotate:[0.0, -1.0, 0.0][1.0, 0.0, 0.0][0.0, 0.0, 1.0]
5.其他方法
invert: 求矩陣的逆矩陣
isAffine: 判斷當(dāng)前矩陣是否為仿射矩陣雾鬼,API21(5.0)才添加的方法。
isIdentity: 判斷當(dāng)前矩陣是否為單位矩陣宴树。
三.利用setPolyToPoly制造3D效果
點(diǎn)擊進(jìn)入??
Android FoldingLayout 折疊布局 原理及實(shí)現(xiàn)(一)
Android FoldingLayout 折疊布局 原理及實(shí)現(xiàn)(二)