Canvas与图层(一)Canvas创建与画布状态操作

获取Canvas对象的方法
方法一:重写onDraw()、dispatchDraw()函数
  • 构建方法:
protected void onDraw(canvas canvas){
    super.onDraw();
}

protexted void dispatchDraw(Canvas canvas){
    super.dispatchDraw(canvas);
}
  • 可见上述两种方法中都有Canvas对象,这个对象是View中的Canvas对象,利用这个Canvas对象进行画图,效果会反映在View中

  • 区别:

    • onDraw()函数用于绘制视图自身
    • dispatchDraw()函数用于绘制子视图
  • 注意:

    • 无论是View还是ViewGroup, 对这两个函数的调用顺序都是onDraw()->dispatchDraw(), 但是在ViewGroup中,当他有背景的时候就会调用onDraw()函数,没有背景的时候就会跳过onDraw()函数,直接调用dispatchDraw()函数,所以在ViewGroup中绘图直接使用dispatchDraw()
    • 在View中, onDraw()和dispatchDraw()都会被调用,无论是将绘图代码放到onDraw()还是dispatchDraw()中,都将得到执行,但是由于dispatchDraw()用于绘制子视图,所以从这层定义上讲,我们使用onDraw()函数
  • 总结:

    • 绘制View控件,使用onDraw()函数
    • 绘制VIewGroup控件, 使用dispatchDraw()函数
方法二:使用Bitmap创建

1.构造方法:

Canvas c = new Canvas(bitmap);
或者
Canvas c = new Canvas();
c.setBitmap(bitmap);

其中Bitmap可以从图片中加载,也可以自行创建
//从图片中加载
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.XXX, null);
//自行创建
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

2.在onDraw()函数中使用:

  • 使用Bitmap构造了一个Canvas, 那么在这个Canvas上绘制的图像也会保存到这个Bitmap上,而不会画在View上。如果想要画在View, 就必须使用onDraw(Canvas canvas)函数中传入的Canvas来画一遍Bitmap。

eg:

public class BitmapCanvasView extends View {
    private Bitmap bitmap;
    private Paint paint;
    private Canvas bitmapCanvas;
    public BitmapCanvasView(Context context,AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
        paint.setColor(Color.BLACK);
        bitmap = Bitmap.createBitmap(500, 500, Bitmap.Config.ARGB_8888);
        bitmapCanvas = new Canvas(bitmap);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        paint.setTextSize(50);
        //将文字卸载了bitmap上并没有将图片画在画布上,所以此时什么都不会有
        bitmapCanvas.drawText("Wjx", 0,  100, paint);
       //将bitmap显示出来
        canvas.drawBitmap(bitmap,0, 0, paint);
    }
}
方法三:调用SurfaceHolder.lockCanvas()函数
  • 在使用SurfaceView的时候,当调用SurfaceHolder.lockCanvas()函数是,也会创建Canvas对象。
图层与画布:
  • 之前提到过Canvas的save()和restore()函数,除这两个函数以外,还有一些其他的函数用来保存和恢复画布状态。
    1.saveLayer()函数:
//保存指定矩形区域的Canvas内容:
//rect要保存的对应的矩形对象
//int saveFlags:取值有ALL_SAVE_FLAG\ MATRIX_SAVE_FLAG、CLIP_SAVE_FLAG、HAS_ALPHA_LAYER_SAVE_FLAG、FULL_COLOR_LAYER_SAVE_FLAG和CLIP_TO_LAYER_SAVE_FLAG,其中ALL_SAVE_FLAG表示保存全部内容
public int saveLayer(RectF rect, Paint paint, int saveFlags)

//使用四个点来构造矩形
public int saveLayer(float left, float top, float right, float bottom, Paint paint, int saveFlags)

eg:

public class XferModeView extends View {
    private int width = 400;
    private int height = 400;
    private Bitmap dstBitmap;
    private Bitmap srcBitmap;
    private Paint paint;
    public XferModeView(Context context,AttributeSet attrs) {
        super(context, attrs);
        setLayerType(LAYER_TYPE_SOFTWARE, null);
        paint = new Paint();
        paint.setColor(Color.BLACK);
        dstBitmap = makeDst(width, height);
        srcBitmap = makeSrc(width, height);
    }


    /**
     * 创建一张矩形图片
     * @param width
     * @param height
     * @return
     */
    static Bitmap makeSrc(int width, int height) {
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bitmap);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(0xFF66AAFF);
        c.drawRect(0, 0, width, height,paint);
        return bitmap;
    }


    /**
     * 创建一张圆形图片
     * @param width
     * @param height
     * @return
     */
    static Bitmap makeDst(int width, int height) {
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bitmap);
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);


        p.setColor(0xFFFFCC44);
        c.drawOval(new RectF(0, 0 ,width, height), p);
        return bitmap;
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.GREEN);
        int layerId = canvas.saveLayer(0, 0, width * 2, height * 2, paint, Canvas.ALL_SAVE_FLAG);
        canvas.drawBitmap(dstBitmap, 0, 0 ,paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(srcBitmap, width/2, height/2, paint);
        paint.setXfermode(null);
        canvas.restoreToCount(layerId);
    }
}

在这里插入图片描述

  • 当注释掉layerId和restoreToCount时会发现结果变得不同
    在这里插入图片描述

  • 分析调用saveLayer()函数时的绘图流程:

在调用saveLayer()函数时,会生成一块全新的画布(Bitmap),这块画布的大小就是我们指定的所要保存的大小。新生成的画布是全透明的,在调用saveLayer()函数后所有的绘图操作都是在这块画布上进行的,调用drawXXX会生成一个透明的图层来专门绘制这个图形,而每次生成的图层都会叠加到最近的画布上,调用XFermode算法会将计算好的画布整体覆盖在原始的画布上。
  • 分析没有调用saveLayer()函数时绘制的流程:
去掉saveLayer()之后就不会 新建一个画布,所有的绘图操作都会在最底层的画布上进行操作,所以调用Xfermode算法的时候计算出来的图像是 针对的底层不透明的绿色背景
  • **结论:**调用saveLayer()函数会创建出一块全新的透明画布,大小与指定保存的区域大小一致,其后的绘图操作都放在这块画布上行,在绘制结束后,会覆盖在原=原始画布上进行展示。

猜你喜欢

转载自blog.csdn.net/qq_39424143/article/details/93525960