一、FLAG的具体意义
1、FLAG概述
有关save系列函数,在canvas中总共有如下几个:
- 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 saveFlags)
这段我们先关注前四个,save的两个函数和saveLayer的两个函数;我们知道他们两个不同之处在于saveLayer会新建一个画布,而save函数则不会新建画布;它们都具有Flag标识,这些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() |
从上面的表格中可以看到,ALL_SAVE_FLAG、MATRIX_SAVE_FLAG、CLIP_SAVE_FLAG是save()、saveLayer()共用的。而另外三个是saveLayer()专用的;我们一个个来解析下它们的不同之处
在讲解之前,我们先考虑一下,如果让我们保存一个画布的状态,以便恢复,我们需要保存哪些内容呢?
第一个是位置信息,第二个是大小信息;好像除此之外也没什么了。所以,位置信息对应的是MATRIX_SAVE_FLAG,大小信息对应的是:CLIP_SAVE_FLAG,这也就是save\saveLayer所共用的,而另外的三个函数,则是指定saveLayer新建的bitmap具有哪种特性。已经不再是保存画布的范畴了。
2、FLAG之MATRIX_SAVE_FLAG
(1)、save(int flag)与MATRIX_SAVE_FLAG
我们知道canvas.translate(平移)、canvas.rotate(旋转)、canvas.scale(缩放)、canvas.skew(扭曲)其实都是利用位置矩阵matrix实现的,而MATRIX_SAVE_FLAG标识就是指定只保存这个位置矩阵,除此之外的其它任何内容都不会被保存;
我们来举个例子来看下
- public class MATRIX_SAVE_FLAG_View extends View {
- private Paint mPaint;
- public MATRIX_SAVE_FLAG_View(Context context, AttributeSet attrs) {
- super(context, attrs);
- setLayerType(LAYER_TYPE_SOFTWARE,null);
- mPaint = new Paint();
-
- mPaint.setColor(Color.GREEN);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- canvas.save(Canvas.MATRIX_SAVE_FLAG);
- canvas.rotate(40);
- canvas.drawRect(100,0,200,100,mPaint);
- canvas.restore();
-
- mPaint.setColor(Color.YELLOW);
- canvas.drawRect(100,0,200,100,mPaint);
- }
- }
我们直接看OnDraw函数,先调用 canvas.save(Canvas.MATRIX_SAVE_FLAG)将canvas的位置矩阵保存起来,然后将画布旋转40度之后,画一个绿色矩形;
然后调用canvas.restore()之后将画布恢复,然后再在同一个位置画一个黄色的矩形。
效果图如下:
很明显,在canvas.restore()后,画布的旋转给恢复到了原来了状态。
然后我们再来看看,如果我们给画布裁剪,看还能不能被恢复
- public class MATRIX_SAVE_FLAG_View extends View {
- private Paint mPaint;
- public MATRIX_SAVE_FLAG_View(Context context, AttributeSet attrs) {
- super(context, attrs);
- setLayerType(LAYER_TYPE_SOFTWARE,null);
- mPaint = new Paint();
-
- mPaint.setColor(Color.GREEN);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- canvas.save(Canvas.MATRIX_SAVE_FLAG);
- canvas.clipRect(100,0,200,100);
- canvas.drawColor(Color.GREEN);
- canvas.restore();
-
- canvas.drawColor(Color.YELLOW);
- }
- }
效果图如下:
从效果图来看,我们恢复画布后,把画布全部染成了黄色,但并没有染全屏幕的画布,而只是clip后的一部分,这说明,被裁剪的画布没有被还原!
前面我们说了调用 canvas.save(Canvas.MATRIX_SAVE_FLAG)只会保存了位置矩阵!恢复时,也只会恢复画布的位置信息,有关画布的大小,是不会被恢复的!
(2)、saveLayer()与MATRIX_SAVE_FLAG
同样先来看旋转的例子:
- public class MATRIX_SAVE_FLAG_View extends View {
- private Paint mPaint;
- public MATRIX_SAVE_FLAG_View(Context context, AttributeSet attrs) {
- super(context, attrs);
- setLayerType(LAYER_TYPE_SOFTWARE,null);
- mPaint = new Paint();
-
- mPaint.setColor(Color.GREEN);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- canvas.drawColor(Color.RED);
- canvas.saveLayer(0,0,getWidth(),getHeight(),mPaint,Canvas.MATRIX_SAVE_FLAG|Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
- canvas.rotate(40);
- canvas.drawRect(100,0,200,100,mPaint);
- canvas.restore();
-
- mPaint.setColor(Color.YELLOW);
- canvas.drawRect(100,0,200,100,mPaint);
-
- }
- }
效果图如下:
这里在保存Flag时,多了一个Canvas.HAS_ALPHA_LAYER_SAVE_FLAG,表示在新建的画布在合成到上一个画布上时,直接覆盖,不清空所在区域原图像,这个标识这里先忽略,我们后面会具体讲。
效果与原因都是与save()相同,指定保存Canvas.MATRIX_SAVE_FLAG,即canvas的位置信息,当调用canvas.revert()后,原始画布的旋转被恢复。所以再次画图到原始画布上时,是没有旋转的。
我们还是直接来看例子吧,裁剪:
下面我们举个例子来看看
- public class MATRIX_SAVE_FLAG_View extends View {
- private Paint mPaint;
- public MATRIX_SAVE_FLAG_View(Context context, AttributeSet attrs) {
- super(context, attrs);
- setLayerType(LAYER_TYPE_SOFTWARE,null);
- mPaint = new Paint();
-
- mPaint.setColor(Color.GREEN);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- canvas.drawColor(Color.GREEN);
-
- canvas.saveLayer(0,0,getWidth(),getHeight(),mPaint,Canvas.MATRIX_SAVE_FLAG|Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
- canvas.clipRect(100,0,200,100);
- canvas.restore();
-
- canvas.drawColor(Color.YELLOW);
- }
- }
效果图如下:
与上面的例子一样,在saveLayer中也只是保存Canvas.MATRIX_SAVE_FLAG,即canvas的位置信息,之后调用canvas.clipRect(100,0,200,100);将画板裁剪,注意我们在讲解canvas时提到了,无论哪个图层调用canvas的位置变换和裁剪操作,所有的画布都会受到连累,这些连累也只表现在画布以后的绘图上,之前画过的图像不会受到影响。
所以在clipRect之前画出来的全屏绿色是不受影响的,当restore()以后,canvas只恢复了原始画布的位置信息而原始画布的大小却无法被恢复,所以当再调用 canvas.drawColor(Color.YELLOW),也只能画出来一小块了
注意:在上面的例子中用到了canvas.clipRect(),这个函数是不支持硬件加速的,原因参见《自定义控件三部曲之绘图篇(十)——Paint之setXfermode(一)》,所以需要添加setLayerType函数来禁用硬件加速。
所以MATRIX_SAVE_FLAG标识的结论来了:
1、当save\saveLayer调用Canvas.MATRIX_SAVE_FLAG标识时只会保存画布的位置矩阵信息,在canvas.restore()时也只会恢复位置信息,而改变过的画布大小是不会被恢复的。
2、当使用canvas.saveLayer(Canvas.MATRIX_SAVE_FLAG)时,需要与Canvas.HAS_ALPHA_LAYER_SAVE_FLAG一起使用,不然新建画布所在区域原来的图像将被清空。(后面会讲原因)
3、FLAG之CLIP_SAVE_FLAG
这个标识的意思是仅保存Canvas的裁剪信息,而对于位置信息则不管不问,所以在canvas.restore()时,会只恢复Canvas的大小,而对于Canvas的旋转、平移等位置改变的信息是不会恢复的。
(1)、save(int flag)与CLIP_SAVE_FLAG
我们先来看个裁剪的例子:
- public class CLIP_SAVE_FLAG_View extends View {
- private Paint mPaint;
- public CLIP_SAVE_FLAG_View(Context context, AttributeSet attrs) {
- super(context, attrs);
- setLayerType(LAYER_TYPE_SOFTWARE,null);
- mPaint = new Paint();
-
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- canvas.drawColor(Color.RED);
- canvas.save(Canvas.CLIP_SAVE_FLAG);
- canvas.clipRect(100,0,200,100);
- canvas.restore();
-
- canvas.drawColor(Color.YELLOW);
- }
- }
效果图如下:
从效果图中可以看出在canvas.restore()后,canvas被恢复到初始化的全屏大小。
然后我们再看一个旋转的例子
- public class CLIP_SAVE_FLAG_View extends View {
- private Paint mPaint;
- public CLIP_SAVE_FLAG_View(Context context, AttributeSet attrs) {
- super(context, attrs);
- setLayerType(LAYER_TYPE_SOFTWARE,null);
- mPaint = new Paint();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- mPaint.setColor(Color.GREEN);
- canvas.drawRect(100,0,200,100,mPaint);
-
- canvas.save(Canvas.CLIP_SAVE_FLAG);
- canvas.rotate(40);
- canvas.restore();
-
- mPaint.setColor(Color.YELLOW);
- canvas.drawRect(100,0,200,100,mPaint);
- }
- }
效果图如下:
我们先画了一个绿色的矩形,之后旋转画布,然后在调用 canvas.restore()恢复画布之后,再画上一个同样的黄色矩形。
从效果图中可以看出,canvas在恢复时,并没有恢复旋转的画布,这也就是Canvas.CLIP_SAVE_FLAG的意义所在,只保存裁剪信息,不保存位置信息,所以恢复时,位置信息是不会被恢复的!
(2)、saveLayer(int flag)与CLIP_SAVE_FLAG
在添加上Canvas.HAS_ALPHA_LAYER_SAVE_FLAG标识以后,效果与canvas.save相同,这里就简单讲解一下。
先看裁剪的例子:
- public class CLIP_SAVE_FLAG_View extends View {
- private Paint mPaint;
- public CLIP_SAVE_FLAG_View(Context context, AttributeSet attrs) {
- super(context, attrs);
- setLayerType(LAYER_TYPE_SOFTWARE,null);
- mPaint = new Paint();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- canvas.saveLayer(0,0,getWidth(),getHeight(),mPaint,Canvas.CLIP_SAVE_FLAG|Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
- canvas.clipRect(100,0,200,100);
- canvas.restore();
-
- canvas.drawColor(Color.YELLOW);
- }
- }
效果图如下:
效果与canvas.save一样,原因也很简单,因为Canvas.CLIP_SAVE_FLAG标识是可以恢复裁剪信息的。
然后再来看看旋转。
- public class CLIP_SAVE_FLAG_View extends View {
- private Paint mPaint;
- public CLIP_SAVE_FLAG_View(Context context, AttributeSet attrs) {
- super(context, attrs);
- setLayerType(LAYER_TYPE_SOFTWARE,null);
- mPaint = new Paint();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- mPaint.setColor(Color.GREEN);
- canvas.drawRect(100,0,200,100,mPaint);
- canvas.saveLayer(0,0,getWidth(),getHeight(),mPaint,Canvas.CLIP_SAVE_FLAG|Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
- canvas.rotate(40);
- canvas.restore();
-
- mPaint.setColor(Color.YELLOW);
- canvas.drawRect(100,0,200,100,mPaint);
-
- }
- }
效果与canvas.save相同
因为Canvas.CLIP_SAVE_FLAG不能保存位置信息,所以在canvas.restore()后,旋转并没有被恢复。
所以CLIP_SAVE_FLAG标识的结论来了:
1、当save/saveLayer调用 Canvas.CLIP_SAVE_FLAG时只会保存画布的裁剪信息,在canvas.restore()时也只会恢复裁剪信息,而改变过的画布位置信息是不会被恢复的。
2、当使用canvas.saveLayer(Canvas.CLIP_SAVE_FLAG)时,需要与Canvas.HAS_ALPHA_LAYER_SAVE_FLAG一起使用,不然新建画布所在区域原来的图像将被清空。
4、FLAG之HAS_ALPHA_LAYER_SAVE_FLAG和FULL_COLOR_LAYER_SAVE_FLAG
这两个标识都是saveLayer()专用的
HAS_ALPHA_LAYER_SAVE_FLAG表示新建的bitmap画布在与上一个画布合成时,不会将上一层画布内容清空,直接盖在上一个画布内容上面。
FULL_COLOR_LAYER_SAVE_FLAG则表示新建的bimap画布在与上一个画布合成时,先将上一层画布对应区域清空,然后再盖在上面。
下面我们分别举例子来看
注意一定要在view中禁用掉硬件加速,因为在api 21之后,才支持saveLayer
(1)、FULL_COLOR_LAYER_SAVE_FLAG
- public class ALPHA_COLOR_FALG_VIEW extends View {
- private Paint mPaint;
- public ALPHA_COLOR_FALG_VIEW(Context context, AttributeSet attrs) {
- super(context, attrs);
- setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- mPaint = new Paint();
- mPaint.setColor(Color.GREEN);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- canvas.drawColor(Color.RED);
-
- canvas.saveLayer(0,0,500,500,mPaint,Canvas.FULL_COLOR_LAYER_SAVE_FLAG);
- canvas.drawRect(100,100,300,300,mPaint);
- canvas.restore();
- }
- }
效果图如下:
我们在saveLayer时,新建bitmap画布的大小为(0,0,500,500),然后在新建画布中画了一个矩形(100,100,300,300),由于我们使用的标识是Canvas.FULL_COLOR_LAYER_SAVE_FLAG,所以新建画布在与上一层画布合成时,会先把上一层画布对应区域的图像清空掉,然后再盖上新建画布。由于新建画布中除了绿色矩形,其它位置都是透明像素,所以就显示出Activity的底色(黑色)。如果你把activity的背景色在xml中设置为白色,做出来的效果图中,露出来的就是白色了:
main.xml
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:background="#ffffff"
- >
- …………
对应的效果为:
(2)、HAS_ALPHA_LAYER_SAVE_FLAG
我把简单把上面的示例代码改一下,把Canvas.FULL_COLOR_LAYER_SAVE_FLAG改成Canvas.HAS_ALPHA_LAYER_SAVE_FLAG:
- public class ALPHA_COLOR_FALG_VIEW extends View {
- private Paint mPaint;
- public ALPHA_COLOR_FALG_VIEW(Context context, AttributeSet attrs) {
- super(context, attrs);
- setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- mPaint = new Paint();
- mPaint.setColor(Color.GREEN);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- canvas.drawColor(Color.RED);
-
- canvas.saveLayer(0,0,500,500,mPaint,Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
- canvas.drawRect(100,100,300,300,mPaint);
- canvas.restore();
- }
- }
效果图为:
从效果图中可以看出,saveLayer新建的画布在与上一层画布合成时,并没有把上一层画布对应区域清空,而是直接盖在上面。
(3)、共用时,以HAS_ALPHA_LAYER_SAVE_FLAG为主
很明显这两个标识是相互冲突的,因为Canvas.HAS_ALPHA_LAYER_SAVE_FLAG表示直接盖上去而不清空上一画布的图像,而Canvas.FULL_COLOR_LAYER_SAVE_FLAG则表示先将上一画布对应区域图像清空,然后再盖上去。当他们共用时,以哪个标识位为主呢?
我们来做个试验:
- public class ALPHA_COLOR_FALG_VIEW extends View {
- private Paint mPaint;
- public ALPHA_COLOR_FALG_VIEW(Context context, AttributeSet attrs) {
- super(context, attrs);
- setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- mPaint = new Paint();
- mPaint.setColor(Color.GREEN);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- canvas.drawColor(Color.RED);
- canvas.saveLayer(0,0,500,500,mPaint,Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.FULL_COLOR_LAYER_SAVE_FLAG);
- canvas.drawRect(100,100,300,300,mPaint);
- canvas.restore();
- }
- }
还是上面的例子,我们同时加入两个标识,看以哪个结果显示,效果图如下:
所以从效果图中也可以看出,当这两个标识同时使用时,以Canvas.HAS_ALPHA_LAYER_SAVE_FLAG为主;
(4)、当saveLayer只指定MATRIX_SAVE_FLAG/CLIP_SAVE_FLAG的合成方式
前面我们在讲解saveLayer的MATRIX_SAVE_FLAG、CLIP_SAVE_FLAG标识时,都强制加上了Canvas.HAS_ALPHA_LAYER_SAVE_FLAG标识,意思是让其在合成时不清空上一画布图像。那么问题来了,当我们只指定MATRIX_SAVE_FLAG、CLIP_SAVE_FLAG标识时,Android默认的合成方式哪一个呢?
下面我们举个例子来看下:
- public class ALPHA_COLOR_FALG_VIEW extends View {
- private Paint mPaint;
-
- public ALPHA_COLOR_FALG_VIEW(Context context, AttributeSet attrs) {
- super(context, attrs);
- setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- mPaint = new Paint();
- mPaint.setColor(Color.GREEN);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- canvas.drawColor(Color.RED);
-
- canvas.saveLayer(0,0,500,500,mPaint,Canvas.MATRIX_SAVE_FLAG);
- canvas.rotate(40);
- canvas.drawRect(100, 100, 300, 300, mPaint);
- canvas.restore();
- }
- }
效果图如下:
从效果图中可以看出,在默认情况下使用的是Canvas.FULL_COLOR_LAYER_SAVE_FLAG标识,即先清空上一层画布对应区域的图像,然后再合成,所以这也是我们在上面的例子中强制添加HAS_ALPHA_LAYER_SAVE_FLAG标识的原因
所以有关这两个标识的结论来了:
1、HAS_ALPHA_LAYER_SAVE_FLAG表示新建的bitmap画布在与上一个画布合成时,不会将上一层画布内容清空,直接盖在上一个画布内容上面。
2、FULL_COLOR_LAYER_SAVE_FLAG则表示新建的bimap画布在与上一个画布合成时,先将上一层画布对应区域清空,然后再盖在上面。
3、当HAS_ALPHA_LAYER_SAVE_FLAG与FULL_COLOR_LAYER_SAVE_FLAG两个标识同时指定时,以HAS_ALPHA_LAYER_SAVE_FLAG为主
4、当即没有指定HAS_ALPHA_LAYER_SAVE_FLAG也没有指定FULL_COLOR_LAYER_SAVE_FLAG时,系统默认使用FULL_COLOR_LAYER_SAVE_FLAG;
5、FLAG之CLIP_TO_LAYER_SAVE_FLAG
(1)、概述
这个标识比较犯贱,它的意义是,在新建bitmap前,先把canvas给裁剪,前面我们讲过canvas代表的是画板的意思,一旦画板被裁剪,那么其中的各个画布都会被受到影响。而且由于它是在新建bitmap前做的裁剪,所以是无法恢复的!
- public class CLIP_TO_LAYER_SAVE_FLAG_VIEW extends View {
- private Paint mPaint;
- public CLIP_TO_LAYER_SAVE_FLAG_VIEW(Context context, AttributeSet attrs) {
- super(context, attrs);
- setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- mPaint = new Paint();
- mPaint.setColor(Color.GREEN);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- canvas.drawColor(Color.RED);
- canvas.saveLayer(0, 0, 500, 500, mPaint, Canvas.CLIP_TO_LAYER_SAVE_FLAG);
- canvas.restore();
-
- canvas.drawColor(Color.YELLOW);
- }
- }
效果图如下:
从效果图中可以看出,当我们调用canvas.saveLayer(0, 0, 500, 500, mPaint, Canvas.CLIP_TO_LAYER_SAVE_FLAG)时,canvas画板就被裁剪了,不仅影响了自己,而且还把view的原始画布给影响了,虽然在调用了canvas.restore(),但最后一句在将原始画布填充为黄色,也可以看出,原始画布没有被恢复!
(2)、与CLIP_SAVE_FLAG共用时,Canvas将被恢复
我们知道,前面有一个保存裁剪信息的标识:CLIP_SAVE_FLAG,假如我们让它裁剪时,先保存裁剪区域,是不是可以恢复过来呢?
- public class CLIP_TO_LAYER_SAVE_FLAG_VIEW extends View {
- private Paint mPaint;
- public CLIP_TO_LAYER_SAVE_FLAG_VIEW(Context context, AttributeSet attrs) {
- super(context, attrs);
- setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- mPaint = new Paint();
- mPaint.setColor(Color.GREEN);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- canvas.drawColor(Color.RED);
- canvas.saveLayer(0, 0, 500, 500, mPaint, Canvas.CLIP_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
- canvas.restore();
-
- canvas.drawColor(Color.YELLOW);
- }
- }
效果图如下:
从效果图中可以看出canvas被恢复了,不过canvas被恢复也,也就失去了Canvas.CLIP_TO_LAYER_SAVE_FLAG标识的意义了。
所以这个CLIP_TO_LAYER_SAVE_FLAG标识的结论来了:
1、CLIP_TO_LAYER_SAVE_FLAG意义是在新建bitmap前,先把canvas给裁剪,一旦画板被裁剪,那么其中的各个画布都会被受到影响。而且由于它是在新建bitmap前做的裁剪,所以是无法恢复的;
2、当CLIP_TO_LAYER_SAVE_FLAG与CLIP_SAVE_FLAG标识共用时,在调用restore()后,画布将被恢复
6、FLAG之ALL_SAVE_FLAG
这个标识是我们最常用的,它是所有标识的公共集合。
对于save(int flag)来讲,ALL_SAVE_FLAG = MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG;即保存位置信息和裁剪信息
对于save(int flag)来讲,ALL_SAVE_FLAG = MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG很容易理解,因为save(int flag)函数只能使用MATRIX_SAVE_FLAG 、CLIP_SAVE_FLAG这两个标识。
对于saveLayer(int flag)来讲,ALL_SAVE_FLAG = MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG|HAS_ALPHA_LAYER_SAVE_FLAG;即保存保存位置信息和裁剪信息,新建画布在与上一层画布合成时,不清空原画布内容。
原本来讲saveLayer的ALL_SAVE_FLAG标识应当是它所能使用的所有标识的集合,即应当是ALL_SAVE_FLAG = MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG|HAS_ALPHA_LAYER_SAVE_FLAG|FULL_COLOR_LAYER_SAVE_FLAG|CLIP_TO_LAYER_SAVE_FLAG,但由于HAS_ALPHA_LAYER_SAVE_FLAG与FULL_COLOR_LAYER_SAVE_FLAG共用时以HAS_ALPHA_LAYER_SAVE_FLAG为主,CLIP_TO_LAYER_SAVE_FLAG与CLIP_SAVE_FLAG共用时,CLIP_TO_LAYER_SAVE_FLAG将无效,所以最终ALL_SAVE_FLAG = MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG|HAS_ALPHA_LAYER_SAVE_FLAG;
在理解了上面各个TAG的样式以后,这个TAG的难度几乎没有,这里就不再举例了。
四、restore()与restoreToCount()
1、restore()
restore()的作用就是把回退栈中的最上层画布状态出栈,恢复画布状态。在
《自定义控件之绘图篇(四):canvas变换与操作》
中已经详细地说明了restore()函数的用法。这里就不再细讲了
2、restoreToCount(int count)
先看下这几个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 saveFlags)
在save()、saveLayer()、saveLayerAlpha()保存画布后,都会返回一个ID值,这个ID值表示当前保存的画布信息的栈层索引(从0开始),比如保存在第三层,则返回2;
而restoreToCount的声明如下:
- public void restoreToCount(int saveCount);
它表示一直退栈,一直退到指定count的层数为栈顶为止;注意这个saveCount起始值是从1开始的,也就是说它比对应栈的索引要多1;
比如,我们开始的栈已经有两层,然后我们调用如下代码:
- int id = canvas.saveLayer(0,0,getWidth(),getHeight(),mPaint,Canvas.ALL_SAVE_FLAG);
- canvas.restoreToCount(id);
调用canvas.saveLayer后,新保存的画布放在了第三层,返回的id的值是对应的索引即2
而canvas.restoreToCount(id);则表示一直退栈,把栈一直退到第二层在栈顶的位置,刚好把新建的第三层给退出掉。
所以利用这个特性,我们可以调用save函数的时候,把对应的id保存住,然后canvas.restoreToCount(id)就可以把栈的状态回退到生成这个id前的状态。
下面我们举个例子来看下:
- public class RestoreToCountView extends View {
- private Paint mPaint;
- private String TAG = "qijian";
- public RestoreToCountView(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- mPaint = new Paint();
- mPaint.setColor(Color.RED);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- int id1 = canvas.save();
- canvas.clipRect(0,0,800,800);
- canvas.drawColor(Color.RED);
- Log.d(TAG,"count:"+canvas.getSaveCount()+" id1:"+id1);
-
- int id2 = canvas.saveLayer(0,0,getWidth(),getHeight(),mPaint,Canvas.ALL_SAVE_FLAG);
- canvas.clipRect(100,100,700,700);
- canvas.drawColor(Color.GREEN);
- Log.d(TAG,"count:"+canvas.getSaveCount()+" id2:"+id2);
-
- int id3 = canvas.saveLayerAlpha(0,0,getWidth(),getHeight(),0xf0,Canvas.ALL_SAVE_FLAG);
- canvas.clipRect(200,200,600,600);
- canvas.drawColor(Color.YELLOW);
- Log.d(TAG,"count:"+canvas.getSaveCount()+" id3:"+id3);
-
- int id4 = canvas.save(Canvas.ALL_SAVE_FLAG);
- canvas.clipRect(300,300,500,500);
- canvas.drawColor(Color.BLUE);
- Log.d(TAG,"count:"+canvas.getSaveCount()+" id4:"+id4);
- }
- }
在onDraw函数中,我们连续对canvas做裁剪,并且在裁剪后,把当前画布画上一层不同的颜色,然后把当前的栈的层数和最高层的索引打出来
效果图如下:
Log日志如下:
然后我们更改一下上面的代码:
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- int id1 = canvas.save();
- canvas.clipRect(0,0,800,800);
- canvas.drawColor(Color.RED);
- Log.d(TAG,"count:"+canvas.getSaveCount()+" id1:"+id1);
-
- int id2 = canvas.saveLayer(0,0,getWidth(),getHeight(),mPaint,Canvas.ALL_SAVE_FLAG);
- canvas.clipRect(100,100,700,700);
- canvas.drawColor(Color.GREEN);
- Log.d(TAG,"count:"+canvas.getSaveCount()+" id2:"+id2);
-
- int id3 = canvas.saveLayerAlpha(0,0,getWidth(),getHeight(),0xf0,Canvas.ALL_SAVE_FLAG);
- canvas.clipRect(200,200,600,600);
- canvas.drawColor(Color.YELLOW);
- Log.d(TAG,"count:"+canvas.getSaveCount()+" id3:"+id3);
-
- int id4 = canvas.save(Canvas.ALL_SAVE_FLAG);
- canvas.clipRect(300,300,500,500);
- canvas.drawColor(Color.BLUE);
- Log.d(TAG,"count:"+canvas.getSaveCount()+" id4:"+id4);
-
- canvas.restoreToCount(id3);
- canvas.drawColor(Color.GRAY);
- Log.d(TAG,"count:"+canvas.getSaveCount());
- }
我们在最后添加上canvas.restoreToCount(id3);,然后把画布整个绘成灰色。
效果图如下:
Log日志如下:
从代码中可以看出调用canvas.restoreToCount(id3)后,将恢复到生成id3之前的画布状态,id3之前的画布状态就是(100,100,700,700)
3、restore()与restoreToCount(int count)关系
它们两个针对的都是同一个栈,所以是完全可以通用的,不同的是restore()是默认将栈顶内容退出还原画布,而restoreToCount(int count)则是一直退栈,直到指定层count做为栈顶,将此之前的所有动作都恢复。
大家可能还有个疑问,前面我们讲了各种FLAG,在应用不同FLAG时,都是保存在同一个栈中吗,我们下面试一下
- public class RestoreToCountView extends View {
- private Paint mPaint;
- private String TAG = "qijian";
- public RestoreToCountView(Context context, AttributeSet attrs) {
- super(context, attrs);
- setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- mPaint = new Paint();
- mPaint.setColor(Color.RED);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- canvas.save();
- Log.d(TAG,"count:"+canvas.getSaveCount());
- canvas.save(Canvas.ALL_SAVE_FLAG);
- Log.d(TAG,"count:"+canvas.getSaveCount());
- canvas.saveLayer(0,0,getWidth(),getHeight(),mPaint,Canvas.CLIP_SAVE_FLAG);
- Log.d(TAG,"count:"+canvas.getSaveCount());
- canvas.saveLayer(0,0,getWidth(),getHeight(),mPaint,Canvas.MATRIX_SAVE_FLAG);
- Log.d(TAG,"count:"+canvas.getSaveCount());
- canvas.saveLayer(0,0,getWidth(),getHeight(),mPaint,Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
- Log.d(TAG,"count:"+canvas.getSaveCount());
- canvas.saveLayer(0,0,getWidth(),getHeight(),mPaint,Canvas.ALL_SAVE_FLAG);
- Log.d(TAG,"count:"+canvas.getSaveCount());
- }
- }
在这个例子中,我们多次调用不同的save函数和不同的FLAG,然后将栈中层数打出来,日志如下:
从效果图中可以明显看出,每save一次,栈的层数都在加一,所以无论哪种save方法,哪个FLAG标识,保存画布时都使用的是同一个栈
所以restore()与restoreToCount(int count)的结论来了:
1、restore的意义是把回退栈中的最上层画布状态出栈,恢复画布状态。restoreToCount(int count)的意义是一直退栈,直到指定层count做为栈顶,将此之前的所有动作都恢复。
2、所以无论哪种save方法,哪个FLAG标识,保存画布时都使用的是同一个栈
3、restore()与restoreToCount(int count)针对的都是同一个栈,所以是完全可以通用和混用的。