图层(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:表示新建的画布在与上一层画布合成时,先将上一层画布的对应区域清空,再覆盖在上面
- 这两个标签都是saveLayer()专用,使用时要禁用硬件加速,在API26以及以后的版本中,该falg已经失效
- 两者是冲突的,使用时以HAS_ALPHA_LAYER_SAVE_FLAG为主
- 当既没有指定使用的是两者中的哪一个时,默认以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以后已经废弃,在低版本中仍然可以使用