Android自定义View - Canvas与图层

493 阅读4分钟

我们在绘制View控件的时候,我们会重写onDraw方法。

我们再绘制ViewGroup的时候,如果ViewGroup有背景,会在onDraw中绘制背景,如果没有背景,则不会执行onDraw,会执行dispatchDraw方法来绘制子View。

如果我们使用以下方法创建了Canvas

val cancas = Canvas(bitmap)

这个情况下绘制的图像会在传入的bitmap上,并不会在view上,所以我们还需要将bitmap绘制到view上。

图层,画布

saveLayer

public int saveLayer(@Nullable RectF bounds, @Nullable Paint paint, @Saveflags int saveFlags)

public int saveLayer(@Nullable RectF bounds, @Nullable Paint paint)

public int saveLayer(float left, float top, float right, float bottom, @Nullable Paint paint, @Saveflags int saveFlags)

public int saveLayer(float left, float top, float right, float bottom, @Nullable Paint paint)
  • bounds:要保存的区域对应的矩形
  • saveFlags:表示保存什么内容,ALL_SAVE_FLAG表示保存全部

我们在调用xfermode相关方法的时候都会先调用saveLayer方法,当我们在调用saveLayer方法的时候会为我们生成一个新的透明度为0的画布,然后我们在新的画布上画目标图像,当我们再次调用Canvas的drawxxx方法的时候,会继续生成一个新的图层,并在最新的图层上绘制源图像。新的图层会覆盖在最新的画布上,也就是调用saveLayer生成的画布上,xfermode的相关效果就会在新图层和新画布上完成,然后再整体覆盖到原始画布上去。

因为目标图层模式是透明的所以目标图像之外的地方alpha也是0,源图像可能就不会展示,这个会根据xfermode公式来计算。

如果我们在使用xfermode的时候不调用saveLayer方法,那么目标图像会和原始画布在同一层,假如在原始画布先绘制了颜色等,xfermode就会将前面所有的绘制当成是目标图像,最后出来的效果就可能会有问题,因为整个画布alpha都不为0了。

  • 这里需要注意的一点是我们在调用saveLayer的时候会为我们新建一块画布,但是当我们调用save的时候并不会为我们新创建一块画布。

画布与图层

  • 图层:每次调用drawxxx系列函数,会生产一个透明图层来专门绘制图形。
  • 画布:每块画布就是一个Bitmap,所有图像都会画在Bitmap上。画布分两种,一种是View原始画布,通过onDraw(canvas : Canvas)返回,canvas对应的就是原始画布,控件背景就是画在这上面。另一种是生成的画布,通过调用saveLayer,Canvas(Bitmap)等函数创建一块画布。一旦调用了saveLayer生成新画布,以后所有的drawxxx都会画在这块画布上,只有在调用restore(),restoreToCount()后才会到原始画布绘制。
  • Canvas:Canvas是画布的表现形式。Canvas只有一个生成的方法,就是Canvas(Bitmap),不管是原始画布还是人造都是这样,所以说画布就是一个Bitmap。Canvas可以理解为一个绘制工具,把画面都绘制到Bitmap上。我们调用Canvas的clipxxx方法的时候其实就是在对画布进行剪裁,就是剪裁对应的Bitmap。

saveLayerAlpha

public int saveLayerAlpha(@Nullable RectF bounds, int alpha)

public int saveLayerAlpha(@Nullable RectF bounds, int alpha, @Saveflags int saveFlags)

public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha)

public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha, @Saveflags int saveFlags)
  • alpha:指定画布的alpha,范围是0~255 这里指定的alpha是整个画布的alpha,不是画布中空白地方的alpha,所以当我们指定alpha为100的时候,我们画出来的图像也都是半透明的。

Flag具体含义

1654218212422.jpg 建议大家直接使用ALL_SAVE_FLAG就好,其他flag有的已经不能使用,这里感兴趣的可以自己了解。

恢复画布

恢复画布的方法有两个,一个是restore,一个是restoreToCount。

restore是把回退栈最上层的画布出栈。

restoreToCount

我们在调用save系列函数的时候,都会有一个返回值int,这个int就是一个ID,表示当前保存画布信息的栈的索引,是从0开始的。

restoreToCount方法调用的时候会传入索引,就会把索引和索引以上所有的画布退出。

方法restore和restoreToCount操作的栈都是同一个栈,就算我们在调用save方法的时候传入了不同的Flag,也是在同一个栈。