[Android進(jìn)階]自定義View胶坠,Paint詳解

此文章為HenCoder的備份霞揉,感謝HenCoder這篇文章的作者
Paint的Api大致分為4類

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

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

1质礼、顏色

Canvas繪制的內(nèi)容拣宏,有三層對顏色的處理

image.png

這圖大概看看就行,不用鉆研明白再往下看犬耻,因為等這章講完你就懂了。

1.1 基本顏色

像素的基本顏色执泰,很久繪制內(nèi)容的不同而有不同的控制方式:

  • Canvas的顏色填充類方法drawColor/RGB/ARGB()的顏色枕磁,是直接寫在方法的參數(shù)里,通過參數(shù)類設(shè)置的术吝;
  • drawBitmap()的顏色计济,是直接由Bitmap對象提供的;
  • 圖形和文字的繪制排苍,它們的顏色就需要使用paint參數(shù)來額外設(shè)置了沦寂。
    image.png

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

方法名和使用方法都非常簡單直接传藏,不在多說

paint.setColor(Color.parseColor("#009688"));//抄者注:也可寫成paint.setColor(0xff009688);0xff+#號后的顏色
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);  
image.png

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

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

其實和setColor(color)都是一樣一樣的,只是它的參數(shù)用的是更直接的三原色與透明度的值。實際運用中毯侦,setColorsetARGB哪個方便和順手用哪個吧

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這個英文單詞很多人沒有見過侈离,它的中文名叫 [著色器] 试幽,也是用于繪制顏色的。[著色器]不是Android獨有的卦碾,它是圖形領(lǐng)域的一個通用概念铺坞,它和直接設(shè)置顏色的區(qū)別是,著色器設(shè)置的是一個顏色方案洲胖,或者說是一套著色規(guī)則济榨。當(dāng)設(shè)置了Shader之后,Paint在繪制圖形和文字是就不適用setColor/ARGB設(shè)置的顏色了宾濒,而是使用Shader的方案中的顏色腿短。

在Android的繪制里使用Shader,并不直接使用Shader這個類绘梦,而是用它的幾個子類橘忱。具體來講有LinearGradient,RadialGradient,SweepGradient,BitampGradient,ComposeShader這么幾個:

1.1.2.1 LinearGradient 線性漸變

設(shè)置兩個點和兩種顏色,以這兩個點作為端點卸奉,使用每種顏色的漸變來繪制顏色钝诚。就像這樣:

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);
image.png

設(shè)置了Shader之后,繪制除了漸變顏色的元榄棵。(其他形狀及文字都可以這樣設(shè)置顏色凝颇,我只是沒給出圖)
注意:在設(shè)置了Shader的情況下,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:漸變兩個端點的位置
color0 color1:端點的顏色
tile:端點范圍之外的著色規(guī)則拧略。TileMode一共有3個值可選:CLAMP,MIRROR和REPEAT

  • CLAMP:會在端點之外延續(xù)端點處的顏色;
  • MIRROR :鏡像模式,
  • REPEAT:重復(fù)模式瘪弓,具體的看一下例子就明白

CLAMP:

image

MIRROR:

image

REPEAT:

image
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);  

image

構(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:

image

MIRROR:

image

REPEAT:

image
SweepGradient 掃描漸變

又是一個漸變∏赫迹「掃描漸變」這個翻譯我也不知道精確不精確虑乖。大概是這樣:

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

...

canvas.drawCircle(300, 300, 200, paint);  

image

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

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

1.1.2.4 BitmapShader

Bitmap 來著色(終于不是漸變了)。其實也就是用 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);  

image

嗯疹味,看著跟 Canvas.drawBitmap() 好像敖鼋小?事實上也是一樣的效果佛猛。如果你想繪制圓形的 Bitmap惑芭,就別用 drawBitmap() 了,改用 drawCircle() + BitmapShader 就可以了(其他形狀同理)继找。

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

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

CLAMP:

image

MIRROR:

image

REPEAT:

image
1.1.2.5 ComposeShader 混合著色器

所謂混合,就是把兩個 Shader 一起使用婴渡。

// 第一個 Shader:頭像的 Bitmap
Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.batman);  
Shader shader1 = new BitmapShader(bitmap1, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

// 第二個 Shader:從上到下的線性漸變(由透明到黑色)
Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.batman_logo);  
Shader shader2 = new BitmapShader(bitmap2, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

// ComposeShader:結(jié)合兩個 Shader
Shader shader = new ComposeShader(shader1, shader2, PorterDuff.Mode.SRC_OVER);  
paint.setShader(shader);

...

canvas.drawCircle(300, 300, 300, paint);  

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

image

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

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

PorterDuff.Mode

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

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

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

image

而如果再把 mode 改為 PorterDuff.Mode.DST_IN薄霜,就會變成蒙版摳圖效果:
image

這下明白了吧某抓?

具體來說, PorterDuff.Mode 一共有 17 個惰瓜,可以分為兩類:

  1. Alpha 合成 (Alpha Compositing)
  2. 混合 (Blending)

第一類否副,Alpha 合成,其實就是 「PorterDuff」 這個詞所指代的算法崎坊。 「PorterDuff」 并不是一個具有實際意義的詞組备禀,而是兩個人的名字(準(zhǔn)確講是姓)。這兩個人當(dāng)年共同發(fā)表了一篇論文奈揍,描述了 12 種將兩個圖像共同繪制的操作(即算法)曲尸。而這篇論文所論述的操作,都是關(guān)于 Alpha 通道(也就是我們通俗理解的「透明度」)的計算的男翰,后來人們就把這類計算稱為Alpha 合成 ( Alpha Compositing ) 另患。

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

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

image

Alpha 合成:

image

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

效果依然盜 官方文檔话原。

image
結(jié)論

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

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

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

另外:PorterDuff.Mode 建議你動手用一下試試咏雌,對加深理解有幫助。

好了煞檩,這些就是幾個 Shader 的具體介紹处嫌。

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

1.2 setColorFilter(ColorFilter colorFilter)

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

  • 有色光照射:


    w400
  • 有色玻璃透視:

    w400
  • 膠卷:

    image

在 Paint 里設(shè)置 ColorFilter 捆昏,使用的是 Paint.setColorFilter(ColorFilter filter) 方法。 ColorFilter 并不直接使用毙沾,而是使用它的子類骗卜。它共有三個子類:LightingColorFilter PorterDuffColorFilter 和 ColorMatrixColorFilter。

1.2.1 LightingColorFilter

這個 LightingColorFilter 是用來模擬簡單的光照效果的左胞。

LightingColorFilter 的構(gòu)造方法是 LightingColorFilter(int mul, int add) 寇仓,參數(shù)里的 mul 和 add 都是和顏色值格式相同的 int 值,其中 mul用來和目標(biāo)像素相乘(抄著注烤宙,根據(jù)下面看這里應(yīng)該是除)遍烦,add 用來和目標(biāo)像素相加:

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

一個「保持原樣」的「基本 LightingColorFilter 」,mul 為 0xffffff躺枕,add 為 0x000000(也就是0)服猪,那么對于一個像素,它的計算過程就是:

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

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

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 )备闲,那么它的計算過程就是:

R' = R * 0xff / 0xff + 0x0 = R  
G' = G * 0xff / 0xff + 0x30 = G + 0x30 // 綠色被加強  
B' = B * 0xff / 0xff + 0x0 = B  

效果是這樣:

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

這樣的表情才陽光

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

1.2.2 PorterDuffColorFilter

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

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

1.2.3 ColorMatrixColorFilter

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

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

通過計算兆沙, ColorMatrix 可以把要繪制的像素進(jìn)行轉(zhuǎn)換。對于顏色 [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è)置它的每一個元素來對轉(zhuǎn)換效果做精細(xì)調(diào)整憎妙。具體怎樣設(shè)置會有怎樣的效果库正,我就不講了(其實是我也不太會??)。如果你有需求尚氛,可以試一下程大治同學(xué)做的這個庫:StyleImageView

image

以上诀诊,就是 Paint 對顏色的第二層處理:通過 setColorFilter(colorFilter) 來加工顏色。

除了基本顏色的設(shè)置( setColor/ARGB(), setShader() )以及基于原始顏色的過濾( setColorFilter() )之外阅嘶,Paint 最后一層處理顏色的方法是 setXfermode(Xfermode xfermode) 属瓣,它處理的是「當(dāng)顏色遇上 View」的問題。

1.3 setXfermode(Xfermode xfermode)

"Xfermode" 其實就是 "Transfer mode"讯柔,用 "X" 來代替 "Trans" 是一些美國人喜歡用的簡寫方式抡蛙。嚴(yán)謹(jǐn)?shù)刂v, Xfermode 指的是你要繪制的內(nèi)容和 Canvas 的目標(biāo)位置的內(nèi)容應(yīng)該怎樣結(jié)合計算出最終的顏色魂迄。但通俗地說粗截,其實就是要你以繪制的內(nèi)容作為源圖像,以 View 中已有的內(nèi)容作為目標(biāo)圖像,選取一個 PorterDuff.Mode 作為繪制內(nèi)容的顏色處理方案台妆。就像這樣:

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

image

又是 PorterDuff.ModePorterDuff.ModePaint 一共有三處 API 胳徽,它們的工作原理都一樣婿屹,只是用途不同:

image

另外灭美,從上面的示例代碼可以看出,創(chuàng)建 Xfermode 的時候其實是創(chuàng)建的它的子類 PorterDuffXfermode昂利。而事實上届腐,Xfermode 也只有這一個子類。所以在設(shè)置 Xfermode 的時候不用多想蜂奸,直接用 PorterDuffXfermode吧犁苏。

「只有一個子類?扩所?围详?什么設(shè)計?」


其實在更早的 Android 版本中祖屏,Xfermode 還有別的子類短曾,但別的子類現(xiàn)在已經(jīng) deprecated 了,如今只剩下了 PorterDuffXfermode赐劣。所以目前它的使用看起來好像有點啰嗦嫉拐,但其實是由于歷史遺留問題。

Xfermode 注意事項

Xfermode 使用很簡單魁兼,不過有兩點需要注意:

1. 使用離屏緩沖(Off-screen Buffer)

實質(zhì)上婉徘,上面這段例子代碼,如果直接執(zhí)行的話是不會繪制出圖中效果的咐汞,程序的繪制也不會像上面的動畫那樣執(zhí)行盖呼,而是會像這樣:

image

為什么會這樣?

按照邏輯我們會認(rèn)為化撕,在第二步畫圓的時候几晤,跟它共同計算的是第一步繪制的方形。但實際上植阴,卻是整個 View 的顯示區(qū)域都在畫圓的時候參與計算蟹瘾,并且 View 自身的底色并不是默認(rèn)的透明色,而且是遵循一種迷之邏輯掠手,導(dǎo)致不僅繪制的是整個圓的范圍憾朴,而且在范圍之外都變成了黑色。就像這樣:

image

這……那可如何是好喷鸽?

要想使用 setXfermode() 正常繪制众雷,必須使用離屏緩存 (Off-screen Buffer) 把內(nèi)容繪制在額外的層上,再把繪制好的內(nèi)容貼回 View 中。也就是這樣:

image

通過使用離屏緩沖砾省,把要繪制的內(nèi)容單獨繪制在緩沖層鸡岗, Xfermode 的使用就不會出現(xiàn)奇怪的結(jié)果了。使用離屏緩沖有兩種方式:

  • Canvas.saveLayer()

    saveLayer() 可以做短時的離屏緩沖编兄。使用方法很簡單纤房,在繪制代碼的前后各加一行代碼,在繪制之前保存翻诉,繪制之后恢復(fù):

    int saved = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);
    
    canvas.drawBitmap(rectBitmap, 0, 0, paint); // 畫方
    paint.setXfermode(xfermode); // 設(shè)置 Xfermode
    canvas.drawBitmap(circleBitmap, 0, 0, paint); // 畫圓
    paint.setXfermode(null); // 用完及時清除 Xfermode
    
    canvas.restoreToCount(saved);
    
    
  • View.setLayerType()

    View.setLayerType() 是直接把整個 View 都繪制在離屏緩沖中。 setLayerType(LAYER_TYPE_HARDWARE) 是使用 GPU 來緩沖捌刮, setLayerType(LAYER_TYPE_SOFTWARE) 是直接直接用一個 Bitmap 來緩沖碰煌。

如果沒有特殊需求,可以選用第一種方法 Canvas.saveLayer() 來設(shè)置離屏緩沖绅作,以此來獲得更高的性能芦圾。更多關(guān)于離屏緩沖的信息,可以看官方文檔中對于硬件加速的介紹俄认。

2. 控制好透明區(qū)域

使用 Xfermode 來繪制的內(nèi)容个少,除了注意使用離屏緩沖,還應(yīng)該注意控制它的透明區(qū)域不要太小眯杏,要讓它足夠覆蓋到要和它結(jié)合繪制的內(nèi)容夜焦,否則得到的結(jié)果很可能不是你想要的。我用圖片來具體說明一下:

image

如圖所示岂贩,由于透明區(qū)域過小而覆蓋不到的地方茫经,將不會受到 Xfermode 的影響。

好萎津,到此為止卸伞,前面講的就是 Paint 的第一類 API——關(guān)于顏色的三層設(shè)置:直接設(shè)置顏色的 API 用來給圖形和文字設(shè)置顏色; setColorFilter() 用來基于顏色進(jìn)行過濾處理锉屈; setXfermode() 用來處理源圖像和 View 已有內(nèi)容的關(guān)系荤傲。

再貼一次本章開始處的圖作為回顧:

image

2 效果

效果類的 API ,指的就是抗鋸齒颈渊、填充/輪廓遂黍、線條寬度等等這些。

2.1 setAntiAlias (boolean aa) 設(shè)置抗鋸齒

抗鋸齒在上一節(jié)已經(jīng)講過了俊嗽,話不多說妓湘,直接上圖:

image

抗鋸齒默認(rèn)是關(guān)閉的,如果需要抗鋸齒乌询,需要顯式地打開榜贴。另外,除了 setAntiAlias(aa) 方法,打開抗鋸齒還有一個更方便的方式:構(gòu)造方法唬党。創(chuàng)建 Paint 對象的時候鹃共,構(gòu)造方法的參數(shù)里加一個 ANTI_ALIAS_FLAG的 flag,就可以在初始化的時候就開啟抗鋸齒驶拱。

Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);  

2.2 setStyle(Paint.Style style)

setStyle(style) 也在上一節(jié)講過了霜浴,用來設(shè)置圖形是線條風(fēng)格還是填充風(fēng)格的(也可以二者并用):

paint.setStyle(Paint.Style.FILL); // FILL 模式,填充  
canvas.drawCircle(300, 300, 200, paint);  

image
paint.setStyle(Paint.Style.STROKE); // STROKE 模式蓝纲,畫線  
canvas.drawCircle(300, 300, 200, paint);  

image
paint.setStyle(Paint.Style.FILL_AND_STROKE); // FILL_AND_STROKE 模式阴孟,填充 + 畫線  
canvas.drawCircle(300, 300, 200, paint);  

image

FILL 模式是默認(rèn)模式,所以如果之前沒有設(shè)置過其他的 Style税迷,可以不用 setStyle(Paint.Style.FILL) 這句永丝。

2.3 線條形狀

設(shè)置線條形狀的一共有 4 個方法:setStrokeWidth(float width), setStrokeCap(Paint.Cap cap), setStrokeJoin(Paint.Join join), setStrokeMiter(float miter)

2.3.1 setStrokeWidth(float width)

設(shè)置線條寬度箭养。單位為像素慕嚷,默認(rèn)值是 0。

paint.setStyle(Paint.Style.STROKE);  
paint.setStrokeWidth(1);  
canvas.drawCircle(150, 125, 100, paint);  
paint.setStrokeWidth(5);  
canvas.drawCircle(400, 125, 100, paint);  
paint.setStrokeWidth(40);  
canvas.drawCircle(650, 125, 100, paint);  

image

線條寬度 0 和 1 的區(qū)別

默認(rèn)情況下毕泌,線條寬度為 0喝检,但你會發(fā)現(xiàn),這個時候它依然能夠畫出線撼泛,線條的寬度為 1 像素挠说。那么它和線條寬度為 1 有什么區(qū)別呢?

其實這個和后面要講的一個「幾何變換」有關(guān):你可以為 Canvas 設(shè)置 Matrix 來實現(xiàn)幾何變換(如放大愿题、縮小纺涤、平移、旋轉(zhuǎn))抠忘,在幾何變換之后 Canvas 繪制的內(nèi)容就會發(fā)生相應(yīng)變化撩炊,包括線條也會加粗,例如 2 像素寬度的線條在 Canvas 放大 2 倍后會被以 4 像素寬度來繪制崎脉。而當(dāng)線條寬度被設(shè)置為 0 時拧咳,它的寬度就被固定為 1 像素,就算 Canvas 通過幾何變換被放大囚灼,它也依然會被以 1 像素寬度來繪制骆膝。Google 在文檔中把線條寬度為 0 時稱作「hairline mode(發(fā)際線模式)」。

2.3.2 setStrokeCap(Paint.Cap cap)

設(shè)置線頭的形狀灶体。線頭形狀有三種:BUTT 平頭阅签、ROUND 圓頭、SQUARE 方頭蝎抽。默認(rèn)為 BUTT政钟。

放出「平頭」「圓頭」「方頭」這種翻譯我始終有點糾結(jié):既覺得自己翻譯得簡潔清晰盡顯機智,同時又擔(dān)心用詞會不會有點太過通俗,讓人覺得我不夠高貴冷艷养交?

當(dāng)線條的寬度是 1 像素時精算,這三種線頭的表現(xiàn)是完全一致的,全是 1 個像素的點碎连;而當(dāng)線條變粗的時候灰羽,它們就會表現(xiàn)出不同的樣子:

image

虛線是額外加的,虛線左邊是線的實際長度鱼辙,虛線右邊是線頭廉嚼。有了虛線作為輔助,可以清楚地看出 BUTTSQUARE 的區(qū)別倒戏。

2.3.3 setStrokeJoin(Paint.Join join)

設(shè)置拐角的形狀怠噪。有三個值可以選擇:MITER 尖角、 BEVEL 平角和 ROUND 圓角峭梳。默認(rèn)為 MITER

image

輔助理解:

MITER 在現(xiàn)實中其實就是這玩意:

image

而 BEVEL 是這玩意:

image

2.3.4 setStrokeMiter(float miter)

這個方法是對于 setStrokeJoin() 的一個補充蹂喻,它用于設(shè)置 MITER 型拐角的延長線的最大值葱椭。所謂「延長線的最大值」,是這么一回事:

當(dāng)線條拐角為 MITER 時口四,拐角處的外緣需要使用延長線來補償:

image

而這種補償方案會有一個問題:如果拐角的角度太小孵运,就有可能由于出現(xiàn)連接點過長的情況。比如這樣:

image

所以為了避免意料之外的過長的尖角出現(xiàn)蔓彩, MITER 型連接點有一個額外的規(guī)則:當(dāng)尖角過長時治笨,自動改用 BEVEL 的方式來渲染連接點。例如上圖的這個尖角赤嚼,在默認(rèn)情況下是不會出現(xiàn)的旷赖,而是會由于延長線過長而被轉(zhuǎn)為 BEVEL 型連接點:

image

至于多尖的角屬于過于尖,尖到需要轉(zhuǎn)為使用 BEVEL 來繪制更卒,則是由一個屬性控制的等孵,而這個屬性就是 setStrokeMiter(miter) 方法中的 miter 參數(shù)。miter 參數(shù)是對于轉(zhuǎn)角長度的限制蹂空,具體來講俯萌,是指尖角的外緣端點和內(nèi)部拐角的距離與線條寬度的比。也就是下面這兩個長度的比:

image

用幾何知識很容易得出這個比值的計算公式:如果拐角的大小為 θ 上枕,那么這個比值就等于 1 / sin ( θ / 2 ) 咐熙。

這個 miter limit 的默認(rèn)值是 4,對應(yīng)的是一個大約 29° 的銳角:

image

默認(rèn)情況下辨萍,大于這個角的尖角會被保留棋恼,而小于這個夾角的就會被「削成平頭」

所以,這個方法雖然名叫 setStrokeMiter(miter) ,但它其實設(shè)置的是「 線條在 Join 類型為 MITER 時對于 MITER 的長度限制」蘸泻。它的這個名字雖然短琉苇,但卻存在一定的迷惑性,如果叫 setStrokeJoinMiterLimit(limit) 就更準(zhǔn)確了悦施。 Google 的工程師沒有這么給它命名并扇,大概也是不想傷害大家的手指吧,畢竟程序員何苦為難程序員抡诞。

image

以上就是 4 個關(guān)于線條形狀的方法: setStrokeWidth(width) setStrokeCap(cap) setStrokeJoint(join)setStrokeMiter(miter)穷蛹。

2.4 色彩優(yōu)化

Paint 的色彩優(yōu)化有兩個方法: setDither(boolean dither)setFilterBitmap(boolean filter) 。它們的作用都是讓畫面顏色變得更加「順眼」昼汗,但原理和使用場景是不同的肴熏。

2.4.1 setDither(boolean dither)

設(shè)置圖像的抖動。

在介紹抖動之前顷窒,先來看一個猥瑣男:

image

注意毛利小五郎臉上的紅暈蛙吏,它們并不是使用一片淡紅色涂抹出來的,而是畫了三道深色的紅線鞋吉。這三道深色紅線放在臉上鸦做,給人的視覺效果就成了「淡淡的紅暈」。

抖動的原理和這個類似谓着。所謂抖動(注意泼诱,它就叫抖動,不是防抖動赊锚,也不是去抖動治筒,有些人在翻譯的時候自作主張地加了一個「防」字或者「去」字,這是不對的)舷蒲,是指把圖像從較高色彩深度(即可用的顏色數(shù))向較低色彩深度的區(qū)域繪制時耸袜,在圖像中有意地插入噪點,通過有規(guī)律地擾亂圖像來讓圖像對于肉眼更加真實的做法牲平。

比如向 1 位色彩深度的區(qū)域中繪制灰色句灌,由于 1 位深度只包含黑和白兩種顏色,在默認(rèn)情況下欠拾,即不加抖動的時候胰锌,只能選擇向上或向下選擇最接近灰色的白色或黑色來繪制,那么顯示出來也只能是一片白或者一片黑藐窄。而加了抖動后资昧,就可以繪制出讓肉眼識別為灰色的效果了:

image

瞧,像上面這樣荆忍,用黑白相間的方式來繪制格带,就可以騙過肉眼撤缴,讓肉眼辨別為灰色了。

嗯叽唱?你說你看不出灰色屈呕,只看出黑白相間?沒關(guān)系棺亭,那是因為像素顆粒太大虎眨,我把像素顆粒縮小镶摘,看到完整效果你就會發(fā)現(xiàn)變灰了:

image

這下變灰了吧嗽桩?

什么,還沒有變灰凄敢?那一定是你看圖的姿勢不對了碌冶。

image

不過,抖動可不只可以用在純色的繪制涝缝。在實際的應(yīng)用場景中扑庞,抖動更多的作用是在圖像降低色彩深度繪制時,避免出現(xiàn)大片的色帶與色塊拒逮。效果盜一下維基百科的圖:

image

看著很牛逼對吧罐氨?確實很牛逼,而且在 Android 里使用起來也很簡單消恍,一行代碼就搞定:

paint.setDither(true);  

只要加這么一行代碼岂昭,之后的繪制就是加抖動的了以现。

不過對于現(xiàn)在(2017年)而言狠怨, setDither(dither) 已經(jīng)沒有當(dāng)年那么實用了,因為現(xiàn)在的 Android 版本的繪制邑遏,默認(rèn)的色彩深度已經(jīng)是 32 位的 ARGB_8888 佣赖,效果已經(jīng)足夠清晰了。只有當(dāng)你向自建的 Bitmap 中繪制记盒,并且選擇 16 位色的 ARGB_4444 或者 RGB_565 的時候憎蛤,開啟它才會有比較明顯的效果。

2.4.2 setFilterBitmap(boolean filter)

設(shè)置是否使用雙線性過濾來繪制 Bitmap 纪吮。

圖像在放大繪制的時候俩檬,默認(rèn)使用的是最近鄰插值過濾,這種算法簡單碾盟,但會出現(xiàn)馬賽克現(xiàn)象棚辽;而如果開啟了雙線性過濾,就可以讓結(jié)果圖像顯得更加平滑冰肴。效果依然盜維基百科的圖:

![p://upload-images.jianshu.io/upload_images/7093694-bfca697c0a23d46f.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

牛逼吧屈藐?而且它的使用同樣也很簡單:

paint.setFilterBitmap(true);  

加上這一行榔组,在放大繪制 Bitmap 的時候就會使用雙線性過濾了。

以上就是 Paint 的兩個色彩優(yōu)化的方法: setDither(dither) 联逻,設(shè)置抖動來優(yōu)化色彩深度降低時的繪制效果搓扯; setFilterBitmap(filterBitmap) ,設(shè)置雙線性過濾來優(yōu)化 Bitmap 放大繪制的效果包归。

2.5 setPathEffect(PathEffect effect)

使用 PathEffect 來給圖形的輪廓設(shè)置效果锨推。對 Canvas 所有的圖形繪制有效,也就是 drawLine()``drawCircle() drawPath() 這些方法箫踩。大概像這樣:

PathEffect pathEffect = new DashPathEffect(new float[]{10, 5}, 10);  
paint.setPathEffect(pathEffect);

...

canvas.drawCircle(300, 300, 200, paint);  

image

下面就具體說一下 Android 中的 6 種 PathEffect爱态。PathEffect 分為兩類,單一效果的 CornerPathEffect``DiscretePathEffect DashPathEffect PathDashPathEffect 境钟,和組合效果的 SumPathEffect ComposePathEffect锦担。

2.5.1 CornerPathEffect

把所有拐角變成圓角。

PathEffect pathEffect = new CornerPathEffect(20);  
paint.setPathEffect(pathEffect);

...

canvas.drawPath(path, paint);  

image

它的構(gòu)造方法 CornerPathEffect(float radius) 的參數(shù) radius 是圓角的半徑慨削。

2.5.2 DiscretePathEffect

把線條進(jìn)行隨機的偏離洞渔,讓輪廓變得亂七八糟。亂七八糟的方式和程度由參數(shù)決定缚态。

PathEffect pathEffect = new DiscretePathEffect(20, 5);  
paint.setPathEffect(pathEffect);

...

canvas.drawPath(path, paint);  

image

DiscretePathEffect 具體的做法是磁椒,把繪制改為使用定長的線段來拼接,并且在拼接的時候?qū)β窂竭M(jìn)行隨機偏離玫芦。它的構(gòu)造方法 DiscretePathEffect(float segmentLength, float deviation) 的兩個參數(shù)中浆熔, segmentLength 是用來拼接的每個線段的長度, deviation 是偏離量桥帆。這兩個值設(shè)置得不一樣医增,顯示效果也會不一樣,具體的你自己多試幾次就明白了老虫,這里不再貼更多的圖叶骨。

2.5.3 DashPathEffect

使用虛線來繪制線條。

PathEffect pathEffect = new DashPathEffect(new float[]{20, 10, 5, 10}, 0);  
paint.setPathEffect(pathEffect);

...

canvas.drawPath(path, paint);  

image

它的構(gòu)造方法 DashPathEffect(float[] intervals, float phase) 中祈匙, 第一個參數(shù) intervals 是一個數(shù)組忽刽,它指定了虛線的格式:數(shù)組中元素必須為偶數(shù)(最少是 2 個),按照「畫線長度夺欲、空白長度跪帝、畫線長度、空白長度」……的順序排列些阅,例如上面代碼中的 20, 5, 10, 5 就表示虛線是按照「畫 20 像素伞剑、空 5 像素、畫 10 像素扑眉、空 5 像素」的模式來繪制纸泄;第二個參數(shù) phase 是虛線的偏移量赖钞。

2.5.4 PathDashPathEffect

這個方法比 DashPathEffect 多一個前綴 Path ,所以顧名思義聘裁,它是使用一個 Path 來繪制「虛線」雪营。具體看圖吧:

Path dashPath = ...; // 使用一個三角形來做 dash  
PathEffect pathEffect = new PathDashPathEffect(dashPath, 40, 0,  
        PathDashPathEffectStyle.TRANSLATE);
paint.setPathEffect(pathEffect);

...

canvas.drawPath(path, paint);  

image

它的構(gòu)造方法 PathDashPathEffect(Path shape, float advance, float phase, PathDashPathEffect.Style style)中, shape 參數(shù)是用來繪制的 Path 衡便; advance 是兩個相鄰的 shape 段之間的間隔献起,不過注意,這個間隔是兩個 shape 段的起點的間隔镣陕,而不是前一個的終點和后一個的起點的距離谴餐; phaseDashPathEffect 中一樣,是虛線的偏移呆抑;最后一個參數(shù) style岂嗓,是用來指定拐彎改變的時候 shape 的轉(zhuǎn)換方式。style 的類型為 PathDashPathEffect.Style 鹊碍,是一個 enum 厌殉,具體有三個值:

  • TRANSLATE:位移
  • ROTATE:旋轉(zhuǎn)
  • MORPH:變體
image

2.5.5 SumPathEffect

這是一個組合效果類的 PathEffect 。它的行為特別簡單侈咕,就是分別按照兩種 PathEffect 分別對目標(biāo)進(jìn)行繪制公罕。

PathEffect dashEffect = new DashPathEffect(new float[]{20, 10}, 0);  
PathEffect discreteEffect = new DiscretePathEffect(20, 5);  
pathEffect = new SumPathEffect(dashEffect, discreteEffect);

...

canvas.drawPath(path, paint);  

image

2.5.6 ComposePathEffect

這也是一個組合效果類的 PathEffect 。不過它是先對目標(biāo) Path 使用一個 PathEffect耀销,然后再對這個改變后的 Path 使用另一個 PathEffect楼眷。

PathEffect dashEffect = new DashPathEffect(new float[]{20, 10}, 0);  
PathEffect discreteEffect = new DiscretePathEffect(20, 5);  
pathEffect = new ComposePathEffect(dashEffect, discreteEffect);

...

canvas.drawPath(path, paint);  

image

它的構(gòu)造方法 ComposePathEffect(PathEffect outerpe, PathEffect innerpe) 中的兩個 PathEffect 參數(shù), innerpe 是先應(yīng)用的熊尉, outerpe 是后應(yīng)用的罐柳。所以上面的代碼就是「先偏離,再變虛線」帽揪。而如果把兩個參數(shù)調(diào)換硝清,就成了「先變虛線辅斟,再偏離」转晰。至于具體的視覺效果……我就不貼圖了,你自己試試看吧士飒!

上面這些就是 Paint 中的 6 種 PathEffect查邢。它們有的是有獨立效果的,有的是用來組合不同的 PathEffect的酵幕,功能各不一樣扰藕。

注意: PathEffect 在有些情況下不支持硬件加速,需要關(guān)閉硬件加速才能正常使用:

  1. Canvas.drawLine()Canvas.drawLines() 方法畫直線時芳撒,setPathEffect() 是不支持硬件加速的邓深;
  2. PathDashPathEffect 對硬件加速的支持也有問題未桥,所以當(dāng)使用 PathDashPathEffect 的時候,最好也把硬件加速關(guān)了芥备。

剩下的兩個效果類方法:setShadowLayer()setMaskFilter() 冬耿,它們和前面的效果類方法有點不一樣:它們設(shè)置的是「附加效果」,也就是基于在繪制內(nèi)容的額外效果萌壳。

2.6 setShadowLayer(float radius, float dx, float dy, int shadowColor)

在之后的繪制內(nèi)容下面加一層陰影亦镶。

paint.setShadowLayer(10, 0, 0, Color.RED);

...

canvas.drawText(text, 80, 300, paint);  

image

效果就是上面這樣。方法的參數(shù)里煤搜, radius 是陰影的模糊范圍已烤; dx dy 是陰影的偏移量上鞠; shadowColor 是陰影的顏色。

如果要清除陰影層绊起,使用 clearShadowLayer()

注意:

  • 在硬件加速開啟的情況下燎斩, setShadowLayer() 只支持文字的繪制勒庄,文字之外的繪制必須關(guān)閉硬件加速才能正常繪制陰影。

  • 如果 shadowColor 是半透明的瘫里,陰影的透明度就使用 shadowColor 自己的透明度实蔽;而如果 shadowColor是不透明的,陰影的透明度就使用 paint 的透明度谨读。

2.7 setMaskFilter(MaskFilter maskfilter)

為之后的繪制設(shè)置 MaskFilter局装。上一個方法 setShadowLayer() 是設(shè)置的在繪制層下方的附加效果;而這個 MaskFilter 和它相反劳殖,設(shè)置的是在繪制層上方的附加效果铐尚。

到現(xiàn)在已經(jīng)有兩個 setXxxFilter(filter) 了。前面有一個 setColorFilter(filter) 哆姻,是對每個像素的顏色進(jìn)行過濾宣增;而這里的 setMaskFilter(filter) 則是基于整個畫面來進(jìn)行過濾。

MaskFilter 有兩種: BlurMaskFilterEmbossMaskFilter矛缨。

2.7.1 BlurMaskFilter

模糊效果的 MaskFilter爹脾。

paint.setMaskFilter(new BlurMaskFilter(50, BlurMaskFilter.Blur.NORMAL));

...

canvas.drawBitmap(bitmap, 100, 100, paint);  

image

它的構(gòu)造方法 BlurMaskFilter(float radius, BlurMaskFilter.Blur style) 中, radius 參數(shù)是模糊的范圍箕昭, style 是模糊的類型灵妨。一共有四種:

  • NORMAL: 內(nèi)外都模糊繪制
  • SOLID: 內(nèi)部正常繪制,外部模糊
  • INNER: 內(nèi)部模糊落竹,外部不繪制
  • OUTER: 內(nèi)部不繪制泌霍,外部模糊(什么鬼?)
image

2.7.2 EmbossMaskFilter

浮雕效果的 MaskFilter述召。

paint.setMaskFilter(new EmbossMaskFilter(new float[]{0, 1, 1}, 0.2f, 8, 10));

...

canvas.drawBitmap(bitmap, 100, 100, paint);  

image

它的構(gòu)造方法 EmbossMaskFilter(float[] direction, float ambient, float specular, float blurRadius) 的參數(shù)里朱转, direction 是一個 3 個元素的數(shù)組蟹地,指定了光源的方向; ambient 是環(huán)境光的強度藤为,數(shù)值范圍是 0 到 1锈津; specular 是炫光的系數(shù); blurRadius 是應(yīng)用光線的范圍凉蜂。

不過由于我沒有在項目中使用過 EmbossMaskFilter琼梆,對它的每個參數(shù)具體調(diào)節(jié)方式并不熟,你有興趣的話自己研究一下吧窿吩。

2.8 獲取繪制的 Path

這是效果類的最后一組方法茎杂,也是效果類唯一的一組 get 方法。

這組方法做的事是纫雁,根據(jù) paint 的設(shè)置煌往,計算出繪制 Path 或文字時的實際 Path

這里你可能會冒出兩個問題:

  1. 什么叫「實際 Path」轧邪? Path 就是 Path刽脖,這加上個「實際」是什么意思?
  2. 文字的 Path 忌愚?文字還有 Path曲管?

這兩個問題(咦好像有四個問號)的答案就在后面的內(nèi)容里。

2.8.1 getFillPath(Path src, Path dst)

首先解答第一個問題:「實際 Path」硕糊。所謂實際 Path 院水,指的就是 drawPath() 的繪制內(nèi)容的輪廓,要算上線條寬度和設(shè)置的 PathEffect简十。

默認(rèn)情況下(線條寬度為 0檬某、沒有 PathEffect),原 Path 和實際 Path 是一樣的螟蝙;而在線條寬度不為 0 (并且模式為 STROKE 模式或 FLL_AND_STROKE )恢恼,或者設(shè)置了 PathEffect 的時候,實際 Path 就和原 Path不一樣了:

image

看明白了嗎胰默?

通過 getFillPath(src, dst) 方法就能獲取這個實際 Path场斑。方法的參數(shù)里,src 是原 Path 初坠,而 dst 就是實際 Path 的保存位置和簸。 getFillPath(src, dst) 會計算出實際 Path彭雾,然后把結(jié)果保存在 dst 里碟刺。

2.8.2 getTextPath(String text, int start, int end, float x, float y, Path path) / getTextPath(char[] text, int index, int count, float x, float y, Path path)

這里就回答第二個問題:「文字的 Path」。文字的繪制薯酝,雖然是使用 Canvas.drawText() 方法半沽,但其實在下層爽柒,文字信息全是被轉(zhuǎn)化成圖形,對圖形進(jìn)行繪制的者填。 getTextPath() 方法浩村,獲取的就是目標(biāo)文字所對應(yīng)的 Path 。這個就是所謂「文字的 Path」占哟。

image

這兩個方法心墅, getFillPath()getTextPath() ,就是獲取繪制的 Path 的方法榨乎。之所以把它們歸類到「效果」類方法怎燥,是因為它們主要是用于圖形和文字的裝飾效果的位置計算,比如自定義的下劃線效果蜜暑。

image

到此為止铐姚, Paint 的第二類方法——效果類,就也介紹完了肛捍。

3 drawText() 相關(guān)

Paint 有些設(shè)置是文字繪制相關(guān)的隐绵,即和 drawText() 相關(guān)的。

比如設(shè)置文字大凶竞痢:

image

比如設(shè)置文字間隔:

image

比如設(shè)置各種文字效果:

image

除此之外依许,Paint 還有很多與文字繪制相關(guān)的設(shè)置或計算的方法,非常詳細(xì)缀蹄。不過由于太詳細(xì)了悍手,相關(guān)方法太多了(Paint 超過一半的方法都是 drawText() 相關(guān)的,算不算多袍患?)坦康,如果放在這里講它們的話,內(nèi)容會顯得有點過量诡延。所以這一節(jié)我就不講它們了滞欠,把它們放在下一節(jié)里單獨講。

4 初始化類

這一類方法很簡單肆良,它們是用來初始化 Paint 對象筛璧,或者是批量設(shè)置 Paint 的多個屬性的方法。

4.1 reset()

重置 Paint 的所有屬性為默認(rèn)值惹恃。相當(dāng)于重新 new 一個夭谤,不過性能當(dāng)然高一些啦。

4.2 set(Paint src)

src 的所有屬性全部復(fù)制過來巫糙。相當(dāng)于調(diào)用 src 所有的 get 方法朗儒,然后調(diào)用這個 Paint 的對應(yīng)的 set方法來設(shè)置它們。

4.3 setFlags(int flags)

批量設(shè)置 flags。相當(dāng)于依次調(diào)用它們的 set 方法醉锄。例如: ???

paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);  

這行代碼乏悄,和下面這兩行是等價的:

paint.setAntiAlias(true);  
paint.setDither(true);  

setFlags(flags) 對應(yīng)的 get 方法是 int getFlags()

好了恳不,這些就是 Paint 的四類方法:顏色類檩小、效果類文字繪制相關(guān)以及初始化類烟勋。其中顏色類规求、效果類和初始化類都已經(jīng)在這節(jié)里面講過了,剩下的一類——文字繪制類卵惦,下一節(jié)單獨講颓哮。

最后再強調(diào)一遍:這期的內(nèi)容沒必要全部背會,只要看懂鸵荠、理解冕茅,記住有這么個東西就行了。以后在用到的時候蛹找,再拐回來翻一翻就行了姨伤。

練習(xí)項目

為了避免轉(zhuǎn)頭就忘,強烈建議你趁熱打鐵庸疾,做一下這個練習(xí)項目:HenCoderPracticeDraw2

image

下期預(yù)告

下期是文字繪制專場乍楚,我將會花一整期的篇幅來詳述文字的繪制。慣例放出部分配圖作為預(yù)覽:

image
image
image
image

抄者注:以上內(nèi)容皆轉(zhuǎn)載自:HenCoder届慈,刪除了一些不必要的信息徒溪。因為覺得文章寫得很好,很詳細(xì)金顿,所以轉(zhuǎn)載抄錄臊泌。若無意中構(gòu)成了侵權(quán),請作者聯(lián)系我刪除

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末揍拆,一起剝皮案震驚了整個濱河市渠概,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嫂拴,老刑警劉巖播揪,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異筒狠,居然都是意外死亡猪狈,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門辩恼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來雇庙,“玉大人谓形,你說我怎么就攤上這事∽垂玻” “怎么了套耕?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵谁帕,是天一觀的道長峡继。 經(jīng)常有香客問我,道長匈挖,這世上最難降的妖魔是什么碾牌? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮儡循,結(jié)果婚禮上舶吗,老公的妹妹穿的比我還像新娘。我一直安慰自己择膝,他們只是感情好誓琼,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著肴捉,像睡著了一般腹侣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上齿穗,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天傲隶,我揣著相機與錄音,去河邊找鬼窃页。 笑死跺株,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的脖卖。 我是一名探鬼主播乒省,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼畦木!你這毒婦竟也來了作儿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤馋劈,失蹤者是張志新(化名)和其女友劉穎攻锰,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體妓雾,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡娶吞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了械姻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片妒蛇。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡机断,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出绣夺,到底是詐尸還是另有隱情吏奸,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布陶耍,位于F島的核電站奋蔚,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏烈钞。R本人自食惡果不足惜泊碑,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望毯欣。 院中可真熱鬧馒过,春花似錦、人聲如沸酗钞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽砚作。三九已至窘奏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間偎巢,已是汗流浹背蔼夜。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留压昼,地道東北人求冷。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像窍霞,于是被迫代替她去往敵國和親匠题。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

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

  • 本文是自定義View的第二篇但金,主要學(xué)習(xí):Paint第一篇地址Android自定義View(一) -- 初識 本文計...
    T9的第三個三角閱讀 3,986評論 2 26
  • 系列文章之 Android中自定義View(一)系列文章之 Android中自定義View(二)系列文章之 And...
    YoungerDev閱讀 2,159評論 0 4
  • Paint 屬性分類 顏色 效果 drawText() 相關(guān) 初始化 1 顏色 Canvas 繪制的內(nèi)容韭山,有三層...
    黑色海鷗閱讀 550評論 0 8
  • 自定義繪制 自定義繪制的方式是重寫繪制方法,其中最常用的是 onDraw() 繪制的關(guān)鍵是 Canvas 的使用C...
    android小菜雞一枚閱讀 772評論 1 1
  • 早晨象往常一樣到辦公室冷溃。給熱水瓶打上熱水钱磅,拿出面盆接了自來水,然后去拿我的抹布似枕「堑可是,窗臺邊上的抹布凿歼,不見了褪迟。 我...
    野言閱讀 761評論 1 5