自定義View之Paint(畫筆)的詳解
Android提供了2D圖形繪製的各種工具,如Canvas(畫布)、Point(點)、Paint(畫筆)、Rectangles(矩形)等,利用這些工具可以直接在介面上進行繪製。
在自定義View中,我們經常用到的Canvas(畫布)和Paint(畫筆),像我們畫畫一樣,需要畫布和畫筆,在View中繪製控制元件,Canvas就代表著畫布,Paint就代表著畫筆。
官網中的API有很多,下面是比較常用的一些API:
Paint.setAntiAlias(boolean flag);//設定抗鋸齒效果 設定true的時邊緣會將鋸齒模糊化
Paint.setDither(boolean flag);//設定防抖動,設定true的時圖片看上去會更柔和點
Paint.setColor(int color);//設定畫筆顏色
Paint.setARGB(int a, int r, int g, int b); //設定畫筆的ARGB值
Paint.setAlpha(int alpha);//設定畫筆的Alpha值
Paint.setStyle(); //設定畫筆的style (三種:FILL填充 FILL_AND_STROKE填充加描邊 STROKE描邊 )
Paint.setStrokeWidth(float width);//設定描邊寬度
Paint.setXfermode(Xfermode xfermode);//設定圖形重疊時的處理方式,如合併,取交集或並集,經常用來製作橡皮的擦除效果
Paint.setShader(Shader shader);//設定影象效果,使用Shader可以繪製出各種漸變效果
Paint.setShadowLayer(float radius ,float dx,float dy,int color);//在圖形下面設定陰影層,產生陰影效果,radius為陰影的半徑,dx和dy為陰影在x軸和y軸上的距離,color為陰影的顏色
//下面寫文字的時候經常用到的
Paint.setTextSize(float textSize);//設定畫筆文字大小
Paint.measureText(String text);//測試文字的長度
Paint.setTextAlign(Paint.Align align );// CENTER(文字居中) LEFT(文字左對齊) RIGHT(文字右對齊)
下面就演示一下上面這幾個API的效果。
Paint.setStyle()
Paint.setStyle() //設定畫筆的style,有三種
- Paint.Style.FILL //將填充使用此樣式繪製的幾何和文字,忽略繪畫中與筆劃相關的所有設定
- Paint.Style.FILL_AND_STROKE //使用此樣式繪製的幾何和文字將同時填充和描邊,尊重繪畫中與筆劃相關的欄位
- Paint.Style.STROKE //使用此樣式繪製的幾何和文字將被描邊,尊重繪畫上與筆劃相關的欄位
演示一個小demo:
paint = new Paint();
paint.setColor(Color.RED);//畫筆顏色為紅色
paint.setStrokeWidth(80); //描邊寬度為80(為了區分效果,特意設定特別大)
float radius = 100f;
//將填充使用此樣式繪製的幾何和文字,忽略繪畫中與筆劃相關的所有設定
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(400, 200, radius, paint);
//使用此樣式繪製的幾何和文字將同時填充和描邊,尊重繪畫中與筆劃相關的欄位
paint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawCircle(400, 500, radius, paint);
//使用此樣式繪製的幾何和文字將被描邊,尊重繪畫上與筆劃相關的欄位
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(400, 900, radius, paint);
結果:
Paint.setShader(Shader shader)
Paint.setShader(Shader shader) //設定影象效果,使用Shader可以繪製出各種漸變效果
Shader:著色器,用來給影象著色,此類是基類, Shader的API 。有5個子類:
- BitmapShader
- ComposeShader
- LinearGradient
- RadialGradient
- SweepGradient
在瞭解上面5個類之前,先了解一下Shader.TileMode這個列舉,有三個值:
- Shader.TileMode.CLAMP :如果著色器在其原始邊界之外繪製,則複製邊緣顏色
- Shader.TileMode.MIRROR :水平和垂直重複著色器的影象,交替映象,使相鄰的影象始終接縫
- Shader.TileMode.REPEAT :水平和垂直重複著色器的影象
BitmapShader
其實這個Shader用於繪製bitmap作為紋理,然後通過平鋪模式進行填充
/**
* 建構函式
* @bitmap 用來填充圖形的Bitmap
* @tileX X軸Bitmap用Shader.TileMode模式填充
* @tileY Y軸Bitmap用Shader.TileMode模式填充
*/
BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)
演示一下:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.flower);
BitmapShader shader = new BitmapShader(bitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.MIRROR);
paint.setShader(shader);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);
結果:
X軸用Shader.TileMode.CLAMP模式,就是用bitmap的右邊緣去填充X軸的其餘空間
Y軸用Shader.TileMode.MIRROR模式,就是在用相鄰兩張影象互為映象的方式填充整個Y軸其餘空間
接下來XY軸換一下模式:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.flower);
BitmapShader shader = new BitmapShader(bitmap, BitmapShader.TileMode.MIRROR, BitmapShader.TileMode.REPEAT);
paint.setShader(shader);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);
結果:
X軸用Shader.TileMode.MIRROR模式,就是在用相鄰兩張影象互為映象的方式填充整個X軸其餘空間
Y軸用Shader.TileMode.REPEAT模式,就是用相同的影象重複填充整個Y軸其餘空間
LinearGradient
LinearGradient:它是沿一條直線用來建立線性漸變效果,(x0,y0),(x1,y1)分別是起始座標和終止座標,color0,color1分別是起始顏色和終止顏色,tile為Shader.TileMode(CLAMP,REPEAT,MIRROR)模式中的一個。
/**
* 建構函式
* @x0 漸變線起始座標的X座標
* @y0 漸變線起始座標的Y座標
* @x1 漸變線終止座標的X座標
* @y1 漸變線終止座標的Y座標
* @color0 漸變線起始顏色
* @color1 漸變線終止顏色
* @tile 漸變線用Shader.TileMode模式填充
*/
LinearGradient( float x0, float y0, float x1, float y1, int color0, int color1, TileMode tile);
演示一下:
LinearGradient linearGradient = new LinearGradient(200, 200, 600, 600, Color.GREEN, Color.RED, Shader.TileMode.MIRROR);
paint.setShader(linearGradient);
canvas.drawRect(200, 200, 600, 600, paint);
結果:
修改一下,擴大範圍:
LinearGradient linearGradient = new LinearGradient(200, 200, 600, 600, Color.GREEN, Color.RED, Shader.TileMode.MIRROR);
paint.setShader(linearGradient);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);
然後在LinearGradient建構函式裡面的Shader.TileMode模式,分別改成CLAMP、MIRROR和REPEAT。結果為:
LinearGradient的另一個建構函式:
/**
* 建構函式
* @colors[] 用colors陣列線性填充
* @positions[] 每個position取值範圍[0,1],並且和colors陣列中對應位置的color一一對應
*/
LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],TileMode tile)
演示一下:
int[] colors = {Color.GREEN, Color.GRAY, Color.RED, Color.BLUE};
float[] positions = {0f, 0.5f, 0.75f, 1f};
LinearGradient linearGradient = new LinearGradient(200, 200, 600, 600, colors, positions, Shader.TileMode.REPEAT);
paint.setShader(linearGradient);
canvas.drawRect(200, 200, 600, 600, paint);
結果:
修改一下,擴大範圍:
int[] colors = {Color.GREEN, Color.GRAY, Color.RED, Color.BLUE};
float[] positions = {0f, 0.5f, 0.75f, 1f};
LinearGradient linearGradient = new LinearGradient(200, 200, 600, 600, colors, positions, Shader.TileMode.REPEAT);
paint.setShader(linearGradient);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);
一樣的轉換三種模式看看:
RadialGradient
RadialGradient:它也用來建立漸變效果,和LinearGradient不同的是,LinearGradient是線性漸變,而RadialGradient是徑向漸變,也就是從中心向四周發散漸變,RadialGradient也有兩個建構函式。先來看看第一個:
/**
*建構函式
* @centerX 圓中心的X軸座標
* @centerY 圓中心的Y軸座標
* @radius 圓半徑
* @centerColor 圓中心顏色
* @edgeColor 圓邊緣顏色
* @tileMode 徑向漸變Shader.TileMode模式填充
*/
RadialGradient( float centerX, float centerY, float radius, int centerColor, int edgeColor, TileMode tileMode)
演示一下:
float radius = 400f;
RadialGradient gradient = new RadialGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, radius, Color.RED, Color.YELLOW, Shader.TileMode.CLAMP);
paint.setShader(gradient);
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, radius, paint);
結果:
擴大一下範圍:
float radius = 300f;
RadialGradient gradient = new RadialGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, radius, Color.RED, Color.YELLOW, Shader.TileMode.REPEAT);
paint.setShader(gradient);
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, getMeasuredWidth() / 2, paint);
同樣轉換三種模式看看:
RadialGradient的另一個建構函式:
/**
* 建構函式
* @colors[] color陣列分佈在圓的中心和邊緣之間
* @stops[] 取值範圍在[0.0f,1.0f],並且和colors陣列中對應位置的color一一對應,如果為null,顏色均勻的分佈在中心和邊緣之間
*/
RadialGradient(float centerX, float centerY, float radius,int colors[], float stops[],TileMode tileMode)
結果類似LinearGradient第二個建構函式,下來自己去演示。
SweepGradient
SweepGradient:用來建立圍繞一箇中心點360度沿順時針旋轉漸變效果。
/**
* 建構函式
* @cx 圓中心的X軸座標
* @cy 圓中心的Y軸座標
* @color0 開始旋轉起始顏色,起始點在3點鐘方向,順時針
* @color1 結束旋轉終止顏色,終止點也在3點鐘方向
*/
SweepGradient( float cx, float cy, int color0, int color1)
演示一下:
SweepGradient gradient = new SweepGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, Color.GREEN, Color.RED);
paint.setShader(gradient);
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, getMeasuredWidth() / 2, paint);
結果:
修改一下形狀:
SweepGradient gradient = new SweepGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, Color.GREEN, Color.RED);
paint.setShader(gradient);
canvas.drawRect(0, (getMeasuredHeight() - getMeasuredWidth()) / 2, getMeasuredWidth(), (getMeasuredHeight() + getMeasuredWidth()) / 2, paint);
結果:
SweepGradient的另一個建構函式:
/**
* 建構函式
* @colors[] color陣列順時針分佈
* @positions[] 每個position取值範圍[0,1],並且和colors陣列中對應位置的color一一對應
*/
SweepGradient(float cx, float cy, int colors[], float positions[])
演示一下:
int[] colors = {Color.GREEN, Color.YELLOW, Color.BLACK, Color.BLUE, Color.RED};
float[] positions = {0.0f, 0.25f, 0.5f, 0.75f, 1.0f};
SweepGradient gradient = new SweepGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, colors, positions);
paint.setShader(gradient);
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, getMeasuredWidth() / 2, paint);
結果:
ComposeShader
ComposeShader結合Xfermode模式,是兩個Shader的組合模式,ComposeShader有兩個建構函式:
ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)
ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode)
Xfermode可以用於實現新繪製的畫素與Canvas上對應位置已有的畫素按照混合規則進行顏色混合,Xfermode 有三個子類:AvoidXfermode, PixelXorXfermode和PorterDuffXfermode,前兩個已廢棄,PorterDuffXfermode初始化時需要傳入PorterDuff.Mode 即:PorterDuffXfermode(PorterDuff.Mode mode),所以上面第一個建構函式是第二個建構函式的一種情況,我們只看第一個建構函式就可以了:
/**
* 建構函式
* @shaderA 目標畫素DST
* @shaderB 源畫素SRC
* @mode 新繪製的畫素與Canvas上對應位置已有的畫素按照混合規則進行顏色混合
*/
ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)
演示一下:
// 建立BitmapShader物件
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.MIRROR,
Shader.TileMode.MIRROR);
// 建立LinearGradient並設定漸變顏色陣列,平鋪效果為映象
LinearGradient linearGradient = new LinearGradient(0, 0, 0, 100, new int[] {
Color.WHITE, Color.LTGRAY, Color.TRANSPARENT, Color.GREEN }, null,
Shader.TileMode.MIRROR);
ComposeShader composeShader = new ComposeShader(bitmapShader, linearGradient, PorterDuff.Mode.MULTIPLY);
paint.setShader(composeShader);
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, getMeasuredWidth() / 2, paint);
結果:
介紹完了,繼續學習API
Paint.setShadowLayer(float radius ,float dx,float dy,int color)
Paint.setShadowLayer(float radius ,float dx,float dy,int color) //在圖形下面設定陰影層,產生陰影效果
/**
* @radius radius為陰影半徑,半徑越大,陰影面積越大,越模糊;反之,半徑越小,陰影面積越小,也越清晰,radius=0時,陰影消失
* @dx dx為陰影在x軸上的偏移值
* @dy dy為陰影在y軸上的偏移值
* @color color為陰影的顏色
*/
Paint.setShadowLayer( float radius, float dx, float dy, int color);
演示一下:
paint.setColor(Color.RED);
paint.setShadowLayer(20, 0, 0, Color.YELLOW);
paint.setTextSize(100);
canvas.drawText("I am Layne", 200, 300, paint);
結果:
改一下:
paint.setShadowLayer(20,50, 50, Color.YELLOW);
結果:
改一下:
paint.setShadowLayer(1,50, 50, Color.YELLOW);
結果:
再改一下:
paint.setShadowLayer(0,50, 50, Color.YELLOW);
結果:
新增陰影:
paint.setColor(Color.RED);
paint.setShadowLayer(30, 0, 0, Color.BLACK);
setLayerType(LAYER_TYPE_SOFTWARE, paint);//要注意加上這句
canvas.drawCircle(400, 800, 100, paint);
結果:
關注個人公眾號,主推 Android 技術文章