自定義View-第十一步:Matrix

前言

根據(jù)Gcssloop所學(xué)習(xí)的自定義View整理與筆記。
這一節(jié)理論比較多,一定要耐心~

知識(shí)喚醒

矩陣乘法

一.Matrix初識(shí)

1. 基本變換

平移旋轉(zhuǎn)

縮放錯(cuò)切

** 最后三個(gè)參數(shù)是控制透視的,這三個(gè)參數(shù)主要在3D效果中運(yùn)用,通常為(0, 0, 1)**
上面的矩陣便是Matrix的數(shù)據(jù)結(jié)構(gòu),Matrix其實(shí)就是一個(gè)矩陣。

1. 前乘pre、后乘post、設(shè)置set

  1. 前乘pre相當(dāng)于矩陣的右乘:M'=M*S;
  2. 后乘post相當(dāng)于矩陣的左乘:M'=S*M;
  3. 設(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)造方法

  1. 無(wú)參構(gòu)造
 Matrix matrix = new Matrix();

創(chuàng)造出來(lái)的是單位矩陣,如下:


  1. 有參構(gòu)造
Matrix matrix = new Matrix(src);

創(chuàng)建一個(gè)Matrix,并對(duì)src深拷貝(理解為新的matrix和src是兩個(gè)對(duì)象,但內(nèi)部數(shù)值相同即可)

2. 基本方法

  1. equals:比較兩個(gè)Matrix的數(shù)值是否相同
  2. hashCode:獲取Matrix的哈希值
  3. toString: 將Matrix轉(zhuǎn)換為字符串:Matrix{[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]}
  4. 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ù)值

  1. void set (Matrix src):沒(méi)有返回值,有一個(gè)參數(shù),作用是將參數(shù)src中的數(shù)值復(fù)制到當(dāng)前Matrix中,如果參數(shù)為空,則重置當(dāng)前Matrix,相當(dāng)于reset。
  2. void reset ():重置當(dāng)前Matrix,即將當(dāng)前Matrix重置為單位矩陣。
  3. void setValues (float[] values):參數(shù)是浮點(diǎn)型的一維數(shù)組,長(zhǎng)度需要大于9,拷貝數(shù)組中的前9位數(shù)值賦值給當(dāng)前Matrix。
  4. void getValues (float[] values):getValues和setValues是一對(duì)方法,參數(shù)也是浮點(diǎn)型的一維數(shù)組,長(zhǎng)度需要大于9,將Matrix中的數(shù)值拷貝進(jìn)參數(shù)的前9位中

4. 數(shù)值計(jì)算

  1. 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最開(kāi)始位置。
**/
// 初始數(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. 特殊方法

  1. setPolyToPoly:poly全稱是Polygon,多邊形的意思
   boolean setPolyToPoly (
        float[] src,    // 原始數(shù)組 src [x,y],存儲(chǔ)內(nèi)容為一組點(diǎn)
        int srcIndex,   // 原始數(shù)組開(kāi)始位置
        float[] dst,    // 目標(biāo)數(shù)組 dst [x,y],存儲(chǔ)內(nèi)容為一組點(diǎn)
        int dstIndex,   // 目標(biāo)數(shù)組開(kāi)始位置
        int pointCount) // 測(cè)控點(diǎn)的數(shù)量 取值范圍是: 0到4,setPolyToPoly最多可以支持4個(gè)點(diǎn),這四個(gè)點(diǎn)通常為圖形的四個(gè)角,可以通過(guò)這四個(gè)角將視圖從矩形變換成其他形狀
引用自http://www.gcssloop.com/customview/Matrix_Method

舉個(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),額。

  1. 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)(二)

參考資料

官網(wǎng)
Matrix詳解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容