Canvas
保存相关的标志在了解Canvas
的保存以前,咱们先看一下和保存相关的标志的定义,它们定义了保存的类型,这些标志定义在Canvas.java
当中,一共有六个标志。java
/**
* Restore the current matrix when restore() is called.
*/
public static final int MATRIX_SAVE_FLAG = 0x01;
/**
* Restore the current clip when restore() is called.
*/
public static final int CLIP_SAVE_FLAG = 0x02;
/**
* The layer requires a per-pixel alpha channel.
*/
public static final int HAS_ALPHA_LAYER_SAVE_FLAG = 0x04;
/**
* The layer requires full 8-bit precision for each color channel.
*/
public static final int FULL_COLOR_LAYER_SAVE_FLAG = 0x08;
/**
* Clip drawing to the bounds of the offscreen layer, omit at your own peril.
* <p class="note"><strong>Note:</strong> it is strongly recommended to not
* omit this flag for any call to <code>saveLayer()</code> and
* <code>saveLayerAlpha()</code> variants. Not passing this flag generally
* triggers extremely poor performance with hardware accelerated rendering.
*/
public static final int CLIP_TO_LAYER_SAVE_FLAG = 0x10;
/**
* Restore everything when restore() is called (standard save flags).
* <p class="note"><strong>Note:</strong> for performance reasons, it is
* strongly recommended to pass this - the complete set of flags - to any
* call to <code>saveLayer()</code> and <code>saveLayerAlpha()</code>
* variants.
*/
public static final int ALL_SAVE_FLAG = 0x1F;
复制代码
从上面的定义能够看出,flag
是用一个32
位的int
型变量来定义的,它的低5
位的每一位用来表示须要保存Canvas
当前哪部分的信息,若是所有打开,那么就是所有保存,也就是最后定义的ALL_SAVE_FLAG
,这5
位分别对应:android
xxxx1
:保存Matrix
信息,例如平移、旋转、缩放、倾斜等。xxx1x
:保存Clip
信息,也就是裁剪。xx1xx
:保存Alpha
信息。x1xxx
:保存8
位的颜色信息。1xxxx
:Clip drawing to the bounds of the offscreen layer
,不太明白是什么意思。若是须要多选以上的几个信息进行保存,那么对多个标志位执行或操做便可。canvas
save()
和save(int saveFlags)
下面是这两个方法的定义:bash
/**
* Saves the current matrix and clip onto a private stack.
* <p>
* Subsequent calls to translate,scale,rotate,skew,concat or clipRect,
* clipPath will all operate as usual, but when the balancing call to
* restore() is made, those calls will be forgotten, and the settings that
* existed before the save() will be reinstated.
*
* @return The value to pass to restoreToCount() to balance this save()
*/
public int save() {
return native_save(mNativeCanvasWrapper, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG);
}
/**
* Based on saveFlags, can save the current matrix and clip onto a private
* stack.
* <p class="note"><strong>Note:</strong> if possible, use the
* parameter-less save(). It is simpler and faster than individually
* disabling the saving of matrix or clip with this method.
*
* @param saveFlags flag bits that specify which parts of the Canvas state
* to save/restore
* @return The value to pass to restoreToCount() to balance this save()
*/
public int save(@Saveflags int saveFlags) {
return native_save(mNativeCanvasWrapper, saveFlags);
}
复制代码
注释已经很好地说明了save()
和save(int saveFlags)
的做用:当调用完save
方法以后,例如平移、缩放、旋转、倾斜、拼接或者裁剪这些操做,都是和原来的同样,而当调用完restore
方法以后,在save()
到restore()
之间的全部操做都会被遗忘,而且会恢复调用save()
以前的全部设置。此外还能够得到如下信息:app
native_save
方法,而无参方法save()
默认是保存Matrix
和Clip
这两个信息。save()
方法,而不是使用有参的save(int saveFlags)
方法传入别的Flag
。index
,以后能够在restoreToCount(int saveCount)
中传入它来说在它之上的全部保存图层都出栈。native_save
来对这个mNativeCanvasWrapper
变量,咱们会发现,全部对于Canvas
的操做,其实最终都是操做了mNativeCanvasWrapper
这个对象。XXX_SAVE_FLAG
的命名来看,带有参数的save(int saveFlags)
方法只容许保存MATRIX_/CLIP_/ALL_
这三种状态,而HAS_ALPHA_LAYER/FULL_COLOR_LAYER_/CLIP_TO_LAYER_
这三种状态,则是为后面的saveLayer/saveLayerAlpha
提供的。restore()
restoreToCount(int count)
getSaveCount()
这三个方法用来恢复图层信息,也就是将以前保存到栈中的元素出栈,咱们看一下这几个方法的定义:less
/**
* This call balances a previous call to save(), and is used to remove all
* modifications to the matrix/clip state since the last save call. It is
* an error to call restore() more times than save() was called.
*/
public void restore() {
boolean throwOnUnderflow = !sCompatibilityRestore || !isHardwareAccelerated();
native_restore(mNativeCanvasWrapper, throwOnUnderflow);
}
/**
* Returns the number of matrix/clip states on the Canvas' private stack. * This will equal # save() calls - # restore() calls. */ public int getSaveCount() { return native_getSaveCount(mNativeCanvasWrapper); } /** * Efficient way to pop any calls to save() that happened after the save * count reached saveCount. It is an error for saveCount to be less than 1. * * Example: * int count = canvas.save(); * ... // more calls potentially to save() * canvas.restoreToCount(count); * // now the canvas is back in the same state it was before the initial * // call to save(). * * @param saveCount The save level to restore to. */ public void restoreToCount(int saveCount) { boolean throwOnUnderflow = !sCompatibilityRestore || !isHardwareAccelerated(); native_restoreToCount(mNativeCanvasWrapper, saveCount, throwOnUnderflow); } 复制代码
这个几个方法很好理解:ide
restore()
方法用来恢复,最近一次调用save()
以前的Matrix/Clip
信息,若是调用restore()
的次数大于save()
的一次,也就是栈中已经没有元素,那么会抛出异常。getSaveCount()
:返回的是当前栈中元素的数量。restoreToCount(int count)
会将saveCount()
之上对应的全部元素都出栈,若是count < 1
,那么会抛出异常。native
的方法。下面,咱们用一个简单的例子,来更加直观的了解一下save()
和restore()
,咱们重写了一个View
当中的onDraw()
方法:布局
Matrix
信息private void saveMatrix(Canvas canvas) {
//绘制蓝色矩形
mPaint.setColor(getResources().getColor(android.R.color.holo_blue_light));
canvas.drawRect(0, 0, 200, 200, mPaint);
//保存
canvas.save();
//裁剪画布,并绘制红色矩形
mPaint.setColor(getResources().getColor(android.R.color.holo_red_light));
//平移.
//canvas.translate(100, 0);
//缩放.
//canvas.scale(0.5f, 0.5f);
//旋转
//canvas.rotate(-45);
//倾斜
canvas.skew(0, 0.5f);
canvas.drawRect(0, 0, 200, 200, mPaint);
//恢复画布
canvas.restore();
//绘制绿色矩形
mPaint.setColor(getResources().getColor(android.R.color.holo_green_light));
canvas.drawRect(0, 0, 50, 200, mPaint);
}
复制代码
咱们对画布分别进行了平移、缩放、旋转、倾斜,获得的结果为: 性能
能够看到,当咱们调用上面这些方法时,其实就是对Canvas
先进行了一些移动,旋转,缩放的操做,而后再在它这个新的状态上进行绘制,以后调用restore()
以后,又恢复回了调用save()
以前的状态。ui
Clip
信息private void saveClip(Canvas canvas) {
//绘制蓝色矩形
mPaint.setColor(getResources().getColor(android.R.color.holo_blue_light));
canvas.drawRect(0, 0, 200, 200, mPaint);
//保存.
canvas.save();
//裁剪画布,并绘制红色矩形
mPaint.setColor(getResources().getColor(android.R.color.holo_red_light));
canvas.clipRect(150, 0, 200, 200);
canvas.drawRect(0, 0, 200, 200, mPaint);
//恢复画布
canvas.restore();
//绘制绿色矩形
mPaint.setColor(getResources().getColor(android.R.color.holo_green_light));
canvas.drawRect(0, 0, 50, 200, mPaint);
}
复制代码
最终的结果以下所示:
canvas
的大小和View
的大小是同样的,咱们以(0,0)
为原点坐标,绘制了一个大小为200px * 200px
的蓝色矩形。(150, 0)
为原点坐标,裁剪成了一个大小为50px * 200px
的新画布,对于这个裁剪的过程能够这么理解:就是咱们之前上学时候用的那些带镂空的板子,上面有各类的形状,而这一裁剪,其实就是在原来的canvas
上盖了这么一个镂空的板子,镂空部分就是咱们定义的裁剪区域,当咱们进行绘制时,就是在这个板子上面进行绘制,而最终在canvas
上展示的部分就是这些镂空部分和以后绘制部分的交集。(0, 0)
为原点绘制一个大小为200px * 200px
的红色矩形,可是此时因为(0, 0) - (150, 200)
这部分被盖住了,因此不咱们画不上去,实际画上去的只有(50, 0) - (200, 200)
这一部分。restore()
方法,就至关于把板子拿掉,那么这时候就可以像以前那样正常的绘制了。saveLayer
saveLayerAlpha
除了save()
方法以外,canvas
还提供了saveLayer
方法
saveLayer
和
saveLayerAlpha
,它们最终都是调用了两个
native
方法: 对于
saveLayer
,若是不传入
saveFlag
,那么默认是采用
ALL_SAVE_FLAG
:
native_saveLayer(mNativeCanvasWrapper, left, top, right, bottom, paint != null ? paint.getNativeInstance() : 0, saveFlags);
复制代码
对于saveLayerAlpha
,若是不传入saveFlag
,那么默认是采用ALL_SAVE_FLAG
,若是不传入alpha
,那么最终调用的alpha = 0
。
native_saveLayerAlpha(mNativeCanvasWrapper, left, top, right, bottom, alpha, saveFlags);
复制代码
save()
方法的区别关于save()
和saveLayer()
的区别,源码当中是这么解释的,也就是说它会新建一个不在屏幕以内的bitmap
,以后的全部绘制都是在这个bitmap
上操做的。
This behaves the same as save(), but in addition it allocates and redirects drawing to an offscreen bitmap.
而且这个方法是至关消耗资源的,由于它会致使内容的二次渲染,特别是当canvas
的边界很大或者使用了CLIP_TO_LAYER
这个标志时,更推荐使用LAYER_TYPE_HARDWARE
,也就是硬件渲染来进行Xfermode
或者ColorFilter
的操做,它会更加高效。
* this method is very expensive,
*
* incurring more than double rendering cost for contained content. Avoid
* using this method, especially if the bounds provided are large, or if
* the {@link #CLIP_TO_LAYER_SAVE_FLAG} is omitted from the
* {@code saveFlags} parameter. It is recommended to use a
* {@link android.view.View#LAYER_TYPE_HARDWARE hardware layer} on a View
* to apply an xfermode, color filter, or alpha, as it will perform much
* better than this method.
复制代码
当咱们在以后调用restore()
方法以后,那么这个新建的bitmap
会绘制回Canvas
的当前目标,若是当前就位于canvas
的最底层图层,那么就是目标屏幕,不然就是以前的图层。
* All drawing calls are directed to a newly allocated offscreen bitmap.
* Only when the balancing call to restore() is made, is that offscreen
* buffer drawn back to the current target of the Canvas (either the
* screen, it's target Bitmap, or the previous layer). 复制代码
再回头看下方法的参数,这两大类方法分别传入了Paint
和Alpha
这两个变量,对于saveLayer
来讲,Paint
的下面这三个属性会在新生成的bitmap
被从新绘制到当前画布时,也就是调用了restore()
方法以后,被采用:
* Attributes of the Paint - {@link Paint#getAlpha() alpha},
* {@link Paint#getXfermode() Xfermode}, and
* {@link Paint#getColorFilter() ColorFilter} are applied when the
* offscreen bitmap is drawn back when restore() is called.
复制代码
而对于saveLayerAlpha
来讲,它的Alpha
则会在被从新绘制回来时被采用:
* The {@code alpha} parameter is applied when the offscreen bitmap is
* drawn back when restore() is called.
复制代码
对于这两个方法,都推荐传入ALL_SAVE_FLAG
来提升性能,它们的返回值和save()
方法的含义是相同的,都是用来提供给restoreToCount(int count)
使用。 总结一下:就是调用saveLayer
以后,建立了一个透明的图层,以后在调用restore()
方法以前,咱们都是在这个图层上面进行操做,而save
方法则是直接在原先的图层上面操做,那么对于某些操做,咱们不但愿原来图层的状态影响到它,那么咱们应该使用saveLayer
。
saveLayer
示例和前面相似,咱们建立一个继承于View
的SaveLayerView
,并重写onDraw(Canvas canvas)
方法:
首先,由于咱们前面整个一整节获得的结论是saveLayer
会建立一个新的图层,而验证是否产生新图层的方式就是采用Paint#setXfermode()
方法,经过它和下面图层的结合关系,咱们就能知道是否生成了一个新的图层了。当使用saveLayer
时:
@Override
protected void onDraw(Canvas canvas) {
useSaveLayer(canvas);
}
private void useSaveLayer(Canvas canvas) {
//1.先画一个蓝色圆形.
canvas.drawCircle(mRadius, mRadius, mRadius, mBlueP);
//canvas.save();
//2.这里产生一个新的图层
canvas.saveLayer(0, 0, mRadius + mRadius, mRadius + mRadius, null);
//3.现先在该图层上画一个绿色矩形
canvas.drawRect(mRadius, mRadius, mRadius + mRadius, mRadius + mRadius, mGreenP);
//4.设为取下面的部分
mRedP.setXfermode(mDstOverXfermode);
//5.再画一个红色圆形,若是和下面的图层有交集,那么取下面部分
canvas.drawCircle(mRadius, mRadius, mRadius/2, mRedP);
}
复制代码
当咱们使用saveLayer()
方法时,获得的是:
save()
方法,获得的则是:
DST_OVER
模式,也就是底下部分和即将画上去的部分有重合的时候,取底下部分。当咱们在第2步当中使用
saveLayer
的时候,按咱们的假设,会产生一个新的图层,那么第3步的绿色矩形就是画在这个新的透明图层上的,所以第5步画红色圆形的时候,
DST
是按绿色矩形部分来算的,重叠部分只占了红色圆形的
1/4
,所以最后画出来的结果跟第一张图同样。 而不使用
saveLayer
时,因为没有产生新的图层,所以在第5步绘制的时候,
DST
实际上是由蓝色圆形和绿色矩形组成的,这时候和红色圆形的重叠部分占了整个红色圆形,因此最后画上去的时候就看不到了。 这就很好地验证了
saveLayer
会建立一个新的图层。
saveLayerAlpha
下面,咱们再来看一下saveLayerAlpha
,这个方法能够用来产生一个带有透明度的图层:
private void useSaveLayerAlpha(Canvas canvas) {
//先划一个蓝色的圆形.
canvas.drawCircle(mRadius, mRadius, mRadius, mBlueP);
//canvas.save();
//这里产生一个新的图层
canvas.saveLayerAlpha(0, 0, mRadius + mRadius, mRadius + mRadius, 128);
//现先在该图层上画一个矩形
canvas.drawRect(mRadius, mRadius, mRadius + mRadius, mRadius + mRadius, mGreenP);
}
复制代码
最终,咱们就获得了下面的带有透明度的绿色矩形覆盖在上面:
HAS_ALPHA_LAYER_XXX
和FULL_COLOR_LAYER_XXX
HAS_ALPHA_LAYER
表示图层结合的时候,没有绘制的地方会是透明的,而对于FULL_COLOR_LAYER_XXX
,则会强制覆盖掉。 首先,咱们先看一下整个布局为一个黑色背景的Activity
,里面有一个背景为绿色的自定义View
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
tools:context="com.example.lizejun.repocanvaslearn.MainActivity">
<com.example.lizejun.repocanvaslearn.SaveLayerView
android:background="@android:color/holo_green_light"
android:layout_width="200dp"
android:layout_height="200dp" />
</RelativeLayout>
复制代码
下面,咱们重写onDraw(Canvas canvas)
方法:
private void useSaveLayerHasAlphaOrFullColor(Canvas canvas) {
//先划一个蓝色的圆形.
canvas.drawRect(0, 0, mRadius * 2, mRadius * 2, mBlueP);
//这里产生一个新的图层
canvas.saveLayer(0, 0, mRadius, mRadius, null, Canvas.FULL_COLOR_LAYER_SAVE_FLAG);
//canvas.saveLayer(0, 0, mRadius, mRadius, null, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
//绘制一个红色矩形.
canvas.drawRect(0, 0, mRadius / 2, mRadius / 2, mRedP);
}
复制代码
当采用FULL_COLOR_LAYER_SAVE_FLAG
时,对应的结果为下图:
HAS_ALPHA_LAYER_SAVE_FLAG
时,对应的结果为:
FULL_COLOR_LAYER_SAVE_FLAG
,不只下一层本来绘制的蓝色没有用,连
View
自己的绿色背景也没有了,最后透上来的是
Activity
的黑色背景。 关于这个方法,有几个须要注意的地方:
View
中禁用硬件加速。setLayerType(LAYER_TYPE_SOFTWARE, null);
复制代码
FULL_COLOR_LAYER_SAVE_FLAG
为准。saveLayer
而且只指定MATRIX_SAVE_FLAG
或者CLIP_SAVE_FLAG
时,默认的合成方式是FULL_COLOR_LAYER_SAVE_FLAG
。CLIP_TO_LAYER_SAVE_FLAG
它在新建bitmap
前,先把canvas
给裁剪,一旦画板被裁剪,那么其中的各个画布就会被受到影响,而且它是没法恢复的。当其和CLIP_SAVE_FLAG
共用时,是能够被恢复的。
ALL_SAVE_FLAG
对于save()
来讲,它至关于MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG
。 对于saveLayer()
来讲,它至关于MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG|HAS_ALPHA_LAYER_SAVE_FLAG
。