Android Canvas Layer 图层
Canvas
虽然一直被称之为 画布,当我们千万不要认为它就是现实生活中的一张画布,而是应该理解为 Photoshop
中的图层集合
更有趣的是 Android
中的 Canvas
具有记忆功能,会记住上次绘画时的坐标
Canvas 绘图机制
我们先来看一张图
drawCircle(50, 50, 50, mPaint)
参考坐标一直是 (50,50)
那为何会出现这样的效果?
那是因为,画布坐标原点每次分别在x,y轴上移动 100
那么假如我们要重新回到 (0,0)
点处绘制新的图形呢?translate(-100,-100)
的慢慢地平移回去?
肯定不是,我们可以在做平移变换之前将当前 canvas
的状态进行保存
Canvas
为我们提供了 图层(Layer) 的支持,Layer(图层)是按 "栈结构" 来进行管理的
当我们调用 save() 方法,会保存当前 Canvas
的状态然后作为一个 Layer(图层)
,添加到 Canvas栈
中,
而当我们调用 restore() 方法的时候,会恢复之前 Canvas
的状态,而此时 Canvas
的图层栈
会弹出栈顶的那个 Layer
,后继的 Layer
来到栈顶,此时的 Canvas
回复到此栈顶时保存的 Canvas
状态
简单说就是 save()往栈压入一个 Layer,restore()弹出栈顶的一个Layer,这个Layer代表Canvas的 状态! 也就是说可以 save() 多次,也可以 restore() 多次,但是 restore() 的调用次数 不能大于** save()否则会引发错误
范例
我们写一个范例来验证下 save() 和 restore() 的作用
-
创建一个 空的 Android 项目
cn.twle.android.CanvasLayer
-
自定义一个 View 类
MsView.java
package cn.twle.android.canvaslayer; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.support.v7.widget.AppCompatImageView; public class MsView extends AppCompatImageView { public MsView(Context context) { super(context, null); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint mPaint = new Paint(); mPaint.setFlags(Paint.ANTI_ALIAS_FLAG); mPaint.setAntiAlias(true);// 抗锯尺 mPaint.setColor(0xffff0000); mPaint.setStyle(Paint.Style.FILL_AND_STROKE); canvas.save(); //保存当前 canvas 的状态 canvas.translate(100, 100); canvas.drawRect(200,200,200+300,200+200,mPaint); canvas.restore(); //恢复保存的Canvas的状态 mPaint.setColor(0xff00ff00); canvas.drawRect(100,100,100+300,100+200,mPaint); invalidate(); } }
-
修改
MainActivity.java
设置setContentView(new MsView(MainActivity.this))
package cn.twle.android.canvaslayer; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new MsView(MainActivity.this)); } }
运行结果
代码和结果已经说明了一切,接着我们搞得复杂点,来一发多个 save()
和 restore()
修改 MsView
的 onDraw()
方法
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.save(); Paint mPaint = new Paint(); mPaint.setColor(0xffff0000); mPaint.setTextSize(48); canvas.translate(300, 300); canvas.drawText("简单教程,简单编程",0,0,mPaint); canvas.save(); canvas.rotate(45); canvas.drawText("简单教程,简单编程",0,0,mPaint); canvas.save(); canvas.rotate(45); canvas.drawText("简单教程,简单编程",0,0,mPaint); canvas.save(); canvas.translate(0, 200); canvas.drawText("简单教程,简单编程",0,0,mPaint); invalidate(); }
运行结果
首先平移 (300,300)
画文字,然后旋转 45 度画文字,再接着旋转 45 度画文字,接着平移 (0,200)
期间每次画图前都 save()
一下,看到这里可能有个疑问,最后这个平移不是 y 移动 200
么,怎么变成向左了 ?
这是因为,rotate()
是旋转的是整个坐标轴么
坐标轴的变化如下图
rotate()
应该弄懂了,现在我们看看 restore()
我们在最后绘图的前面加两个 restore()
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.save(); Paint mPaint = new Paint(); mPaint.setColor(0xffff0000); mPaint.setTextSize(48); canvas.translate(300, 300); canvas.drawText("简单教程,简单编程",0,0,mPaint); canvas.save(); canvas.rotate(45); canvas.drawText("简单教程,简单编程",0,0,mPaint); canvas.save(); canvas.rotate(45); canvas.drawText("简单教程,简单编程",0,0,mPaint); canvas.save(); canvas.restore(); canvas.restore(); canvas.translate(0, 200); canvas.drawText("简单教程,简单编程",0,0,mPaint); invalidate(); }
运行结果
不说什么,自己体会,再加多个 restore()
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.save(); Paint mPaint = new Paint(); mPaint.setColor(0xffff0000); mPaint.setTextSize(48); canvas.translate(300, 300); canvas.drawText("简单教程,简单编程",0,0,mPaint); canvas.save(); canvas.rotate(45); canvas.drawText("简单教程,简单编程",0,0,mPaint); canvas.save(); canvas.rotate(45); canvas.drawText("简单教程,简单编程",0,0,mPaint); canvas.save(); canvas.restore(); canvas.restore(); canvas.restore(); canvas.translate(0, 200); canvas.drawText("简单教程,简单编程",0,0,mPaint); invalidate(); }
有点意思,继续加 restore()
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.save(); Paint mPaint = new Paint(); mPaint.setColor(0xffff0000); mPaint.setTextSize(48); canvas.translate(300, 300); canvas.drawText("简单教程,简单编程",0,0,mPaint); canvas.save(); canvas.rotate(45); canvas.drawText("简单教程,简单编程",0,0,mPaint); canvas.save(); canvas.rotate(45); canvas.drawText("简单教程,简单编程",0,0,mPaint); canvas.save(); canvas.restore(); canvas.restore(); canvas.restore(); canvas.restore(); canvas.translate(0, 200); canvas.drawText("简单教程,简单编程",0,0,mPaint); invalidate(); }
好像不可以再写 restore() 了是吧,因为我们只 save()
了四次
按照网上的说法,这会报错的,真的是这样吗?
我们可以调用 Canvas
给我们提供的一个获得当前栈中有多少个 Layer
的方法 getSaveCount() ;然后在 save()
和 restore()
的前后都加一个 Log 将栈中 Layer 的层数打印出来
结果真是喜闻乐见,毕竟实践出真知