日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

HenCoder Android 开发进阶:自定义 View 1-4 Canvas 对绘制的辅助 clipXXX() 和 Matrix

發(fā)布時(shí)間:2023/12/10 Android 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 HenCoder Android 开发进阶:自定义 View 1-4 Canvas 对绘制的辅助 clipXXX() 和 Matrix 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

這期是 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 幾何變換

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

  • 使用?Canvas?來做常見的二維變換;
  • 使用?Matrix?來做常見和不常見的二維變換;
  • 使用?Camera?來做三維變換。
  • 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?做常見變換的方式:

  • 創(chuàng)建?Matrix?對(duì)象;
  • 調(diào)用?Matrix?的?pre/postTranslate/Rotate/Scale/Skew()?方法來設(shè)置幾何變換;
  • 使用?Canvas.setMatrix(matrix)?或?Canvas.concat(matrix)?來把幾何變換應(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();

    效果就不放圖了,和?Canvas?是一樣的。

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

  • Canvas.setMatrix(matrix):用?Matrix?直接替換?Canvas?當(dāng)前的變換矩陣,即拋棄?Canvas?當(dāng)前的變換,改用?Matrix?的變換(注:根據(jù)下面評(píng)論里以及我在微信公眾號(hào)中收到的反饋,不同的系統(tǒng)中?setMatrix(matrix)?的行為可能不一致,所以還是盡量用?concat(matrix)?吧);
  • Canvas.concat(matrix):用?Canvas?當(dāng)前的變換矩陣和?Matrix?相乘,即基于?Canvas?當(dāng)前的變換,疊加上?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)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。