看本篇文章前,請先閱讀治花裏胡哨(二)征服自定義View,各種最基本的drawXXX()方法你都會了嗎?
作爲 花裏胡哨系列的第三篇
,這篇文章就詳細的講解一下 Paint
的各種 api
的使用。我們要知道的是 View
上的內容都是通過 Canvas
畫出來的,但是畫成什麼樣子,都是需要通過 Paint
來指揮的,所以說對於 Paint
瞭解的越詳細,那麼我們在後期繪製時,可用的方法就越多。廢話不多說,馬上進入主題。
本人水平有限,如有錯誤,請在下方評論中多多指正
。
一、 創建畫筆
// 1.創建一個默認畫筆,使用默認的配置
Paint()
// 2.創建一個新畫筆,並通過 flags 參數進行配置。
Paint(int flags)
// 3.創建一個新畫筆,並複製參數中畫筆的設置。
Paint(Paint paint)
第一種創建默認畫筆,第二種中的 flags
設置可以有很多,最常見的是下面的這個,如果需要設置多個參數,參數之間用 |
進行連接即可。
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG );
第三種是根據已有的畫筆複製一個畫筆,複製後的畫筆是一個全新的畫筆,對複製後的畫筆進行任何修改調整都不會影響到被複制的畫筆。
二、 畫筆顏色
mPaint.setColor(Color.RED);//設置顏色
mPaint.setARGB(255, 255, 255, 0);//設置Paint對象顏色,範圍爲0~255
int color = context.getResources().getColor(R.color.colorPrimary);
mPaint.setColor(color);
第一種是最常用的,第二種通過RGB值設置,第三種是使用系統內置的顏色資源。
三、設置透明度
mPaint.setAlpha(200);//設置alpha不透明度,範圍爲0~255
四、設置抗鋸齒
mPaint.setAntiAlias(true);
開啓抗鋸齒的原因是修改圖形邊緣處的像素顏色,從而讓圖形在肉眼看來具有更加平滑的感覺。大多數情況下,都需要開啓,給個效果圖解釋一下:
五、設置描邊寬度
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(1);
canvas.drawCircle(150, 125, 100, paint);
mPaint.setStrokeWidth(5);
canvas.drawCircle(400, 125, 100, paint);
mPaint.setStrokeWidth(40);
canvas.drawCircle(650, 125, 100, paint);
效果圖:
在畫筆寬度爲 0 的情況下,使用 drawLine 或者使用描邊模式(STROKE)也可以繪製出內容。只是繪製出的內容始終是 1 像素,不受畫布縮放的影響。該模式被稱爲hairline mode
(髮際線模式)。
六、設置描邊模式
// 畫筆初始設置
Paint mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(50);
mPaint.setColor(0xFF7FC2D8);
// 填充,默認
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(500, 200, 100, paint);
// 描邊
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(500, 500, 100, paint);
// 描邊 + 填充
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawCircle(500, 800, 100, paint);
效果圖:
在這裏設置了描邊的寬度較大值,這樣顯示出來的效果比較明顯,紅色表示圓的實際大小。
七、設置畫筆線帽
mPaint.setStrokeCap(Paint.Cap.BUTT); //無線帽
mPaint.setStrokeCap(Paint.Cap.SQUARE);//方形線帽
mPaint.setStrokeCap(Paint.Cap.ROUND); //圓角效果
效果圖:
八、設置線段連接方式
mPaint.setStrokeJoin(Paint.Join.MITER);//尖角(默認模式)
mPaint.setStrokeJoin(Paint.Join.BEVEL);//平角
mPaint.setStrokeJoin(Paint.Join.ROUND);//圓角
效果圖:
九 、斜接模式長度限制
Android 中線段連接方式默認是 MITER
,即在拐角處延長外邊緣,直到相交位置。當連接角度小於一定程度時會自動將連接模式轉換爲 BEVEL
(平角)。這個角度大約是 28.96°。即 MITER
(尖角) 模式下小於該角度的線段連接方式會自動轉換爲 BEVEL
(平角) 模式。我們可以通過下面的方法來更改默認限制:
// 參數 miter 就是對長度的限制,
// 它可以通過這個公式計算:miter = 1 / sin ( angle / 2 ) ,
// angel 是兩條線的形成的夾角
// 這個參數的默認值是 4
paint.setStrokeMiter(10);
十 、雙線性過濾
圖像在放大繪製的時候,默認使用的是最近鄰插值過濾,這種算法簡單,但會出現馬賽克現象;而如果開啓了雙線性過濾,就可以讓結果圖像顯得更加平滑。通過以下代碼設置:
mPaint.setFilterBitmap(true);
效果圖:
十一 、PathEffect
什麼是 PathEffect
?它是用來給圖形的輪廓設置效果。對 Canvas
所有的圖形繪製有效。總共有 6 種,如下表格顯示:
名稱 | 說明 |
---|---|
CornerPathEffect | 圓角效果,將尖角替換爲圓角。 |
DashPathEffect | 虛線效果,用於各種虛線效果。 |
PathDashPathEffect | Path 虛線效果,虛線中的間隔使用 Path 代替。 |
DiscretePathEffect | 把線條進行隨機的偏離,讓輪廓變得亂七八糟。亂七八糟的方式和程度由參數決定 |
SumPathEffect | 兩個 PathEffect 效果組合,同時繪製兩種效果。 |
ComposePathEffect | 兩個 PathEffect 效果疊加,先使用效果1,之後使用效果2。 |
最常用的是前兩種,例如第一種:
RectF rect = new RectF(0, 0, 600, 600);
float corner = 300;
// 使用 CornerPathEffect 實現類圓角效果
canvas.translate((1080 - 600) / 2, (1920 / 2 - 600) / 2);
mPaint.setPathEffect(new CornerPathEffect(corner));
canvas.drawRect(rect, mPaint);
效果如圖:
例如第二種:
Path path_dash = new Path();
path_dash.lineTo(0, 1720);
canvas.save();
canvas.translate(980, 100);
/**
* intervals[]:
* 間隔,用於控制虛線顯示長度和隱藏長度,它必須爲偶數(且至少爲 2 個),
* 按照[顯示長度,隱藏長度,顯示長度,隱藏長度]的順序來顯示。
*
* phase:
* 相位(和正餘弦函數中的相位類似,週期爲intervals長度總和),
* 也可以簡單的理解爲偏移量。
*/
mPaint.setPathEffect(new DashPathEffect(new float[]{200, 100}, 0));
canvas.drawPath(path_dash, mPaint);
canvas.restore();
canvas.save();
canvas.translate(400, 100);
mPaint.setPathEffect(new DashPathEffect(new float[]{200, 100}, 100));
canvas.drawPath(path_dash, mPaint);
canvas.restore();
效果如圖:
十二、setMaskFilter
這個方法的作用是給繪製內容的上方添加效果。寫法如下
/**
* radius: 模糊半徑
* NORMAL: 內外都模糊繪製
* SOLID: 內部正常繪製,外部模糊
* INNER: 內部模糊,外部不繪製
* OUTER: 內部不繪製,外部模糊
*/
mPaint.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.NORMAL));//設置畫筆遮罩濾鏡 ,傳入度數和樣式
效果圖就盜用一張:
十三 、渲染器
1. 線性渲染
/**
* 1.線性渲染,LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[], @Nullable float positions[], @NonNull TileMode tile)
* (x0,y0):漸變起始點座標
* (x1,y1):漸變結束點座標
* color0:漸變開始點顏色,16進制的顏色表示,必須要帶有透明度
* color1:漸變結束顏色
* colors:漸變數組
* positions:位置數組,position的取值範圍[0,1],作用是指定某個位置的顏色值,如果傳null,漸變就線性變化。
* tile:用於指定控件區域大於指定的漸變區域時,空白區域的顏色填充方法
*/
mShader = new LinearGradient(0, 0, 500, 500, new int[]{Color.RED, Color.BLUE, Color.GREEN}, new float[]{0.f, 0.7f, 1}, Shader.TileMode.REPEAT);
mPaint.setShader(mShader);
canvas.drawRect(0,0,500,500, mPaint);
效果圖:
2. 環形渲染
/**
* 環形渲染,RadialGradient(float centerX, float centerY, float radius, @ColorInt int colors[], @Nullable float stops[], TileMode tileMode)
* centerX ,centerY:shader的中心座標,開始漸變的座標
* radius:漸變的半徑
* centerColor,edgeColor:中心點漸變顏色,邊界的漸變顏色
* colors:漸變顏色數組
* stops:漸變位置數組,類似掃描漸變的positions數組,取值[0,1],中心點爲0,半徑到達位置爲1.0f
* tileMode:shader未覆蓋以外的填充模式。
*/
mShader = new RadialGradient(250, 250, 250, new int[]{Color.GREEN, Color.YELLOW, Color.RED}, null, Shader.TileMode.CLAMP);
mPaint.setShader(mShader);
canvas.drawCircle(250, 250, 250, mPaint);
效果圖:
3. 掃描渲染
/**
* 掃描渲染,SweepGradient(float cx, float cy, @ColorInt int color0,int color1)
* cx,cy 漸變中心座標
* color0,color1:漸變開始結束顏色
* colors,positions:類似LinearGradient,用於多顏色漸變,positions爲null時,根據顏色線性漸變
*/
mShader = new SweepGradient(250, 250, Color.RED, Color.GREEN);
mPaint.setShader(mShader);
canvas.drawCircle(250, 250, 250, mPaint);
效果圖:
4. 位圖渲染
/**
* 位圖渲染,BitmapShader(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY)
* Bitmap:構造shader使用的bitmap
* tileX:X軸方向的TileMode
* tileY:Y軸方向的TileMode
* Shader.TileMode:
* REPEAT, 繪製區域超過渲染區域的部分,重複排版
* CLAMP, 繪製區域超過渲染區域的部分,會以最後一個像素拉伸排版
* MIRROR, 繪製區域超過渲染區域的部分,鏡像翻轉排版
*/
mShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT, Shader.TileMode.MIRROR);
mPaint.setShader(mShader);
canvas.drawRect(0,0,500, 500, mPaint);
canvas.drawCircle(250, 250, 250, mPaint);
效果圖:
5. 組合渲染
/**
* 組合渲染,
* ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, Xfermode mode)
* ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, PorterDuff.Mode mode)
* shaderA,shaderB:要混合的兩種shader
* Xfermode mode: 組合兩種shader顏色的模式
* PorterDuff.Mode mode: 組合兩種shader顏色的模式
*/
BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
LinearGradient linearGradient = new LinearGradient(0, 0, 1000, 1600, new int[]{Color.RED, Color.GREEN, Color.BLUE}, null, Shader.TileMode.CLAMP);
mShader = new ComposeShader(bitmapShader, linearGradient, PorterDuff.Mode.MULTIPLY);
mPaint.setShader(mShader);
canvas.drawCircle(250, 250, 250, mPaint);
效果圖:
上面介紹了這麼多的 api
的使用,可能看的眼都花了,下面介紹一個需要點理解力的。
十四、圖層混合模式
在上面的組合渲染中,有一個參數 PorterDuff.Mode
,也就是圖層混合模式。
我們所知的圖層混合所使用的地方一共有三處:
- 組合渲染 ComposeShader 中會使用到。
- 畫筆
Paint.setXfermode()
方法中會使用到。 PorterDuffColorFilter
中會使用到。
現在說的是第二種,它將所繪製的圖形的像素與 Canvas 中對應位置的像素按照一定規則進行混合,形成新的像素值,從而更新 Canvas 中最終的像素顏色值。一共有 18
種模式:
對各種模式說明:
//所繪製不會提交到畫布上
new PorterDuffXfermode(PorterDuff.Mode.CLEAR),
//顯示上層繪製的圖像
new PorterDuffXfermode(PorterDuff.Mode.SRC),
//顯示下層繪製圖像
new PorterDuffXfermode(PorterDuff.Mode.DST),
//正常繪製顯示,上下層繪製疊蓋
new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),
//上下層都顯示,下層居上顯示
new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),
//取兩層繪製交集,顯示上層
new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),
//取兩層繪製交集,顯示下層
new PorterDuffXfermode(PorterDuff.Mode.DST_IN),
//取上層繪製非交集部分,交集部分變成透明
new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),
//取下層繪製非交集部分,交集部分變成透明
new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),
//取上層交集部分與下層非交集部分
new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),
//取下層交集部分與上層非交集部分
new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),
//去除兩圖層交集部分
new PorterDuffXfermode(PorterDuff.Mode.XOR),
//取兩圖層全部區域,交集部分顏色加深
new PorterDuffXfermode(PorterDuff.Mode.DARKEN),
//取兩圖層全部區域,交集部分顏色點亮
new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),
//取兩圖層交集部分,顏色疊加
new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),
//取兩圖層全部區域,交集部分濾色
new PorterDuffXfermode(PorterDuff.Mode.SCREEN),
//取兩圖層全部區域,交集部分飽和度相加
new PorterDuffXfermode(PorterDuff.Mode.ADD),
//取兩圖層全部區域,交集部分疊加
new PorterDuffXfermode(PorterDuff.Mode.OVERLAY)
展示一下效果圖:
另外,要想使用 setXfermode()
正常繪製,必須使用離屏緩存 (Off-screen Buffer) 把內容繪製在額外的層上,再把繪製好的內容貼回 View 中。也就是這樣:
使用離屏緩衝有兩種方式:
saveLayer()
- 可以做短時的離屏緩衝。使用方法很簡單,在繪製代碼的前後各加一行代碼,在繪製之前保存,繪製之後恢復。
int saved = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(rectBitmap, 0, 0, paint); // 畫方
paint.setXfermode(xfermode); // 設置 Xfermode
canvas.drawBitmap(circleBitmap, 0, 0, paint); // 畫圓
paint.setXfermode(null); // 用完及時清除 Xfermode
canvas.restoreToCount(saved);
View.setLayerType()
//直接把整個 View 都繪製在離屏緩衝中。
View.setLayerType()
//使用 GPU 來緩衝。
setLayerType(LAYER_TYPE_HARDWARE)
//直接直接用一個 Bitmap 來緩衝。
setLayerType(LAYER_TYPE_SOFTWARE)
一般情況下,使用 第一種
方法作離屏緩衝。
十五、顏色過濾
1. LightingColorFilter
/**
* R' = R * mul.R / 0xff + add.R
* G' = G * mul.G / 0xff + add.G
* B' = B * mul.B / 0xff + add.B
*/
//原始圖片效果
LightingColorFilter lighting = new LightingColorFilter(0xffffff,0x000000);
mPaint.setColorFilter(lighting);
canvas.drawBitmap(mBitmap, 0,0, mPaint);
效果圖:
這個表示原圖效果,如果我們需要修改顏色,該如何修改?
比如說去掉紅色,根據註釋中的公式,只要將 0xffffff 改爲 0x00ffff 即可,效果如下:
如果要讓綠色更亮,又該怎麼辦,看到公式右邊有個 add 的參數了嗎?,修改
0x000000 爲 0x003000 ,再來看一下效果圖:
2. PorterDuffColorFilter
- 使用一個指定的顏色和一種指定的 PorterDuff.Mode 來與繪製對象進行合成。
這個比較簡單,看一下代碼就懂:
PorterDuffColorFilter porterDuffColorFilter = new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DARKEN);
mPaint.setColorFilter(porterDuffColorFilter);
canvas.drawBitmap(mBitmap, 100, 0, mPaint);
效果圖:
3. ColorMatrixColorFilter
這個就比較厲害了,這是一個 4x5 的矩陣,直接看代碼吧:
float[] colorMatrix = {
1,0,0,0,0, //red
0,1,0,0,0, //green
0,0,1,0,0, //blue
0,0,0,1,0 //alpha
};
mColorMatrixColorFilter = new ColorMatrixColorFilter(colorMatrix);
mPaint.setColorFilter(mColorMatrixColorFilter);
canvas.drawBitmap(mBitmap, 100, 0, mPaint);
這個顯示原圖,如何修改,只需要修改其中的表示 1 的值,如果紅色要加強,就將表示紅色的 1 改爲更高的數字,去掉紅色則將 1 改爲 0 就行了,這個就不想演示了。
其實還有另外一種寫法,如下:
ColorMatrix cm = new ColorMatrix();
mColorMatrixColorFilter = new ColorMatrixColorFilter(cm);
mPaint.setColorFilter(mColorMatrixColorFilter);
canvas.drawBitmap(mBitmap, 100, 0, mPaint);
這個也是顯示原圖的,其中調節亮度,調用這個方法:
//什麼都不改
cm.setScale(1,1,1,1);
//綠色加強
cm.setScale(1,2,1,1);
它還有一個增強飽和度的方法:
//飽和度調節0-無色彩, 1- 默認效果, >1飽和度加強
cm.setSaturation(2);
顯示效果如下:
好了,關於 Paint
的相關 api
基本就介紹到位了,希望各位看官有收穫。