Android 中自定義View(四)

系列文章之 Android中自定義View(一)
系列文章之 Android中自定義View(二)
系列文章之 Android中自定義View(三)
系列文章之 Android中自定義View(四)
系列文章之 Android中自定義View(xml繪圖)
本文出自:
http://www.reibang.com/u/a1251e598483

我們?cè)谑褂酶鞣NApp時(shí)都會(huì)看到好多漂亮的效果,說(shuō)實(shí)話有的效果真的很好看,所以覺(jué)得能寫出這些效果的人都好厲害的說(shuō),自定義View 在Android 進(jìn)階相關(guān)的圖書(shū)中都是必會(huì)內(nèi)容,我也一直看過(guò)大概的自定義View 的內(nèi)容,看過(guò)之后還是覺(jué)得不夠詳細(xì),上手還是抓瞎. 剛好網(wǎng)上 扔物線 大神 寫了一個(gè)自定義view 的詳細(xì)教程. http://hencoder.com .如果想學(xué)習(xí)自定義View的同學(xué)請(qǐng)去 大神那里圍觀,本文是記錄自己學(xué)習(xí) 自定義View 的理解和收獲,也是一個(gè)記錄吧,等到用的時(shí)候比較容易找到.

我是分割線,下面開(kāi)始本文內(nèi)容--------------------------

自定義View分為以下幾個(gè)部分

  • Canvas 的 drawXXX() 系列方法及 Paint 最常見(jiàn)的使用
  • Paint 的完全攻略
  • Canvas 對(duì)繪制的輔助——范圍裁切和幾何變換度宦。
  • 使用不同的繪制方法來(lái)控制繪制順序
今天這篇就是第四部分: Canvas 對(duì)繪制的輔助 clipXXX() 和 Matrix

先去看個(gè)視頻 再回來(lái)

1 范圍裁切

范圍裁切有兩個(gè)方法: clipRect() 和 clipPath()。裁切方法之后的繪制代碼似枕,都會(huì)被限制在裁切范圍內(nèi)。

1.1 clipRect()

使用很簡(jiǎn)單年柠,直接應(yīng)用:

canvas.clipRect(left, top, right, bottom);
canvas.drawBitmap(bitmap, x, y, paint);

記得要加上 Canvas.save() 和 Canvas.restore() 來(lái)及時(shí)恢復(fù)繪制范圍菠净,所以完整代碼是這樣的:

canvas.save();  
canvas.clipRect(left, top, right, bottom);  
canvas.drawBitmap(bitmap, x, y, paint);  
canvas.restore();  

1.2 clipPath()

其實(shí)和 clipRect() 用法完全一樣,只是把參數(shù)換成了 Path 彪杉,所以能裁切的形狀更多一些:

canvas.save();  
canvas.clipPath(path1);  
canvas.drawBitmap(bitmap, point1.x, point1.y, paint);  
canvas.restore();

canvas.save();  
canvas.clipPath(path2);  
canvas.drawBitmap(bitmap, point2.x, point2.y, paint);  
canvas.restore();  

2 幾何變換

幾何變換的使用大概分為三類:

使用 Canvas 來(lái)做常見(jiàn)的二維變換毅往;
使用 Matrix 來(lái)做常見(jiàn)和不常見(jiàn)的二維變換;
使用 Camera 來(lái)做三維變換派近。

2.1 使用 Canvas 來(lái)做常見(jiàn)的二維變換:

2.1.1 Canvas.translate(float dx, float dy) 平移

參數(shù)里的 dx 和 dy 表示橫向和縱向的位移攀唯。
canvas.save();
canvas.translate(200, 0);
canvas.drawBitmap(bitmap, x, y, paint);
canvas.restore();

2.1.2 Canvas.rotate(float degrees, float px, float py) 旋轉(zhuǎn)

參數(shù)里的 degrees 是旋轉(zhuǎn)角度,單位是度(也就是一周有 360° 的那個(gè)單位)渴丸,方向是順時(shí)針為正向侯嘀; px 和 py 是軸心的位置。
canvas.save();
canvas.rotate(45, centerX, centerY);
canvas.drawBitmap(bitmap, x, y, paint);
canvas.restore();

2.1.3 Canvas.scale(float sx, float sy, float px, float py) 放縮

參數(shù)里的 sx sy是橫向和縱向的放縮倍數(shù)谱轨; px py是放縮的軸心戒幔。

canvas.save();
canvas.scale(1.3f, 1.3f, x + bitmapWidth / 2, y + bitmapHeight / 2); canvas.drawBitmap(bitmap, x, y, paint); canvas.restore();

2.1.4 skew(float sx, float sy) 錯(cuò)切

參數(shù)里的 sx和 sy是 x 方向和 y 方向的錯(cuò)切系數(shù)。
canvas.save();
canvas.skew(0, 0.5f);
canvas.drawBitmap(bitmap, x, y, paint);
canvas.restore();

2.2 使用 Matrix 來(lái)做變換

2.2.1 使用 Matrix 來(lái)做常見(jiàn)變換

Matrix 做常見(jiàn)變換的方式:
創(chuàng)建 Matrix 對(duì)象土童;
調(diào)用 Matrix 的 pre/postTranslate/Rotate/Scale/Skew() 方法來(lái)設(shè)置幾何變換诗茎;
使用 Canvas.setMatrix(matrix) 或 Canvas.concat(matrix) 來(lái)把幾何變換應(yīng)用到 Canvas。

Matrix matrix = new Matrix();

...

matrix.reset();  
matrix.postTranslate();  
matrix.postRotate();

canvas.save();  
canvas.concat(matrix);  
canvas.drawBitmap(bitmap, x, y, paint);  
canvas.restore();  

把 Matrix 應(yīng)用到 Canvas 有兩個(gè)方法: Canvas.setMatrix(matrix) 和 Canvas.concat(matrix)献汗。

  • Canvas.setMatrix(matrix):用 Matrix 直接替換 Canvas 當(dāng)前的變換矩陣敢订,即拋棄 Canvas 當(dāng)前的變換,改用 Matrix 的變換(不同的系統(tǒng)中 setMatrix(matrix) 的行為可能不一致罢吃,所以還是盡量用 concat(matrix) 吧)楚午;
  • Canvas.concat(matrix):用 Canvas 當(dāng)前的變換矩陣和 Matrix 相乘,即基于 Canvas 當(dāng)前的變換尿招,疊加上 Matrix 中的變換矾柜。

2.2.2 使用 Matrix 來(lái)做自定義變換

Matrix 的自定義變換使用的是 setPolyToPoly() 方法阱驾。

2.2.2.1 Matrix.setPolyToPoly(float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount) 用點(diǎn)對(duì)點(diǎn)映射的方式設(shè)置變換

poly 就是「多」的意思。setPolyToPoly() 的作用是通過(guò)多點(diǎn)的映射的方式來(lái)直接設(shè)置變換怪蔑“∫祝「多點(diǎn)映射」的意思就是把指定的點(diǎn)移動(dòng)到給出的位置,從而發(fā)生形變饮睬。例如:(0, 0) -> (100, 100) 表示把 (0, 0) 位置的像素移動(dòng)到 (100, 100) 的位置租谈,這個(gè)是單點(diǎn)的映射,單點(diǎn)映射可以實(shí)現(xiàn)平移捆愁。而多點(diǎn)的映射割去,就可以讓繪制內(nèi)容任意地扭曲。

Matrix matrix = new Matrix(); float pointsSrc = {left, top, right, top, left, bottom, right, bottom}; float pointsDst = {left - 10, top + 50, right + 120, top - 90, left + 20, bottom + 30, right + 20, bottom + 60};...matrix.reset(); matrix.setPolyToPoly(pointsSrc, 0, pointsDst, 0, 4);canvas.save(); canvas.concat(matrix); canvas.drawBitmap(bitmap, x, y, paint); canvas.restore();

參數(shù)里昼丑,
src和 dst 是源點(diǎn)集合目標(biāo)點(diǎn)集呻逆;
srcIndex和 dstIndex是第一個(gè)點(diǎn)的偏移;
pointCount是采集的點(diǎn)的個(gè)數(shù)(個(gè)數(shù)不能大于 4菩帝,因?yàn)榇笥?4 個(gè)點(diǎn)就無(wú)法計(jì)算變換了)咖城。

2.3 使用 Camera 來(lái)做三維變換

Camera 的三維變換有三類:旋轉(zhuǎn)、平移呼奢、移動(dòng)相機(jī)宜雀。

2.3.1 Camera.rotate*() 三維旋轉(zhuǎn)

Camera.rotate*() 一共有四個(gè)方法: rotateX(deg) rotateY(deg) rotateZ(deg) rotate(x, y, z)

canvas.save();camera.save(); // 保存 Camera 的狀態(tài) 
camera.rotateX(30); // 旋轉(zhuǎn) Camera 的三維空間 
canvas.translate(centerX, centerY); // 旋轉(zhuǎn)之后把投影移動(dòng)回來(lái) camera.applyToCanvas(canvas); // 把旋轉(zhuǎn)投影到 
Canvas canvas.translate(-centerX, -centerY); // 旋轉(zhuǎn)之前把繪制內(nèi)容移動(dòng)到軸心(原點(diǎn)) 
camera.restore(); // 恢復(fù) Camera 的狀態(tài)
canvas.drawBitmap(bitmap, point1.x, point1.y, paint); 
canvas.restore();

Canvas
的幾何變換順序是反的,所以要把移動(dòng)到中心的代碼寫在下面握础,把從中心移動(dòng)回來(lái)的代碼寫在上面辐董。

2.3.2 Camera.translate(float x, float y, float z) 移動(dòng)

它的使用方式和 Camera.rotate*() 相同,而且我在項(xiàng)目中沒(méi)有用過(guò)它禀综,所以就不貼代碼和效果圖了简烘。

2.3.3 Camera.setLocation(x, y, z) 設(shè)置虛擬相機(jī)的位置

注意!這個(gè)方法有點(diǎn)奇葩定枷,它的參數(shù)的單位不是像素孤澎,而是 inch,英寸欠窒。
在 Camera 中覆旭,相機(jī)的默認(rèn)位置是 (0, 0, -8)(英寸)。8 x 72 = 576贱迟,所以它的默認(rèn)位置是 (0, 0, -576)(像素)姐扮。

如果繪制的內(nèi)容過(guò)大絮供,當(dāng)它翻轉(zhuǎn)起來(lái)的時(shí)候衣吠,就有可能出現(xiàn)圖像投影過(guò)大的「糊臉」效果。而且由于換算單位被寫死成了 72 像素壤靶,而不是和設(shè)備 dpi 相關(guān)的缚俏,所以在像素越大的手機(jī)上,這種「糊臉」效果會(huì)越明顯。

而使用 setLocation() 方法來(lái)把相機(jī)往后移動(dòng)忧换,就可以修復(fù)這種問(wèn)題恬惯。

camera.setLocation(0, 0, newZ);
Camera.setLocation(x, y, z) 的 x 和 y 參數(shù)一般不會(huì)改變,直接填 0 就好亚茬。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末酪耳,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子刹缝,更是在濱河造成了極大的恐慌碗暗,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梢夯,死亡現(xiàn)場(chǎng)離奇詭異言疗,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)颂砸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門噪奄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人人乓,你說(shuō)我怎么就攤上這事勤篮。” “怎么了色罚?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵叙谨,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我保屯,道長(zhǎng)手负,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任姑尺,我火速辦了婚禮竟终,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘切蟋。我一直安慰自己统捶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布柄粹。 她就那樣靜靜地躺著喘鸟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪驻右。 梳的紋絲不亂的頭發(fā)上什黑,一...
    開(kāi)封第一講書(shū)人閱讀 51,274評(píng)論 1 300
  • 那天,我揣著相機(jī)與錄音堪夭,去河邊找鬼愕把。 笑死拣凹,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的恨豁。 我是一名探鬼主播嚣镜,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼橘蜜!你這毒婦竟也來(lái)了菊匿?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤计福,失蹤者是張志新(化名)和其女友劉穎捧请,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體棒搜,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡疹蛉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了力麸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片可款。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖克蚂,靈堂內(nèi)的尸體忽然破棺而出闺鲸,到底是詐尸還是另有隱情,我是刑警寧澤埃叭,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布摸恍,位于F島的核電站,受9級(jí)特大地震影響赤屋,放射性物質(zhì)發(fā)生泄漏立镶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一类早、第九天 我趴在偏房一處隱蔽的房頂上張望媚媒。 院中可真熱鬧,春花似錦涩僻、人聲如沸缭召。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)嵌巷。三九已至,卻和暖如春室抽,著一層夾襖步出監(jiān)牢的瞬間搪哪,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工狠半, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留噩死,地道東北人颤难。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓神年,卻偏偏與公主長(zhǎng)得像已维,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子已日,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

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

  • 系列文章之 Android中自定義View(一)系列文章之 Android中自定義View(二)系列文章之 And...
    YoungerDev閱讀 4,398評(píng)論 3 11
  • 系列文章之 Android中自定義View(一)系列文章之 Android中自定義View(二)系列文章之 And...
    YoungerDev閱讀 2,171評(píng)論 0 4
  • 一垛耳、概述 1. 四線格與基線 小時(shí)候,我們?cè)趧傞_(kāi)始學(xué)習(xí)寫字母時(shí)飘千,用的本子是四線格的堂鲜,我們必須把字母按照規(guī)則寫在四線...
    addapp閱讀 7,653評(píng)論 2 17
  • 前言 做這個(gè)項(xiàng)目的初衷是想練手,因?yàn)楝F(xiàn)在rxjava+retrofit框架相當(dāng)火护奈,而公司的同事正在用這個(gè)框架也覺(jué)得...
    xiaoluYi閱讀 2,522評(píng)論 15 12
  • 也許好人做慣了缔莲,連生個(gè)氣都是矯情
    半塊青金石閱讀 168評(píng)論 0 0