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

歡迎訪問 生活随笔!

生活随笔

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

Android

HenCoder Android 开发进阶: 自定义 View 1-2 Paint 详解

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

這期是 HenCoder 自定義繪制的第二期:?Paint。如果你沒看過第一期,可以先去看一下第一期:

HenCoder Android 開發(fā)進(jìn)階:自定義 View 1-1 繪制基礎(chǔ)

簡介

上一期我已經(jīng)簡單說過,?Canvas?的?drawXXX()?方法配合?Paint?的幾個(gè)常用方法可以實(shí)現(xiàn)最常見的繪制需求;而如果你只會(huì)基本的繪制,?Paint?的完全功能的掌握,能讓你更進(jìn)一步,做出一些更加細(xì)致、炫酷的效果。把?Paint?掌握之后,你幾乎不再會(huì)遇到「iOS 組可以實(shí)現(xiàn),但你卻實(shí)現(xiàn)不了」的繪制效果。

由于依然是講繪制的,所以這期就沒有介紹視頻了。繪制的內(nèi)容一共需要講大概 5~6 期才能講完,也就是說你要看 5~6 期才能成為自定義繪制的高手。相對(duì)于上期的內(nèi)容,這期的內(nèi)容更為專項(xiàng)、深度更深。對(duì)于沒有深入研究過?Paint?的人,這期是一個(gè)對(duì)?Paint?的詮釋;而對(duì)于嘗試過研究?Paint?但仍然對(duì)其中一些 API 有疑惑的人,這期也可以幫你解惑。

另外,也正由于這期的內(nèi)容是更為專項(xiàng)的,所以建議你在看的時(shí)候,不必像上期那樣把所有東西都完全記住,而是只要把內(nèi)容理解了就好。這期的內(nèi)容,只要做到「知道有這么個(gè)東西」,在需要用到的時(shí)候能想起來這個(gè)功能能不能做、大致用什么做就好,至于具體的實(shí)現(xiàn),到時(shí)候拐回來再翻一次就行了。

好,下面進(jìn)入正題。

Paint?的 API 大致可以分為 4 類:

  • 顏色
  • 效果
  • drawText() 相關(guān)
  • 初始化

下面我就對(duì)這 4 類分別進(jìn)行介紹:

1 顏色

Canvas?繪制的內(nèi)容,有三層對(duì)顏色的處理:

這圖大概看看就行,不用鉆研明白再往下看,因?yàn)榈冗@章講完你就懂了。

1.1 基本顏色

像素的基本顏色,根據(jù)繪制內(nèi)容的不同而有不同的控制方式:?Canvas?的顏色填充類方法?drawColor/RGB/ARGB()?的顏色,是直接寫在方法的參數(shù)里,通過參數(shù)來設(shè)置的(上期講過了);?drawBitmap()?的顏色,是直接由?Bitmap?對(duì)象來提供的(上期也講過了);除此之外,是圖形和文字的繪制,它們的顏色就需要使用?paint?參數(shù)來額外設(shè)置了(下面要講的)。

Paint?設(shè)置顏色的方法有兩種:一種是直接用?Paint.setColor/ARGB()?來設(shè)置顏色,另一種是使用?Shader?來指定著色方案。

1.1.1 直接設(shè)置顏色

1.1.1.1 setColor(int color)

方法名和使用方法都非常簡單直接,而且這個(gè)方法在上期已經(jīng)介紹過了,不再多說。

paint.setColor(Color.parseColor("#009688")); canvas.drawRect(30, 30, 230, 180, paint);paint.setColor(Color.parseColor("#FF9800")); canvas.drawLine(300, 30, 450, 180, paint);paint.setColor(Color.parseColor("#E91E63")); canvas.drawText("HenCoder", 500, 130, paint);

setColor()?對(duì)應(yīng)的 get 方法是?getColor()

1.1.1.2 setARGB(int a, int r, int g, int b)

其實(shí)和?setColor(color)?都是一樣一樣兒的,只是它的參數(shù)用的是更直接的三原色與透明度的值。實(shí)際運(yùn)用中,setColor()?和?setARGB()?哪個(gè)方便和順手用哪個(gè)吧。

paint.setARGB(100, 255, 0, 0); canvas.drawRect(0, 0, 200, 200, paint); paint.setARGB(100, 0, 0, 0); canvas.drawLine(0, 0, 200, 200, paint);

1.1.2 setShader(Shader shader) 設(shè)置 Shader

除了直接設(shè)置顏色,?Paint?還可以使用?Shader?。

Shader 這個(gè)英文單詞很多人沒有見過,它的中文叫做「著色器」,也是用于設(shè)置繪制顏色的。「著色器」不是 Android 獨(dú)有的,它是圖形領(lǐng)域里一個(gè)通用的概念,它和直接設(shè)置顏色的區(qū)別是,著色器設(shè)置的是一個(gè)顏色方案,或者說是一套著色規(guī)則。當(dāng)設(shè)置了?Shader?之后,Paint?在繪制圖形和文字時(shí)就不使用?setColor/ARGB()?設(shè)置的顏色了,而是使用?Shader?的方案中的顏色。

在 Android 的繪制里使用?Shader?,并不直接用?Shader?這個(gè)類,而是用它的幾個(gè)子類。具體來講有?LinearGradient?RadialGradient?SweepGradient?BitmapShader?ComposeShader?這么幾個(gè):

1.1.2.1 LinearGradient 線性漸變

設(shè)置兩個(gè)點(diǎn)和兩種顏色,以這兩個(gè)點(diǎn)作為端點(diǎn),使用兩種顏色的漸變來繪制顏色。就像這樣:

Shader shader = new LinearGradient(100, 100, 500, 500, Color.parseColor("#E91E63"), Color.parseColor("#2196F3"), Shader.TileMode.CLAMP); paint.setShader(shader);...canvas.drawCircle(300, 300, 200, paint);

設(shè)置了?Shader?之后,繪制出了漸變顏色的圓。(其他形狀以及文字都可以這樣設(shè)置顏色,我只是沒給出圖。)

注意:在設(shè)置了?Shader?的情況下,?Paint.setColor/ARGB()?所設(shè)置的顏色就不再起作用。

構(gòu)造方法:?
LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode tile)。

參數(shù):?
x0?y0?x1?y1:漸變的兩個(gè)端點(diǎn)的位置?
color0?color1?是端點(diǎn)的顏色?
tile:端點(diǎn)范圍之外的著色規(guī)則,類型是?TileMode。TileMode?一共有 3 個(gè)值可選:?CLAMP,?MIRROR和?REPEAT。CLAMP?(夾子模式???算了這個(gè)詞我不會(huì)翻)會(huì)在端點(diǎn)之外延續(xù)端點(diǎn)處的顏色;MIRROR是鏡像模式;REPEAT?是重復(fù)模式。具體的看一下例子就明白。

CLAMP:

MIRROR:

REPEAT:

1.1.2.2 RadialGradient 輻射漸變

輻射漸變很好理解,就是從中心向周圍輻射狀的漸變。大概像這樣:

Shader shader = new RadialGradient(300, 300, 200, Color.parseColor("#E91E63"), Color.parseColor("#2196F3"), Shader.TileMode.CLAMP); paint.setShader(shader);...canvas.drawCircle(300, 300, 200, paint);

構(gòu)造方法:?
RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, TileMode tileMode)。

參數(shù):?
centerX?centerY:輻射中心的坐標(biāo)?
radius:輻射半徑?
centerColor:輻射中心的顏色?
edgeColor:輻射邊緣的顏色?
tileMode:輻射范圍之外的著色模式。

CLAMP:

MIRROR:

REPEAT:

1.1.2.3 SweepGradient 掃描漸變

又是一個(gè)漸變。「掃描漸變」這個(gè)翻譯我也不知道精確不精確。大概是這樣:

Shader shader = new SweepGradient(300, 300, Color.parseColor("#E91E63"), Color.parseColor("#2196F3")); paint.setShader(shader);...canvas.drawCircle(300, 300, 200, paint);

構(gòu)造方法:?
SweepGradient(float cx, float cy, int color0, int color1)

參數(shù):?
cx?cy?:掃描的中心?
color0:掃描的起始顏色?
color1:掃描的終止顏色

1.1.2.4 BitmapShader

用?Bitmap?來著色(終于不是漸變了)。其實(shí)也就是用?Bitmap?的像素來作為圖形或文字的填充。大概像這樣:

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.batman); Shader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); paint.setShader(shader);...canvas.drawCircle(300, 300, 200, paint);

嗯,看著跟?Canvas.drawBitmap()?好像啊?事實(shí)上也是一樣的效果。如果你想繪制圓形的?Bitmap,就別用?drawBitmap()?了,改用?drawCircle()?+?BitmapShader?就可以了(其他形狀同理)。

構(gòu)造方法:?
BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)

參數(shù):?
bitmap:用來做模板的?Bitmap?對(duì)象?
tileX:橫向的?TileMode?
tileY:縱向的?TileMode。

CLAMP:

MIRROR:

REPEAT:

1.1.2.5 ComposeShader 混合著色器

所謂混合,就是把兩個(gè)?Shader?一起使用。

// 第一個(gè) Shader:頭像的 Bitmap Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.batman); Shader shader1 = new BitmapShader(bitmap1, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);// 第二個(gè) Shader:從上到下的線性漸變(由透明到黑色) Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.batman_logo); Shader shader2 = new BitmapShader(bitmap2, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);// ComposeShader:結(jié)合兩個(gè) Shader Shader shader = new ComposeShader(shader1, shader2, PorterDuff.Mode.SRC_OVER); paint.setShader(shader);...canvas.drawCircle(300, 300, 300, paint);

注意:上面這段代碼中我使用了兩個(gè)?BitmapShader?來作為?ComposeShader()?的參數(shù),而?ComposeShader()?在硬件加速下是不支持兩個(gè)相同類型的?Shader?的,所以這里也需要關(guān)閉硬件加速才能看到效果。

構(gòu)造方法:ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)

參數(shù):?
shaderA,?shaderB:兩個(gè)相繼使用的?Shader?
mode: 兩個(gè)?Shader?的疊加模式,即?shaderA?和?shaderB?應(yīng)該怎樣共同繪制。它的類型是?PorterDuff.Mode?。

PorterDuff.Mode

PorterDuff.Mode?是用來指定兩個(gè)圖像共同繪制時(shí)的顏色策略的。它是一個(gè) enum,不同的?Mode?可以指定不同的策略。「顏色策略」的意思,就是說把源圖像繪制到目標(biāo)圖像處時(shí)應(yīng)該怎樣確定二者結(jié)合后的顏色,而對(duì)于?ComposeShader(shaderA, shaderB, mode)?這個(gè)具體的方法,就是指應(yīng)該怎樣把?shaderB?繪制在?shaderA?上來得到一個(gè)結(jié)合后的?Shader。

沒有聽說過?PorterDuff.Mode?的人,看到這里很可能依然會(huì)一頭霧水:「什么怎么結(jié)合?就……兩個(gè)圖像一疊加,結(jié)合唄?還能怎么結(jié)合?」你還別說,還真的是有很多種策略來結(jié)合。

最符合直覺的結(jié)合策略,就是我在上面這個(gè)例子中使用的?Mode:?SRC_OVER。它的算法非常直觀:就像上面圖中的那樣,把源圖像直接鋪在目標(biāo)圖像上。不過,除了這種,其實(shí)還有一些其他的結(jié)合方式。例如如果我把上面例子中的參數(shù)?mode?改為?PorterDuff.Mode.DST_OUT,就會(huì)變成挖空效果:

而如果再把?mode?改為?PorterDuff.Mode.DST_IN,就會(huì)變成蒙版摳圖效果:

這下明白了吧?

具體來說,?PorterDuff.Mode?一共有 17 個(gè),可以分為兩類:

  • Alpha 合成 (Alpha Compositing)
  • 混合 (Blending)
  • 第一類,Alpha 合成,其實(shí)就是 「PorterDuff」 這個(gè)詞所指代的算法。 「PorterDuff」 并不是一個(gè)具有實(shí)際意義的詞組,而是兩個(gè)人的名字(準(zhǔn)確講是姓)。這兩個(gè)人當(dāng)年共同發(fā)表了一篇論文,描述了 12 種將兩個(gè)圖像共同繪制的操作(即算法)。而這篇論文所論述的操作,都是關(guān)于 Alpha 通道(也就是我們通俗理解的「透明度」)的計(jì)算的,后來人們就把這類計(jì)算稱為Alpha 合成?( Alpha Compositing ) 。

    看下效果吧。效果直接盜 Google 的官方文檔了。

    源圖像和目標(biāo)圖像:

    Alpha 合成:

    第二類,混合,也就是 Photoshop 等制圖軟件里都有的那些混合模式(multiply?darken?lighten之類的)。這一類操作的是顏色本身而不是?Alpha?通道,并不屬于?Alpha?合成,所以和 Porter 與 Duff 這兩個(gè)人也沒什么關(guān)系,不過為了使用的方便,它們同樣也被 Google 加進(jìn)了?PorterDuff.Mode里。

    效果依然盜?官方文檔。

    結(jié)論

    從效果圖可以看出,Alpha 合成類的效果都比較直觀,基本上可以使用簡單的口頭表達(dá)來描述它們的算法(起碼對(duì)于不透明的源圖像和目標(biāo)圖像來說是可以的),例如?SRC_OVER?表示「二者都繪制,但要源圖像放在目標(biāo)圖像的上面」,DST_IN?表示「只繪制目標(biāo)圖像,并且只繪制它和源圖像重合的區(qū)域」。

    而混合類的效果就相對(duì)抽象一些,只從效果圖不太能看得出它們的著色算法,更看不出來它們有什么用。不過沒關(guān)系,你如果拿著這些名詞去問你司的設(shè)計(jì)師,他們八成都能給你說出來個(gè) 123。

    所以對(duì)于這些?Mode,正確的做法是:對(duì)于 Alpha 合成類的操作,掌握他們,并在實(shí)際開發(fā)中靈活運(yùn)用;而對(duì)于混合類的,你只要把它們的名字記住就好了,這樣當(dāng)某一天設(shè)計(jì)師告訴你「我要做這種混合效果」的時(shí)候,你可以馬上知道自己能不能做,怎么做。

    另外:PorterDuff.Mode?建議你動(dòng)手用一下試試,對(duì)加深理解有幫助。

    好了,這些就是幾個(gè)?Shader?的具體介紹。

    除了使用?setColor/ARGB()?和?setShader()?來設(shè)置基本顏色,?Paint?還可以來設(shè)置?ColorFilter,來對(duì)顏色進(jìn)行第二層處理。

    1.2 setColorFilter(ColorFilter colorFilter)

    ColorFilter?這個(gè)類,它的名字已經(jīng)足夠解釋它的作用:為繪制設(shè)置顏色過濾。顏色過濾的意思,就是為繪制的內(nèi)容設(shè)置一個(gè)統(tǒng)一的過濾策略,然后?Canvas.drawXXX()?方法會(huì)對(duì)每個(gè)像素都進(jìn)行過濾后再繪制出來。舉幾個(gè)現(xiàn)實(shí)中比較常見的顏色過濾的例子:

    • 有色光照射:

    • 有色玻璃透視:

    • 膠卷:

    在?Paint?里設(shè)置?ColorFilter?,使用的是?Paint.setColorFilter(ColorFilter filter)?方法。?ColorFilter?并不直接使用,而是使用它的子類。它共有三個(gè)子類:LightingColorFilterPorterDuffColorFilter?和?ColorMatrixColorFilter。

    1.2.1 LightingColorFilter

    這個(gè)?LightingColorFilter?是用來模擬簡單的光照效果的。

    LightingColorFilter?的構(gòu)造方法是?LightingColorFilter(int mul, int add)?,參數(shù)里的?mul?和?add?都是和顏色值格式相同的 int 值,其中?mul?用來和目標(biāo)像素相乘,add?用來和目標(biāo)像素相加:

    R' = R * mul.R / 0xff + add.R G' = G * mul.G / 0xff + add.G B' = B * mul.B / 0xff + add.B

    一個(gè)「保持原樣」的「基本?LightingColorFilter?」,mul?為?0xffffff,add?為?0x000000(也就是0),那么對(duì)于一個(gè)像素,它的計(jì)算過程就是:

    R' = R * 0xff / 0xff + 0x0 = R // R' = R G' = G * 0xff / 0xff + 0x0 = G // G' = G B' = B * 0xff / 0xff + 0x0 = B // B' = B

    基于這個(gè)「基本?LightingColorFilter?」,你就可以修改一下做出其他的 filter。比如,如果你想去掉原像素中的紅色,可以把它的?mul?改為?0x00ffff?(紅色部分為 0 ) ,那么它的計(jì)算過程就是:

    R' = R * 0x0 / 0xff + 0x0 = 0 // 紅色被移除 G' = G * 0xff / 0xff + 0x0 = G B' = B * 0xff / 0xff + 0x0 = B

    具體效果是這樣的:

    ColorFilter lightingColorFilter = new LightingColorFilter(0x00ffff, 0x000000); paint.setColorFilter(lightingColorFilter);

    表情忽然變得陰郁了

    或者,如果你想讓它的綠色更亮一些,就可以把它的?add?改為?0x003000?(綠色部分為 0x30 ),那么它的計(jì)算過程就是:

    R' = R * 0xff / 0xff + 0x0 = R G' = G * 0xff / 0xff + 0x30 = G + 0x30 // 綠色被加強(qiáng) B' = B * 0xff / 0xff + 0x0 = B

    效果是這樣:

    ColorFilter lightingColorFilter = new LightingColorFilter(0xffffff, 0x003000); paint.setColorFilter(lightingColorFilter);

    這樣的表情才陽光

    至于怎么修改參數(shù)來模擬你想要的某種具體光照效果,你就別問我了,還是跟你司設(shè)計(jì)師討論吧,這個(gè)我不專業(yè)……

    1.2.2 PorterDuffColorFilter

    這個(gè)?PorterDuffColorFilter?的作用是使用一個(gè)指定的顏色和一種指定的?PorterDuff.Mode?來與繪制對(duì)象進(jìn)行合成。它的構(gòu)造方法是?PorterDuffColorFilter(int color, PorterDuff.Mode mode)?其中的?color?參數(shù)是指定的顏色,?mode?參數(shù)是指定的?Mode。同樣也是?PorterDuff.Mode?,不過和?ComposeShader?不同的是,PorterDuffColorFilter?作為一個(gè)?ColorFilter,只能指定一種顏色作為源,而不是一個(gè)?Bitmap。

    PorterDuff.Mode?前面已經(jīng)講過了,而?PorterDuffColorFilter?本身的使用是非常簡單的,所以不再展開講。

    1.2.3 ColorMatrixColorFilter

    這個(gè)就厲害了。ColorMatrixColorFilter?使用一個(gè)?ColorMatrix?來對(duì)顏色進(jìn)行處理。?ColorMatrix這個(gè)類,內(nèi)部是一個(gè) 4x5 的矩陣:

    [ a, b, c, d, e,f, g, h, i, j,k, l, m, n, o,p, q, r, s, t ]

    通過計(jì)算,?ColorMatrix?可以把要繪制的像素進(jìn)行轉(zhuǎn)換。對(duì)于顏色 [R, G, B, A] ,轉(zhuǎn)換算法是這樣的:

    R’ = a*R + b*G + c*B + d*A + e; G’ = f*R + g*G + h*B + i*A + j; B’ = k*R + l*G + m*B + n*A + o; A’ = p*R + q*G + r*B + s*A + t;

    ColorMatrix?有一些自帶的方法可以做簡單的轉(zhuǎn)換,例如可以使用?setSaturation(float sat)?來設(shè)置飽和度;另外你也可以自己去設(shè)置它的每一個(gè)元素來對(duì)轉(zhuǎn)換效果做精細(xì)調(diào)整。具體怎樣設(shè)置會(huì)有怎樣的效果,我就不講了(其實(shí)是我也不太會(huì)

    總結(jié)

    以上是生活随笔為你收集整理的HenCoder Android 开发进阶: 自定义 View 1-2 Paint 详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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