HenCoder Android 开发进阶:自定义 View 1-4 Canvas 对绘制的辅助 clipXXX() 和 Matrix
這期是 HenCoder 自定義繪制的第 1-4 期:Canvas 對(duì)繪制的輔助——范圍裁切和幾何變換。
之前的內(nèi)容在這里:?
HenCoder Android 開發(fā)進(jìn)階 自定義 View 1-1 繪制基礎(chǔ)?
HenCoder Android 開發(fā)進(jìn)階 自定義 View 1-2 Paint 詳解?
HenCoder Android 開發(fā)進(jìn)階 自定義 View 1-3 文字的繪制
如果你沒聽說過 HenCoder,可以先看看這個(gè):?
HenCoder:給高級(jí) Android 工程師的進(jìn)階手冊
簡介
一圖勝千言,一視頻勝千圖,走你:
如果你是在手機(jī)上看的,可以點(diǎn)這里去 B 站看原視頻。
1 范圍裁切
范圍裁切有兩個(gè)方法:?clipRect()?和?clipPath()。裁切方法之后的繪制代碼,都會(huì)被限制在裁切范圍內(nèi)。
1.1 clipRect()
使用很簡單,直接應(yīng)用:
canvas.clipRect(left, top, right, bottom); canvas.drawBitmap(bitmap, x, y, paint);記得要加上?Canvas.save()?和?Canvas.restore()?來及時(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 幾何變換
幾何變換的使用大概分為三類:
2.1 使用 Canvas 來做常見的二維變換:
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();好吧這個(gè)從截圖并不能看出什么,那你就用心去感受吧
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 來做變換
2.2.1 使用 Matrix 來做常見變換
Matrix?做常見變換的方式:
效果就不放圖了,和?Canvas?是一樣的。
把?Matrix?應(yīng)用到?Canvas?有兩個(gè)方法:?Canvas.setMatrix(matrix)?和?Canvas.concat(matrix)。
2.2.2 使用 Matrix 來做自定義變換
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()?的作用是通過多點(diǎn)的映射的方式來直接設(shè)置變換?!付帱c(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)就無法計(jì)算變換了)。
2.3 使用 Camera 來做三維變換
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)。這四個(gè)方法的區(qū)別不用我說了吧?
canvas.save();camera.rotateX(30); // 旋轉(zhuǎn) Camera 的三維空間 camera.applyToCanvas(canvas); // 把旋轉(zhuǎn)投影到 Canvascanvas.drawBitmap(bitmap, point1.x, point1.y, paint); canvas.restore();另外,Camera?和?Canvas?一樣也需要保存和恢復(fù)狀態(tài)才能正常繪制,不然在界面刷新之后繪制就會(huì)出現(xiàn)問題。所以上面這張圖完整的代碼應(yīng)該是這樣的:
canvas.save();camera.save(); // 保存 Camera 的狀態(tài) camera.rotateX(30); // 旋轉(zhuǎn) Camera 的三維空間 camera.applyToCanvas(canvas); // 把旋轉(zhuǎn)投影到 Canvas camera.restore(); // 恢復(fù) Camera 的狀態(tài)canvas.drawBitmap(bitmap, point1.x, point1.y, paint); canvas.restore();如果你需要圖形左右對(duì)稱,需要配合上?Canvas.translate(),在三維旋轉(zhuǎn)之前把繪制內(nèi)容的中心點(diǎn)移動(dòng)到原點(diǎn),即旋轉(zhuǎn)的軸心,然后在三維旋轉(zhuǎn)后再把投影移動(dòng)回來:
canvas.save();camera.save(); // 保存 Camera 的狀態(tài) camera.rotateX(30); // 旋轉(zhuǎn) Camera 的三維空間 canvas.translate(centerX, centerY); // 旋轉(zhuǎn)之后把投影移動(dòng)回來 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)回來的代碼寫在上面。
2.3.2 Camera.translate(float x, float y, float z) 移動(dòng)
它的使用方式和?Camera.rotate*()?相同,而且我在項(xiàng)目中沒有用過它,所以就不貼代碼和效果圖了。
2.3.3 Camera.setLocation(x, y, z) 設(shè)置虛擬相機(jī)的位置
注意!這個(gè)方法有點(diǎn)奇葩,它的參數(shù)的單位不是像素,而是 inch,英寸。
我 TM 的真沒逗你,我也沒有胡說八道,它的單位就。是。英。寸。
這種設(shè)計(jì)源自 Android 底層的圖像引擎?Skia?。在 Skia 中,Camera 的位置單位是英寸,英寸和像素的換算單位在 Skia 中被寫死為了 72 像素,而 Android 中把這個(gè)換算單位照搬了過來。是的,它。寫。死。了。
吐槽到此為止,俗話說看透不說透,還是好朋友。
在?Camera?中,相機(jī)的默認(rèn)位置是 (0, 0, -8)(英寸)。8 x 72 = 576,所以它的默認(rèn)位置是 (0, 0, -576)(像素)。
如果繪制的內(nèi)容過大,當(dāng)它翻轉(zhuǎn)起來的時(shí)候,就有可能出現(xiàn)圖像投影過大的「糊臉」效果。而且由于換算單位被寫死成了 72 像素,而不是和設(shè)備 dpi 相關(guān)的,所以在像素越大的手機(jī)上,這種「糊臉」效果會(huì)越明顯。
而使用?setLocation()?方法來把相機(jī)往后移動(dòng),就可以修復(fù)這種問題。
camera.setLocation(0, 0, newZ);Camera.setLocation(x, y, z)?的?x?和?y?參數(shù)一般不會(huì)改變,直接填 0 就好。
好了,上面這些就是本期的內(nèi)容:范圍裁切和幾何變換。
練習(xí)項(xiàng)目
為了避免轉(zhuǎn)頭就忘,強(qiáng)烈建議你趁熱打鐵,做一下這個(gè)練習(xí)項(xiàng)目:HenCoderPracticeDraw4
下期預(yù)告
到這期為止,所有繪制的「術(shù)」就講完了,下期講的是「道」:繪制順序。
總結(jié)
以上是生活随笔為你收集整理的HenCoder Android 开发进阶:自定义 View 1-4 Canvas 对绘制的辅助 clipXXX() 和 Matrix的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Node mysql
- 下一篇: android sina oauth2.