1. 程式人生 > >自定義View之Paint(畫筆)的詳解

自定義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 技術文章