Canvas与图层(二)画布与图层

图层(Layer):
  • 每次调用canvas.drawXXX系列函数,都会生成一个透明图层来绘制这个图形
画布(Bitmap):
  • 每块画布都是一个Bitmap,所有的图像都是画在这个Bitmap上的,画布有两种:
    * 一种是View的原始画布,通过onDraw(Canvas canvas)的方法传入的,canvas对应的就是原始的画布,控件的背景就是华仔这块 画布上的
    * 另一种是人造画布,通过saveLayer()、new Canvas(bitmap)等函数来人为的创建一块新的画布,调用saveLayer()函数以后所有的绘图都是在新创建出来的画布上进行的,只有在调用restore()、restoreToCount()之后才会返回原始的界面
Canvas:
  • Canvas是画布的表现形式,所绘制的所有东西都是通过Canvas实现的,可以将Canvas理解成绘图的工具,生成Canvas的方式只有一种new Canvas(Bitmap),即只能通过Bitmap生成,无论是原始画布还是人造画布,所有的画布最后都是通过Canvas画到画布上的。Canvas这个工具利用他封装的函数进行绘图操作,其实就是在对应的Bitmap上进行绘图操作,如果利用Canvas.clipXXX函数进行剪裁,就是剪裁对应的Bitmap,之后再利用Canvas的绘图区域就会变小。
总结:

Bitmap是画布 -->Canvas在Bitmap上绘图
Bitmap画布–>Layer产生图层–>Canvas绘图–>覆盖在画布上显示

saveLayer()和saveLayerAlpha()函数:

saveLayer()
//bounds:新建画布的尺寸
//paint:画笔实例
//saveFlags:新建画布的标识
public int saveLayer(RectF bounds, Paint paint, int saveFlags)
  • saveLayer()会新建一个新的画布,之后所有的绘图动作都会在新的画布上进行
注意:

(1) saveLayer()函数后的所有动作都只对新建画布有效

public class SaveLayerUseExample extends View {
    private Paint paint;
    private Bitmap bitmap;
    public SaveLayerUseExample(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
        paint.setColor(Color.RED);
        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dog);


    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawBitmap(bitmap, 0, 0, paint);
        int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), paint, Canvas.ALL_SAVE_FLAG);
        //将新建的图层水平斜切45度,在进行绘画一个矩形,但是只是对新建的画布产生影响,并不会对原有的画布有影响
        canvas.skew(1.732f, 0);
        canvas.drawRect(0, 0, 150, 160, paint);
        canvas.restoreToCount(layerId);


    }
}

(2)通过Rect指定矩形大小就是新建的画布大小:

在saveLayer()函数的参数中,可以通过指定Rect对象或者指定4个点来指定一个矩形,这个矩形的大小就是新建的画布大小,而且在创建画布的时候一定要选择适当的大小,否则APP会发生OOM
setLayerAlpha()函数的使用:
//相比于saveLayer()函数,多了一个alpha的参数,用于指定新建画布的透明度,取值范围0~255,可以使用16进制的oxAA表示,取0时表示全透明
public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags)

public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha, int saveFlags)
  • saveLayerAlpha()会创建一个新的画布,以后的绘图就在这个新建的画布上完成,但是这块画布是有透明度的,通过alpha参数进行指定

eg:

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawBitmap(bitmap, 0, 0, paint);
        int layerId = canvas.saveLayerAlpha(0, 0, 200, 200, 100, Canvas.ALL_SAVE_FLAG);
        canvas.drawColor(Color.GRAY);
        canvas.restoreToCount(layerId);
    }
}
Flag的具体含义:
  • Canvas中有以下几个save系列函数:
public int save();
public int save(int saveFlags)
public int saveLayer(RectF bounds, Paint paint, int saveFlags)
public int saveLayer(float left, float top, float right , float bottom, Paint paint, int saveFlags)
public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags)
public int saveLayerAlpha(float left, float top. float right, float bottom, int alpha, int saveFalgs)
  • save()和saveLayer()区别:
save不会创建一个画布,saveLayer创建一个新的画布
  • save()和saveLayer()的flag的区别:
Flag 含义 适用范围
ALL_SAVE_FLAG 保存所有的标识 save()、saveLayer()
MATRIX_SAVE_FLAG 仅保存Canvas的matrix数组大小 save()、saveLayer()
CLIP_SAVE_FLAG 仅保存Canvas的当前大小 save(), saveLayer()
HAS_ALPHA_LAYER_SAVE_FLAG 标识新建的bmp具有透明度,在与上层画布结合的时候,透明位置显示上层图像,与FULL_COLOR_LAYER_SAVE_FLAG冲突,若同时指定,则以HAS_ALPHA_LAYER_SAVE_FLAG为主 saveLayer()
FULL_COLOR_LAYER_SAVE_FLAG 标识新建的bmp颜色完全独立,在与上层的画布结合的时候,先清空上层画布再覆盖上去 saveLayer()
CLIP_TO_LAYER_SAVE_FLAG 在保存图层前先把当前画布根据bounds剪裁,与CLIP_SAVE_FLAG冲突,若同时指定,则以CLIP_SAVE_FLAG为主 saveLayer()
注意:在保存一块画布的状态的时候,需要保存哪些内容
  • 位置信息:MATRIX_SAVE_FLAG
  • 大小信息:CLIP_SAVE_FLAG
Flag之MATRIX_SAVE_FLAG
  • 平移,旋转,缩放,扭曲都是利用位置矩阵Matrix实现的,而MATRIX_SAVE_FLAG标识仅保存这个位置矩阵,除此之外的任何内容都不会进行保存
public class MATRIX_SAVE_FLAG_View extends View {
    private Paint paint;
    public MATRIX_SAVE_FLAG_View(Context context, AttributeSet attrs) {
        super(context, attrs);
        setLayerType(LAYER_TYPE_SOFTWARE, null);
        paint = new Paint();
    }


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


        //bao保存画布的位置信息
        canvas.save();
        //将画布旋转40度
        canvas.rotate(40);
        //画一个矩形
        canvas.drawRect(100, 0, 200, 100, paint);
        //恢复画布
        canvas.restore();
        paint.setColor(Color.BLACK);
        canvas.drawRect(100, 0, 200, 100, paint);
    }
}
  • 首先将画布的位置信息保存,然后绘制一个矩形,之后将画布恢复,在最后一个位置绘制 一个矩形,分析结果可以得知之前的保存操作是将画布的位置信息保存,但是如果使用MATRIX_SAVE_FLAG的flag进行保存,则不会保存画布的大小信息,也就是说裁剪之后的画布是不会被复原的
结论:
  • MATRIX_SAVE_FLAG标识只会保存位置矩阵,在恢复时也只会恢复画布的位置信息,除此之外的任何信息(比如画布的大小信息是不会被恢复的,save()和saveLayer()相同

  • saveLayer()函数在使用Canvas.MATRIX_SAVE_FLAG标识时,需要与Canvas.HAS_ALPHA_LAYER_SAVE_FLAG一起使用,否则新建画布所在区域原来的图像将被清空

Flag之CLIP_SAVE_FLAG
  • 这个标识是仅保存Canvas的裁剪信息,并不保存位置信息,只会恢复大小,并不会恢复旋转,位置信息
canvas.save(Canvas.CLIP_SAVE_FLAG);
canvas.drawColor(Color.BLUE);
canvas.clipRect(100, 0, 200, 100);
canvas.restore();
canvas.drawColor(Color.GRAY);
  • 首先将画布绘制蓝色并保存,再进行裁剪,裁剪后恢复,并画布绘制成灰色,最后发现全屏为灰色,则证明裁剪的画布被恢复。但是当你先绘制一个矩形,之后按照此flag保存画布,旋转40度后再恢复,再绘制一个矩形,会发现绘制的矩形旋转40度,所以证明这个flag只会对裁剪进行恢复,并不会对位置的信息进行恢复
结论:
  • CLIP_SAVE_FLAG:只会保存剪裁的信息,再恢复时也只会恢复画布的裁剪信息,除此之外的任何信息是不会被恢复的,save()和saveLayer()相同
  • saveLayer()函数在使用Canvas.CLIP_SAVE_FLAG标识时,需要与Canvas.HAS_ALPHA_LAYER_SAVE_FLAG标识一起使用,否则新建 画布所在区域的原来的图像将被清空
flag之FULL_COLOR_LAYER_FLAG和HAS_ALPHA_LAYER_SAVE_FLAG
  • HAS_ALPHA_LAYER_SAVE_FLAG:表示新建的画布在与上一层画布合成时,不会将上一层画布的内容清空,而是直接覆盖在上一层画布上的
  • FULL_COLOR_LAYER_FLAG:表示新建的画布在与上一层画布合成时,先将上一层画布的对应区域清空,再覆盖在上面
  1. 这两个标签都是saveLayer()专用,使用时要禁用硬件加速,在API26以及以后的版本中,该falg已经失效
  2. 两者是冲突的,使用时以HAS_ALPHA_LAYER_SAVE_FLAG为主
  3. 当既没有指定使用的是两者中的哪一个时,默认以FULL_COLOR_LAYER_SAVE_FLAG标识
flag之CLIP_TO_LAYER_SAVE_FLAG:
  • 在新建bitmap之前,先对Canvas进行剪裁,在Canvas内部的画布被剪裁后,利用saveLayer()函数生成的画布大小与剪裁后的画布大小相同;而且再利canvas.restore()函数进行恢复时,只会把saveLayer()函数新建画布的内容叠加,而不会将剪裁的Canvas恢复(API23失效)
  • 在与CLIP_SAVE_FLAG标识共用时,以CLIP_SAVE_FLAG为主
flag之ALL_SAVE_FLAG:
  • 它是所有标识的公共集合
  • 对于save()来说,ALL_SAVE_FLAG = MATRIX_SAVE_FLAG|CLIP_SAVE_FLAG,即保存位置信息和裁剪信息,因为save(int flag)函数只能使用MATRIX_SAVE_FLAG和CLIP_SAVE_FLAG
  • 对于saveLayer()来说, ALL_SAVE_FLAG = MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG | HAS_ALPHA_LAYER_SAVE_FLAG,即保存的位置信息和裁剪信息,新建画布在与上一层画布合成时, 不清空原画布的内容

注意:上述的Flag除了ALL_SAVE_FLAG以外的所有标识在API26以后已经废弃,在低版本中仍然可以使用

猜你喜欢

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