Android VIew繪圖機(jī)制

本篇博客是我對View的繪圖方面的理解和歸納,也不會說的非常的細(xì)煞抬,只是類似于一個思維導(dǎo)圖的作用霜大,因為有人寫得更好了,我就沒必要寫了革答,況且寫了還不一定如別人的战坤。

本文大綱如下:

大綱
  • View形狀繪制
  • View顏色繪制
  • View幾何變換和裁剪
  • View的繪制順序

一、繪制形狀

1)蝗碎、Paint類的基本用法

  • paint.setAntiAlias(true); //抗鋸齒功能,一般都會加入的
  • paint.setColor( ); // 設(shè)置畫筆顏色
  • paint.setARGB(int a, int r, int g, int b) // 設(shè)置三原色和透明通路
  • paint.setAlpha(int a) // 設(shè)置透明通道
  • paint.setStrokeWidth(int a);//設(shè)置畫筆寬度
  • paint.setStyle(Style.FILL);//設(shè)置填充樣式
  • paint.setTextAlign(Align.CENTER); //設(shè)置文字對齊方式旗扑,取值:Align.CENTER蹦骑、Align.LEFT或Align.RIGHT
  • paint.setTextSize(12); //設(shè)置文字大小
  • paint.setUnderlineText(true); //設(shè)置下劃線
  • paint.setStrikeThruText(true); //設(shè)置帶有刪除線效果
  • paint.setTextScaleX(2); //只會將水平方向拉伸,高度不會變
Paint.Style 屬性 描述
Paint.Style.FILL 填充內(nèi)部
Paint.Style.FILL_AND_STROKE 填充內(nèi)部和描邊
Paint.Style.STROKE 僅描邊
Paint.Cap 屬性 描述
Paint.Cap.BUTT 線頭形狀平頭
Paint.Cap.ROUND 線頭形狀圓頭
Paint.Cap.SQUARE 線頭形狀方頭
Cap形狀
Paint.Align屬性 描述
Paint.Align.LEFT 內(nèi)容居左顯示
Paint.Align.CENTER 內(nèi)容居中顯示
Paint.Align.RIGHT 內(nèi)容居右顯示
  • paint.setShadowLayer (float radius, float dx, float dy, int color) // 添加陰影臀防,傾斜度眠菇,x軸偏離值,y軸偏離值袱衷,陰影顏色捎废。如TextView等也可以在xml布局中屬性有android:shadowDx等等,原理一致
  • paint.drawPath(@NonNull Path path, @NonNull Paint paint) //畫條路徑

2)致燥、Canvas 繪制

①登疗、通用函數(shù)
  • canvas.drawColor(Color.BLUE); // 設(shè)置畫布顏色,即背景
  • canvas.drawRGB(255, 255, 0); // 設(shè)置畫布顏色嫌蚤,即背景
  • canvas.drawLine (float startX, float startY, float stopX, float stopY, Paint paint) // 畫一條直線
  • void drawLines (float[] pts, Paint paint) // 多條直線
  • void drawPoint (float x, float y, Paint paint) // 畫條點
  • void drawPoints (float[] pts, Paint paint)
  • void drawRect (float left, float top, float right, float bottom, Paint paint) // 畫矩形
  • void drawRect (RectF rect, Paint paint)
  • void drawRect (Rect r, Paint paint)
  • void drawRoundRect (RectF rect, float rx, float ry, Paint paint) // 圓角矩形
  • void drawCircle (float cx, float cy, float radius, Paint paint) // 圓形
  • void drawOval (RectF oval, Paint paint) // 橢圓
  • void drawArc (RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) // 畫條弧線
    注意:useCenter和Paint.Style搭配使用可以達(dá)到不同的效果辐益。如扇形和弧線等。
②脱吱、文字相關(guān)
  • void drawText (String text, float x, float y, Paint paint)
  • void drawText (CharSequence text, int start, int end, float x, float y, Paint paint) // 畫一個Text出來智政,start和end代表畫那些字符,x代表x軸位置箱蝠,y代表基線续捂。
    基線算法:
       Paint.FontMetricsInt fm = paint.getFontMetricsInt();
       int dy = (fm.top + fm.bottom)/2;
       int baseLine = getHeight()/2 - dy;`
  • void drawText (String text, int start, int end, float x, float y, Paint paint)
  • void drawText (char[] text, int index, int count, float x, float y, Paint paint)
  • void drawPosText (String text, float[] pos, Paint paint) // 指定每個文字的位置
  • void drawTextOnPath (String text, Path path, float hOffset, float vOffset, Paint paint) // 沿著一個路徑繪制

3)、Path基本用法

Path用法 描述
path.moveTo(10, 10); 設(shè)定起始點
path.lineTo(10, 100) 連接到下一個點
path.rMoveTo(10, 10); 以在上一個結(jié)束點為基準(zhǔn)點宦搬,x軸偏移10像素牙瓢,y軸偏移10像素
path.rLineTo(10, 100) 連接到下一個點,下個點是以在上一個結(jié)束點為基準(zhǔn)點间校,x軸偏移10像素一罩,y軸偏移10像素
path.close(); 形成一個閉環(huán)
path.addRect(RectF rect, Direction dir) 增加一個矩形路徑,Direction枚舉代表生成的方向
void addCircle (float x, float y, float radius, Path.Direction dir) 圓形路徑
void addOval (RectF oval, Path.Direction dir) 橢圓路徑
void addArc (RectF oval, float startAngle, float sweepAngle) 弧形路徑
void quadTo 二階貝塞爾曲線
void cubicTo 三階貝塞爾曲線
Path.Direction屬性 描述
CW 順時針開始繪制
CCW 逆時針開始繪制
只有在兩個圖形相交的時候撇簿,才會用到FillType屬性聂渊,用于判斷相交的部分是否需要顯示
Path.FillType屬性 描述
WINDING 非零環(huán)繞數(shù)原則差购,默認(rèn)為WINDING
EVEN_ODD 交叉填充,兩圖形奇偶原則汉嗽。一點重合了偶數(shù)次就不畫
INVERSE_WINDING 與WINDING完全相反
INVERSE_EVEN_ODD 與EVEN_ODD完全相反
WINDING

對于上圖欲逃,WINDING規(guī)則下,畫的方向會直接影響多個圖形的重合區(qū)域饼暑,對于一點順時針則+1稳析,逆時針-1,如果相加等于0則不畫弓叛。

INVERSE_WINDING和WINDING畫的是完全相反的彰居。INVERSE_EVEN_ODD和EVEN_ODD是完全相反的。

二撰筷、繪制顏色

1)陈惰、基本顏色

  • canvas.drawColor(Color.BLUE); // 設(shè)置畫布顏色,即背景
  • canvas.drawRGB(255, 255, 0); // 設(shè)置畫布顏色毕籽,即背景
  • paint.setColor( ); // 設(shè)置畫筆顏色
  • paint.setARGB(int a, int r, int g, int b) // 設(shè)置三原色和透明通路
  • setShader(Shader shader) 設(shè)置 Shader

Shader稱之著色器和渲染器抬闯,可以實現(xiàn)一些漸變和渲染效果。它的子類包括:

  • BitmapShader : 位圖Shader
  • LinearShader : 線性Shader
  • RadialShader : 光束Shader
  • SweepShader : 梯度Shader
  • ComposeShader : 混合Shader
BitmapShader

構(gòu)造函數(shù):
public BitmapShader(@NonNull Bitmap bitmap, TileMode tileX, TileMode tileY) 关筒;
其中TileMode 是圖像的填充模式

  • CLAMP : 拉伸模式溶握,圖片最后一個像素的顏色鋪滿剩余的X或者Y軸
  • REPETA : 重復(fù)模式
  • MIRROR : 鏡像模式

看一個關(guān)于圖像中間去一個圓形圖像的例子

public class TelescopeView extends View {
    private Paint mPaint;
    private Bitmap mBitmap,mBitmapBG;
    private int mDx = -1, mDy = -1;
    public TelescopeView(Context context) {
        super(context);
        init();
    }

    public TelescopeView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public TelescopeView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        mPaint = new Paint();
        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.game);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mDx = (int) event.getX();
                mDy = (int) event.getY();
                postInvalidate();
                return true;
            case MotionEvent.ACTION_MOVE:
                mDx = (int) event.getX();
                mDy = (int) event.getY();
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mDx = -1;
                mDy = -1;
                break;
        }

        postInvalidate();
        return super.onTouchEvent(event);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mBitmapBG == null){
            mBitmapBG = Bitmap.createBitmap(getWidth(),getHeight(), Bitmap.Config.ARGB_8888);
            Canvas canvasbg = new Canvas(mBitmapBG);
            canvasbg.drawBitmap(mBitmap,null,new Rect(0,0,getWidth(),getHeight()),mPaint);
            mPaint.setShader(new BitmapShader(mBitmapBG, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
        }

        if (mDx != -1 && mDy != -1) {
            canvas.drawCircle(mDx, mDy, 150, mPaint);
        }
    }
}

2)、使用ColorFilter

ColorFilter 是一個顏色過濾的基類蒸播,它的子類分別是

  • ColorMatrixColorFilter : 色彩矩陣過濾
  • LightingColorFilter : ColorMatrixColorFilter的簡化版
  • PorterDuffColorFilter : 用單一顏色和特定的復(fù)合模式對源像素進(jìn)行著色
①睡榆、ColorMatrixColorFilter

在此之前先說明ColorMatrix這個類。主要屬性是一個大小為20的float數(shù)組袍榆,即45的舉證肉微,這是個45階的矩陣,RGBA和一個啞元蜡塌。啞元是一個附加值碉纳。增加一些分量。

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

上面矩陣和RGBA這個矩陣相乘就會得到一個運算后的新的RGBA
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 colorMatrix = new ColorMatrix(new float[]{
        1, 0, 0, 0, 0,
        0, 1, 0, 0, 0,
        0, 0, 1, 0, 0,
        0, 0, 0, 0.5, 0,
});
mPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));

通過ColorMatrix矩陣的值的不同劳曹,得到的新的顏色也是不一致的。

1琅摩、過濾Blue通道的顏色
colorMatrix = new ColorMatrix(new float[]{
                0, 0, 0, 0, 0,
                0, 0, 0, 0, 0,
                0, 0, 1, 0, 0,
                0, 0, 0, 1, 0,
        })

2铁孵、顏色取反
 colorMatrix = new ColorMatrix(new float[]{
                -1,0,0,0,255,
                0,-1,0,0,255,
                0,0,-1,0,255,
                0,0,0,1,0
        });

3、色彩的縮放運算
colorMatrix = new ColorMatrix(new float[]{
                1.2f, 0, 0, 0, 0,
                0, 1.2f, 0, 0, 50,
                0, 0, 1.2f, 0, 0,
                0, 0, 0, 1.2f, 0,
        });

4房资、色彩反色 (紅綠反色)
        colorMatrix = new ColorMatrix(new float[]{
                0,1,0,0,0,
                1,0,0,0,0,
                0,0,1,0,0,
                0,0,0,1,0
        });

ColorMatrix類也給出了一些基本的運算:

  • colorMatrix.setRotate(); : 設(shè)置旋轉(zhuǎn)
  • colorMatrix.setSaturation(); : 設(shè)置飽和度蜕劝,同時增加RGB值
  • colorMatrix.setScale(); : 色彩縮放

上面只給出了一些特定的矩陣效果,這個要求數(shù)學(xué)功底還可以,所以才有了LightingColorFilter

②岖沛、LightingColorFilter

唯一的構(gòu)造方法 : LightingColorFilter(int mul, int add)// mul是用來相乘的暑始,add運來相加
運算結(jié)果:

R = (r*mul.R+add.R)%255;
G = (g*mul.G+add.G)%255;
B = (b*mul.B+add.B)%255;

用法比較簡單: 
mPaint.setColorFilter(new LightingColorFilter(0x00ff00, 0x000000));
③、PorterDuffColorFilter

這是個比較強(qiáng)大的類婴削。和PhotoShop的顏色疊加理念是一致的廊镜。
構(gòu)造方法:
public PorterDuffColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode)
一個附加顏色和一個模式。
PorterDuff.Mode 模式種類

     Mode.SRC
     Mode.DST
     Mode.SRC_OVER
     Mode.DST_OVER
     Mode.SRC_IN
     Mode.DST_IN
     Mode.SRC_OUT  透明
     Mode.DST_OUT
     Mode.SRC_ATOP
     Mode.DST_ATOP

     Mode.CLEAR
     Mode.XOR

*    Mode.DARKEN     變暗
*    Mode.LIGHTEN    變亮
*    Mode.MULTIPLY   正片疊底
*    Mode.SCREEN     濾色
*    Mode.OVERLAY    疊加
*    Mode.ADD        飽和度相加

大體分為下面幾種模式唉俗,源顏色為Color.RED舉例

  • 清除模式 : Mode.CLEAR 和 Mode.XOR嗤朴,什么都不會顯示喻鳄,Mode.XOR與透明度有關(guān)


  • 源模式: 前綴為Mode.SRC 顯示后加顏色煎源,除了Mode.SRC_OUT透明外,其他顯示RED


  • 目標(biāo)模式:前綴為Mode.DST耐朴,除了Mode.DST_OUT透明外衡楞,其他顯示目標(biāo)圖


  • 其他為疊加模式


代碼如下:

public class SelfViewTwo extends View {

    private Paint mPaint;
    private Bitmap mBmp;

    public SelfViewTwo(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint();

        mBmp = BitmapFactory.decodeResource(getResources(), R.drawable.game);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setAntiAlias(true);

        drawPorterDuffFilter5(canvas);
    }

    private void drawPorterDuffFilter(Canvas canvas){
        int width  = 300;
        int height = width * mBmp.getHeight()/mBmp.getWidth();

        mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.ADD));//飽和度相加
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);


        canvas.translate(350,0);
        mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DARKEN));//變暗
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);

        canvas.translate(-350,350);
        mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.LIGHTEN));//變亮
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);

        canvas.translate(350,0);
        mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.MULTIPLY));//正片疊底
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);

        canvas.translate(-350,350);
        mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.OVERLAY));//疊加
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);

        canvas.translate(350,0);
        mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SCREEN));//濾色
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
    }

    /**
     * 清空模式
     * PorterDuff.Mode.CLEAR 和 PorterDuff.Mode.XOR 不會顯示的
     */
    private void drawPorterDuffFilter2(Canvas canvas){
        int width  = 500;
        int height = width * mBmp.getHeight()/mBmp.getWidth();

        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);

        canvas.translate(550,0);
        mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.CLEAR));
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);

        canvas.translate(-550,550);
        mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.XOR));
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
    }


    /**
     * 目標(biāo)圖像模式
     * Mode.DST吱雏、Mode.DST_IN、Mode.DST_OUT寺酪、Mode.DST_OVER坎背、Mode.DST_ATOP
     * 除了Mode.DST_OUT顯示完全透明圖片以外替劈,其它全部顯示目標(biāo)圖像寄雀;
     */
    private void drawPorterDuffFilter3(Canvas canvas){
        int width  = 500;
        int height = width * mBmp.getHeight()/mBmp.getWidth();

        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);

        canvas.translate(550,0);
        mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DST));
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);

        canvas.translate(-550,550);
        mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DST_IN));
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);

        canvas.translate(550,0);
        mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DST_OUT));
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);

        canvas.translate(-550,550);
        mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DST_OVER));
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);

        canvas.translate(550,0);
        mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DST_ATOP));
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
    }

    /**
     * 源圖模式
     * Mode.SRC、Mode.SRC_IN陨献、Mode.SRC_OUT盒犹、Mode.SRC_OVER、Mode.SRC_ATOP
     * 除了Mode.SRC_OUT顯示完全透明圖片以外眨业,其它全部顯示源圖像急膀;
     */
    private void drawPorterDuffFilter4(Canvas canvas){
        int width  = 500;
        int height = width * mBmp.getHeight()/mBmp.getWidth();

        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);

        canvas.translate(550,0);
        mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC));
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);

        canvas.translate(-550,550);
        mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);

        canvas.translate(550,0);
        mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_OUT));
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);

        canvas.translate(-550,550);
        mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_OVER));
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);

        canvas.translate(550,0);
        mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_ATOP));
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
    }


    /**
     * SRC相關(guān)的模式,只有Mode.SRC_ATOP和SRC_IN能夠?qū)崿F(xiàn)SetTint的功能
     */
    private void drawPorterDuffFilter5(Canvas canvas){
        int width  = 100;
        int height = width * mBmp.getHeight()/mBmp.getWidth();

        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);

        canvas.translate(150,0);
        mPaint.setColorFilter(new PorterDuffColorFilter(0xffff00ff, PorterDuff.Mode.SRC));
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);

        canvas.translate(150,0);
        mPaint.setColorFilter(new PorterDuffColorFilter(0xff00f0ff, PorterDuff.Mode.SRC_ATOP));
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);

        canvas.translate(150,0);
        mPaint.setColorFilter(new PorterDuffColorFilter(0xfff0f0ff, PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);

        canvas.translate(150,0);
        mPaint.setColorFilter(new PorterDuffColorFilter(0xffffff00, PorterDuff.Mode.SRC_OVER));
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);


        canvas.translate(150,0);
        mPaint.setColorFilter(new PorterDuffColorFilter(0xff000000, PorterDuff.Mode.SRC_ATOP));
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
    }
}

總的來說龄捡,PorterDuffColorFilter是很強(qiáng)大的卓嫂,前提是弄清楚PorterDuff.Mode的模式特點,就是掌握了精髓聘殖。上面實例并沒有涉及到透明度的問題晨雳,所以還算簡單。不足的是疊加的只能是顏色不能是其他的如bitmap等奸腺。有關(guān)PorterDuff.Mode的模式算法餐禁,后面會細(xì)說。

3)突照、paint設(shè)置setXfermode

Xfermode 的唯一子類就是PorterDuffXfermode帮非,使用Xfermode時需要增加離屏

給出一張官方的PorterDuff.Mode圖
PorterDuff.Mode

對應(yīng)的相應(yīng)算法如下:

其中
Sa : 源透明度
Da :目標(biāo)透明度
Sc : 源Color
Dc : 目標(biāo)Color


PorterDuff.Mode公式算法

使用:準(zhǔn)備疊加之前paint先增加一個mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN))即可。

        canvas.drawBitmap(dstBmp, 0, 0, mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(srcBmp, 0, 0, mPaint);
        mPaint.setXfermode(null);

如PorterDuff.Mode.SRC_OUT實現(xiàn)刮刮卡的功能

public class GuaGuaCardView_SRCOUT extends View{
    private Paint mBitPaint;
    private Bitmap BmpDST,BmpSRC,BmpText;
    private Path mPath;
    private float mPreX,mPreY;
    public GuaGuaCardView_SRCOUT(Context context, AttributeSet attrs) {
        super(context, attrs);
 
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        mBitPaint = new Paint();
        mBitPaint.setColor(Color.RED);
        mBitPaint.setStyle(Paint.Style.STROKE);
        mBitPaint.setStrokeWidth(45);
 
        BmpText = BitmapFactory.decodeResource(getResources(),R.drawable.guaguaka_text,null);
        BmpSRC = BitmapFactory.decodeResource(getResources(),R.drawable.guaguaka_pic,null);
        BmpDST = Bitmap.createBitmap(BmpSRC.getWidth(), BmpSRC.getHeight(), Bitmap.Config.ARGB_8888);
        mPath = new Path();
    }
 
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
 
        canvas.drawBitmap(BmpText,0,0,mBitPaint);
 
        int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
 
        //先把手指軌跡畫到目標(biāo)Bitmap上
        Canvas c = new Canvas(BmpDST);
        c.drawPath(mPath,mBitPaint);
 
        //然后把目標(biāo)圖像畫到畫布上
        canvas.drawBitmap(BmpDST,0,0,mBitPaint);
 
        //計算源圖像區(qū)域
        mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
        canvas.drawBitmap(BmpSRC,0,0,mBitPaint);
 
        mBitPaint.setXfermode(null);
        canvas.restoreToCount(layerId);
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                mPath.moveTo(event.getX(),event.getY());
                mPreX = event.getX();
                mPreY = event.getY();
                return true;
            case MotionEvent.ACTION_MOVE:
                float endX = (mPreX+event.getX())/2;
                float endY = (mPreY+event.getY())/2;
                mPath.quadTo(mPreX,mPreY,endX,endY);
                mPreX = event.getX();
                mPreY =event.getY();
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        postInvalidate();
        return super.onTouchEvent(event);
    }
}

  • 1、首先末盔,目標(biāo)圖像和源圖像混合筑舅,需不需要生成顏色的疊加特效,如果需要疊加特效則從顏色疊加相關(guān)模式中選擇庄岖,有Mode.ADD(飽和度相加)豁翎、Mode.DARKEN(變暗),Mode.LIGHTEN(變亮)隅忿、Mode.MULTIPLY(正片疊底)心剥、Mode.OVERLAY(疊加),Mode.SCREEN(濾色)
  • 2背桐、當(dāng)不需要特效优烧,而需要根據(jù)某一張圖像的透明像素來裁剪時,就需要使用SRC相關(guān)模式或DST相關(guān)模式了链峭。由于SRC相關(guān)模式與DST相關(guān)模式是相通的畦娄,唯一不同的是決定當(dāng)前哪個是目標(biāo)圖像和源圖像;
  • 3弊仪、當(dāng)需要清空圖像時熙卡,使用Mode.CLEAR

小結(jié):

一共有三處地方使用PorterDuff.Mode

PorterDuff.Mode 描述
ComposeShader 兩個Shader組合方式,使用時不能使用硬件加速
PorterDuffColorFilter 增加一個單色的ColorFilter
Xfermode 兩個繪制內(nèi)容的疊加

三励饵、canvas變換函數(shù)和裁剪

1)驳癌、幾何變換

  • 平移(translate)
// 畫筆
        Paint paint = new Paint();
        paint.setTextSize(50);
        paint.setColor(Color.RED);
        String str = "Android View";
// 基線
        Paint.FontMetricsInt fm = paint.getFontMetricsInt();
        int dy = (fm.top + fm.bottom)/2;
        int baseLine = getHeight()/2 - dy;
// 構(gòu)造一個背景bitmap
        Bitmap bitmap = Bitmap.createBitmap(getWidth(),getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas1 = new Canvas(bitmap);
        canvas1.translate(100,100);
        canvas1.drawText(str,0,str.length(),0,baseLine,paint);
        canvas.drawBitmap(bitmap,null,new Rect(0,0,getWidth(),getHeight()),paint);

  • 旋轉(zhuǎn)(Rotate)
    void rotate(float degrees)
    void rotate (float degrees, float px, float py) // 以某一個point旋轉(zhuǎn)

  • 縮放(scale )
    public void scale (float sx, float sy) // x和y軸的縮放比例

  • 扭曲(skew)
    void skew (float sx, float sy)

2)、canvas 裁剪

// 一些函數(shù)役听,裁剪出一個區(qū)域
boolean clipPath(Path path)
boolean clipPath(Path path, Region.Op op)
boolean clipRect(Rect rect, Region.Op op)
boolean clipRect(RectF rect, Region.Op op)
boolean clipRect(int left, int top, int right, int bottom)
boolean clipRect(float left, float top, float right, float bottom)
boolean clipRect(RectF rect)
boolean clipRect(float left, float top, float right, float bottom, Region.Op op)
boolean clipRect(Rect rect)
boolean clipRegion(Region region)
boolean clipRegion(Region region, Region.Op op)

下面通過clip裁剪一塊區(qū)域顯示颓鲜,來實現(xiàn)一段文字,不同顏色的功能

public class Test extends View {

    public Test(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }


    protected void onDraw(Canvas canvas) {

        // 畫筆
        Paint paint = new Paint();
        paint.setTextSize(50);
        paint.setColor(Color.RED);

        String str = "Android View Administrator";

        // 基線
        Paint.FontMetricsInt fm = paint.getFontMetricsInt();
        int dy = (fm.top + fm.bottom)/2;
        int baseLine = getHeight()/2 - dy;

        // 畫一個寬度為0~270px這塊區(qū)域
        canvas.save();
        canvas.clipRect(0,0,270,getHeight());
        canvas.drawText(str,0,str.length(),0,baseLine,paint);
        canvas.restore();

        // 畫一個寬度為270px~getWidth()這塊區(qū)域
        canvas.save();
        paint.setColor(Color.GREEN);
        canvas.clipRect(270,0,getWidth(),getHeight());
        canvas.drawText(str,0,str.length(),0,baseLine,paint);
        canvas.restore();

    }


}
結(jié)果

3)典予、canvas 保存與恢復(fù)

  • int save () // 會把當(dāng)前的畫布的狀態(tài)進(jìn)行保存甜滨,然后放入特定的棧中;
  • void restore() //會把棧中最頂層的畫布狀態(tài)取出來瘤袖,并按照這個狀態(tài)恢復(fù)當(dāng)前的畫布衣摩,并在這個畫布上做畫。

上個例子中也使用了canvas的保存和恢復(fù)捂敌。不進(jìn)行保存和恢復(fù)艾扮,就會在上一個畫布的基礎(chǔ)上進(jìn)行繪畫,后面的文字就不會被顯示出來黍匾。

4)栏渺、Matrix變換

步驟:

1、創(chuàng)建 Matrix 對象锐涯;
2磕诊、調(diào)用 Matrix 的 pre/postTranslate/Rotate/Scale/Skew() 方法來設(shè)置幾何變換;
3、使用 Canvas.setMatrix(matrix) 或 Canvas.concat(matrix) 來把幾何變換應(yīng)用到 Canvas霎终。

Canvas.setMatrix(matrix)是用新的matrix替代之前的matrix滞磺。Canvas.concat(matrix)是用新的和舊的matrix矩陣相乘。

5)莱褒、Camera三維變換

①击困、Camera.rotate*() 三維旋轉(zhuǎn)
  • rotateX(deg) 沿X軸旋轉(zhuǎn)deg角度
  • rotateY(deg) 沿Y軸旋轉(zhuǎn)deg角度
  • rotateZ(deg) 沿Z軸旋轉(zhuǎn)deg角度
  • rotate(x, y, z) 沿X/Y/Z軸旋轉(zhuǎn)相應(yīng)的角度
rotateX(deg)舉例:

執(zhí)行camera.rotateX(30)時,需要設(shè)置一個旋轉(zhuǎn)軸心广凸,否則會出現(xiàn)不對稱問題;而且canvas的幾何變換順序是相反的阅茶,即后面的先執(zhí)行。所以先寫canvas.translate(centerX, centerY);

canvas.save();
camera.save(); // 保存 Camera 的狀態(tài)  
camera.rotateX(30); // 旋轉(zhuǎn) Camera 的三維空間  
canvas.translate(centerX, centerY); // 旋轉(zhuǎn)之后把投影移動回來  
camera.applyToCanvas(canvas); // 把旋轉(zhuǎn)投影到 Canvas  
canvas.translate(-centerX, -centerY); // 旋轉(zhuǎn)之前把繪制內(nèi)容移動到軸心(原點)  
camera.restore(); // 恢復(fù) Camera 的狀態(tài)
canvas.drawBitmap(bitmap, point1.x, point1.y, paint);  
canvas.restore(); 

四谅海、View繪制順序

android是順序的繪制View的脸哀,這會導(dǎo)致后面繪制的View會影響前面的View。即繪制順序的不同扭吁,會有不同的界面效果撞蜂。

從源碼draw()里面看繪制流程:

  • drawBackground() 繪制背景,該方法不可重寫
  • onDraw() 繪制主體內(nèi)容
  • dispatchDraw() 繪制子View侥袜,需要調(diào)用setWillNotDraw(false)
  • onDrawFroeground() 繪制前景 API 23時候蝌诡,才引用了該方法

1、由于ViewGroup默認(rèn)不會執(zhí)行onDraw方法枫吧,需要調(diào)用View.setWillNotDraw(false)來強(qiáng)制執(zhí)行onDraw
2浦旱、在盡可能的情況下,將代碼放在onDraw()里面寫由蘑,View的機(jī)制中會在不需要重新繪制的時候闽寡,跳過onDraw方法代兵,從而提升效率尼酿。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市植影,隨后出現(xiàn)的幾起案子裳擎,更是在濱河造成了極大的恐慌,老刑警劉巖思币,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鹿响,死亡現(xiàn)場離奇詭異,居然都是意外死亡谷饿,警方通過查閱死者的電腦和手機(jī)惶我,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來博投,“玉大人绸贡,你說我怎么就攤上這事。” “怎么了听怕?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵捧挺,是天一觀的道長。 經(jīng)常有香客問我尿瞭,道長闽烙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任声搁,我火速辦了婚禮黑竞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘疏旨。我一直安慰自己摊溶,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布充石。 她就那樣靜靜地躺著莫换,像睡著了一般。 火紅的嫁衣襯著肌膚如雪骤铃。 梳的紋絲不亂的頭發(fā)上拉岁,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機(jī)與錄音惰爬,去河邊找鬼喊暖。 笑死,一個胖子當(dāng)著我的面吹牛撕瞧,可吹牛的內(nèi)容都是我干的陵叽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼丛版,長吁一口氣:“原來是場噩夢啊……” “哼巩掺!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起页畦,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤胖替,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后豫缨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體独令,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年好芭,在試婚紗的時候發(fā)現(xiàn)自己被綠了燃箭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡舍败,死狀恐怖招狸,靈堂內(nèi)的尸體忽然破棺而出碗硬,到底是詐尸還是另有隱情,我是刑警寧澤瓢颅,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布恩尾,位于F島的核電站,受9級特大地震影響挽懦,放射性物質(zhì)發(fā)生泄漏翰意。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一信柿、第九天 我趴在偏房一處隱蔽的房頂上張望冀偶。 院中可真熱鬧,春花似錦渔嚷、人聲如沸进鸠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽客年。三九已至,卻和暖如春漠吻,著一層夾襖步出監(jiān)牢的瞬間量瓜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工途乃, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留绍傲,地道東北人。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓耍共,卻偏偏與公主長得像烫饼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子试读,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

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