自定義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):沒有返回值定铜,有一個(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最開始位置。
**/
// 初始數(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ù)組開始位置
        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è)角將視圖從矩形變換成其他形狀
引用自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)系作者
  • 序言:七十年代末策菜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子酒贬,更是在濱河造成了極大的恐慌又憨,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,640評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锭吨,死亡現(xiàn)場(chǎng)離奇詭異蠢莺,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)零如,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門躏将,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人考蕾,你說(shuō)我怎么就攤上這事祸憋。” “怎么了肖卧?”我有些...
    開封第一講書人閱讀 165,011評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵蚯窥,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng)拦赠,這世上最難降的妖魔是什么巍沙? 我笑而不...
    開封第一講書人閱讀 58,755評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮矛紫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘牌里。我一直安慰自己颊咬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評(píng)論 6 392
  • 文/花漫 我一把揭開白布牡辽。 她就那樣靜靜地躺著喳篇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪态辛。 梳的紋絲不亂的頭發(fā)上麸澜,一...
    開封第一講書人閱讀 51,610評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音奏黑,去河邊找鬼炊邦。 笑死,一個(gè)胖子當(dāng)著我的面吹牛熟史,可吹牛的內(nèi)容都是我干的馁害。 我是一名探鬼主播,決...
    沈念sama閱讀 40,352評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蹂匹,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼碘菜!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起限寞,我...
    開封第一講書人閱讀 39,257評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤忍啸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后履植,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體计雌,經(jīng)...
    沈念sama閱讀 45,717評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評(píng)論 3 336
  • 正文 我和宋清朗相戀三年玫霎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了白粉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,021評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鼠渺,死狀恐怖鸭巴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拦盹,我是刑警寧澤鹃祖,帶...
    沈念sama閱讀 35,735評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站普舆,受9級(jí)特大地震影響恬口,放射性物質(zhì)發(fā)生泄漏校读。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評(píng)論 3 330
  • 文/蒙蒙 一祖能、第九天 我趴在偏房一處隱蔽的房頂上張望歉秫。 院中可真熱鬧,春花似錦养铸、人聲如沸雁芙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)兔甘。三九已至,卻和暖如春鳞滨,著一層夾襖步出監(jiān)牢的瞬間洞焙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工拯啦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留澡匪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,224評(píng)論 3 371
  • 正文 我出身青樓褒链,卻偏偏與公主長(zhǎng)得像仙蛉,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子碱蒙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容