Android 中自定義View(二)

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

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

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

自定義View分為以下幾個部分

  • Canvas 的 drawXXX() 系列方法及 Paint 最常見的使用
  • Paint 的完全攻略
  • Canvas 對繪制的輔助——范圍裁切和幾何變換。
  • 使用不同的繪制方法來控制繪制順序
今天這篇就是第二部分: Paint的使用

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

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

1 顏色

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


1.1 基本顏色

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


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

1.1.1 直接設(shè)置顏色

1.1.1.1 setColor(int color)

canvas.drawRect(30, 30, 230, 180, paint);

paint.setColor(Color.parseColor("#FF9800"));  
canvas.drawLine(300, 30, 450, 180, paint);
1.1.1.2 setARGB(int a, int r, int g, int b)

其實和 setColor(color) 都是一樣一樣兒的,只是它的參數(shù)用的是更直接的三原色與透明度的值。

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

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

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

在 Android 的繪制里使用 Shader ,并不直接用 Shader 這個類,而是用它的幾個子類。具體來講有 LinearGradient RadialGradient SweepGradient BitmapShader 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);


構(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。TileMode 一共有 3 個值可選: CLAMP, MIRROR 和 REPEAT。CLAMP (夾子模式???算了這個詞我不會翻)會在端點之外延續(xù)端點處的顏色;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:輻射中心的坐標
radius:輻射半徑
centerColor:輻射中心的顏色
edgeColor:輻射邊緣的顏色
tileMode:輻射范圍之外的著色模式。 同樣titlMode有三種模式 CLAMP;MIRROR;REPEAT; 和上面的類似

1.1.2.3 SweepGradient 掃描漸變

又是一個漸變。「掃描漸變」這個翻譯我也不知道精確不精確。大概是這樣:
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)造方法: SweepGradient(float cx, float cy, int color0, int color1)

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

1.1.2.4 BitmapShader
Shader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);  
paint.setShader(shader);

...

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

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

參數(shù):
bitmap:用來做模板的 Bitmap 對象
tileX:橫向的 TileMode
tileY:縱向的 TileMode。
1.1.2.5 ComposeShader 混合著色器

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

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

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

參數(shù):
shaderA, shaderB:兩個相繼使用的 Shader
mode: 兩個 Shader 的疊加模式,即 shaderA 和 shaderB 應(yīng)該怎樣共同繪制。它的類型是 PorterDuff.Mode 。
具體 PorterDuff.Mode是什么 情況 可以參考 https://developer.android.com/reference/android/graphics/PorterDuff.Mode.html

1.2 setColorFilter(ColorFilter colorFilter)

ColorFilter 這個類,它的名字已經(jīng)足夠解釋它的作用:為繪制設(shè)置顏色過濾。顏色過濾的意思,就是為繪制的內(nèi)容設(shè)置一個統(tǒng)一的過濾策略,然后 Canvas.drawXXX() 方法會對每個像素都進行過濾后再繪制出來。

在 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 用來和目標像素相乘,add 用來和目標像素相加:

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

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

1.2.2 PorterDuffColorFilter

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

1.2.3 ColorMatrixColorFilter

這個就厲害了。ColorMatrixColorFilter 使用一個 ColorMatrix 來對顏色進行處理。 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 可以把要繪制的像素進行轉(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;  

1.3 setXfermode(Xfermode xfermode)

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

Xfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);

...
canvas.drawBitmap(rectBitmap, 0, 0, paint); // 畫方  
paint.setXfermode(xfermode); // 設(shè)置 Xfermode  
canvas.drawBitmap(circleBitmap, 0, 0, paint); // 畫圓  
paint.setXfermode(null); // 用完及時清除 Xfermode  

又是 PorterDuff.Mode 。 PorterDuff.Mode
在 Paint一共有三處 API ,它們的工作原理都一樣,只是用途不同:


2 效果

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

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

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

2.2 setStyle(Paint.Style style)

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

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è)置線條寬度。單位為像素,默認值是 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);

線條寬度 0 和 1 的區(qū)別
默認情況下,線條寬度為 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 方頭。默認為 BUTT。
當(dāng)線條的寬度是 1 像素時,這三種線頭的表現(xiàn)是完全一致的,全是 1 個像素的點;而當(dāng)線條變粗的時候,它們就會表現(xiàn)出不同的樣子:

image

######### 2.3.3 setStrokeJoin(Paint.Join join)

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

image

######### 2.3.4 setStrokeMiter(float miter)

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

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

image

MITER 型連接點有一個額外的規(guī)則:當(dāng)尖角過長時,自動改用 BEVEL 的方式來渲染連接點。
而這個屬性就是setStrokeMiter(miter)方法中的miter參數(shù)。miter參數(shù)是對于轉(zhuǎn)角長度的限制,具體來講,是指尖角的外緣端點和內(nèi)部拐角的距離與線條寬度的比。也就是下面這兩個長度的比:

image

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

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

image

默認情況下,大于這個角的尖角會被保留,而小于這個夾角的就會被「削成平頭」

2.4 色彩優(yōu)化

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

2.4.1 setDither(boolean dither)

設(shè)置圖像的抖動。所謂抖動(注意,它就叫抖動,不是防抖動,也不是去抖動,有些人在翻譯的時候自作主張地加了一個「防」字或者「去」字,這是不對的),是指把圖像從較高色彩深度(即可用的顏色數(shù))向較低色彩深度的區(qū)域繪制時,在圖像中有意地插入噪點,通過有規(guī)律地擾亂圖像來讓圖像對于肉眼更加真實的做法。
比如向 1 位色彩深度的區(qū)域中繪制灰色,由于 1 位深度只包含黑和白兩種顏色,在默認情況下,即不加抖動的時候,只能選擇向上或向下選擇最接近灰色的白色或黑色來繪制,那么顯示出來也只能是一片白或者一片黑。而加了抖動后,就可以繪制出讓肉眼識別為灰色的效果了:

image

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

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

image

再把像素顆粒變小,就會出現(xiàn)上圖的灰色. 具體怎么用呢
paint.setDither(true);
只要加這么一行代碼,之后的繪制就是加抖動的了。
不過對于現(xiàn)在(2017年)而言, setDither(dither) 已經(jīng)沒有當(dāng)年那么實用了,因為現(xiàn)在的 Android 版本的繪制,默認的色彩深度已經(jīng)是 32 位的 ARGB_8888 ,效果已經(jīng)足夠清晰了。只有當(dāng)你向自建的 Bitmap 中繪制,并且選擇 16 位色的 ARGB_4444 或者 RGB_565 的時候,開啟它才會有比較明顯的效果。
######## 2.4.2 setFilterBitmap(boolean filter)

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

image

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

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

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

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

image

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

2.5.3 DashPathEffect

使用虛線來繪制線條。

PathEffect pathEffect = new DiscretePathEffect(20, 5);
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ù)是用來繪制的Pathadvance是兩個相鄰的shape段之間的間隔,不過注意,這個間隔是兩個
shape段的起點的間隔,而不是前一個的終點和后一個的起點的距離;phaseDashPathEffect中一樣,是虛線的偏移;最后一個參數(shù)style,是用來指定拐彎改變的時候shape的轉(zhuǎn)換方式。style的類型為PathDashPathEffect.Style,是一個enum,具體有三個值:

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

這是一個組合效果類的PathEffect。它的行為特別簡單,就是分別按照兩種
PathEffect分別對目標進行繪制。

PathEffect pathEffect = new DashPathEffect(new float[]{10, 5}, 10);  
paint.setPathEffect(pathEffect);
...
canvas.drawCircle(300, 300, 200, paint);  
image
2.5.6 ComposePathEffect

這也是一個組合效果類的 PathEffect 。不過它是先對目標 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 的,功能各不一樣。

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);
效果就是上面這樣。方法的參數(shù)里, radius 是陰影的模糊范圍; dx dy 是陰影的偏移量; shadowColor 是陰影的顏色。
如果要清除陰影層,使用 clearShadowLayer() 。

2.7 setMaskFilter(MaskFilter maskfilter)

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

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

2.7.1 BlurMaskFilter

模糊效果的 MaskFilter。
paint.setMaskFilter(new BlurMaskFilter(50, BlurMaskFilter.Blur.NORMAL));
...
canvas.drawBitmap(bitmap, 100, 100, paint);
它的構(gòu)造方法 BlurMaskFilter(float radius, BlurMaskFilter.Blur style) 中, radius 參數(shù)是模糊的范圍, style 是模糊的類型。一共有四種:

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

2.7.2 EmbossMaskFilter

浮雕效果的 MaskFilter。
paint.setMaskFilter(new EmbossMaskFilter(new float[]{0, 1, 1}, 0.2f, 8, 10));
...
canvas.drawBitmap(bitmap, 100, 100, paint);
它的構(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)用光線的范圍。

2.8 獲取繪制的 Path

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

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

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

什么叫「實際 Path」? Path 就是 Path,這加上個「實際」是什么意思?
文字的 Path ?文字還有 Path?
這兩個問題(咦好像有四個問號)的答案就在后面的內(nèi)容里。

2.8.1 getFillPath(Path src, Path dst)

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

默認情況下(線條寬度為 0、沒有 PathEffect),原 Path 和實際 Path 是一樣的;而在線條寬度不為 0 (并且模式為 STROKE 模式或 FLL_AND_STROKE ),或者設(shè)置了 PathEffect 的時候,實際 Path 就和原 Path 不一樣了:
通過 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)化成圖形,對圖形進行繪制的。 getTextPath() 方法,獲取的就是目標文字所對應(yīng)的 Path 。這個就是所謂「文字的 Path」。

3 drawText() 相關(guān)

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

比如設(shè)置文字大小:
比如設(shè)置文字間隔:
比如設(shè)置各種文字效果:

4 初始化類

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

4.1 reset()

重置 Paint 的所有屬性為默認值。相當(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()。

下一篇都是文字繪制類

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 系列文章之 Android中自定義View(一)系列文章之 Android中自定義View(二)系列文章之 And...
    YoungerDev閱讀 4,465評論 3 11
  • 人與人之間最小的差別是智商,最大的差別是堅持。這個道理我想大家都懂,可是為什么有些人能持之以恒,而有些人則淺嘗則止...
    大涵Then閱讀 472評論 1 0
  • 什么時候一個人要想到自己沒有什么?當(dāng)然是有一個前提條件,當(dāng)他已經(jīng)擁有一切的時候。當(dāng)生活把你選座那個幸運兒,讓...
    4Q閱讀 434評論 0 3