自定义View之路——Canvas(save,restore,translate,rotate)

来源:互联网 发布:天狼星期货软件 编辑:程序博客网 时间:2024/04/30 13:02

这章说Canvas,Paint是画笔,有画笔当然也得有画布才能往上作画了,Canvas就是画布,这很好理解。
Canvas给我们提供了很多实用的API,可以画各种图形,文字,图片等,接下来我会一一介绍。

先让我们看一下Canvas的构造(官方文档):

Canvas()  //Construct an empty raster canvas. 
Canvas(Bitmap bitmap)  //Construct a canvas with the specified bitmap to draw into. 

这里有两个构造,第一个先不说。当创建一个Canvas对象时,为什么要传进去一个bitmap对象呢?如果不传入一个bitmap对象,IDE编译虽然不会报错,但是一般我们不会这样做。这是因为传进去的bitmap与通过这个bitmap创建的Canvas画布是紧紧联系在一起的,这个过程我们称之为装载画布。这个bitmap用来存储所有绘制在Canvas上的像素信息。 所以当你通过这种方式创建了Canvas对象后,后面调用所有的Canvas.drawXXX方法都发生在这个bitmap上。如果在View类的onDraw()方法中,通过下面这段代码,我们可以了解到canvas与bitmap的直接关系。首先在onDraw方法中绘制两个bitmap,代码如下所示。

canvas.drawBitmap(bitmap1,0,0,null);canvas.drawBitmap(bitmap2,0,0,null);

而对于bitmap2,我们将它装载到另一个Canvas对象中,代码如下所示。

Canvas mCanvas=new Canvas(bitmap2);

在其他地方使用Canvas对象的绘图方法在装载bitmap2的Canvas对象上进行绘图,代码如下所示。

mCanvas.drawXXX

通过mCanvas将绘制效果作用在了bitmap2上,再刷新View的时候,就会发现通过onDraw方法画出来的bitmap2已经发生了改变,这就是因为bitmap2承载了在mCanvas上所进行的绘图操作。虽然我们也使用了Canvas的绘制API,但其实并没有将图形直接绘制在onDraw()方法指定的那块画布上,而是通过改变bitmap,然后让view重绘,从而显示改变之后的bitmap,这个理解非常重要,这对后续进行深入地学习和提升绘图技巧非常有帮助。(虽然我们可以自己创建Canvas,但是官方还是推荐我们使用onDraw(Canvas canvas)里的canvas进行绘画)

Canvas基本已经介绍了,下面我们来看几个方法。

  • canvas.save();
  • canvas.restore();
  • canvas.translate();
  • canvas.rotate();

首先,来看canvas.save()和canvas.restore()这两个方法。

在讲解这两个方法之前,首先来了解一下Android绘图的坐标体系。在Android中,默认的坐标零点位于屏幕左上角,向下为Y轴正方向,向右为X轴正方向。

canvas.save();这个方法,从字面上可以理解为保存画布。它的作用就是将之前的所有已绘制图像保存起来,让后续的操作就好像在一个新的图层上操作一样,这一点与photoshop中的图层理解基本一致。

而canvas.restore()这个方法,则可以理解为photoshop中的合并图层操作。它的作用是我们在save()之后绘制的所有图像与save()之前的图像进行合并。

   Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG);        paint.setColor(Color.GREEN);        paint.setStyle(Paint.Style.STROKE);        paint.setStrokeWidth(20);        int px=getMeasuredWidth();        int py=getMeasuredHeight();        canvas.drawRect(0, 0, px, py, paint);        canvas.save(); //这时候保存的是画布没旋转之前的状态        canvas.rotate(90, px / 2, py / 2); //画布开始旋转        canvas.drawLine(px / 2, 0, 0, py / 2, paint); //收到旋转操作的影响        canvas.drawLine(px / 2, 0, px, py / 2, paint);//收到旋转操作的影响        canvas.drawLine(px/2,0,px/2,py,paint);//收到旋转操作的影响        canvas.restore();   //还原状态(还原上一个save的状态)        canvas.drawCircle(getWidth()-20,getHeight()-20,20,paint); //还原到画布没旋转之前,所以不受影响

比如上面这段代码,在save之后面有一个对画布坐标的旋转操作,正常来说后面的所有画图操作的原坐标都是基于控件的右上角(原来是左上角,顺时针旋转了90度就变成了右上角)。但是这个旋转操作只对后面的三次画线的操作有了影响,并没有影响到drawCircle的操作,这是为什么呢?原因就在于在旋转之前我们把画布的状态进行了save(); 在drawLine()之后我们又把save();的状态给还原回去了。(save的状态是画布没旋转之前),所以我们drawCircle的时候自然是画布没有旋转的状态了。

那么translate()方法与rotate()方法呢?虽然从字面上看,可以将它们理解为是画布平移,画布翻转,但是把它理解为坐标系的平移与翻转则会更加形象。前面说了,默认绘图坐标零点位于屏幕左上角,那么在调用translate(x,y)方法之后,则将原点(0,0)移动到了(x,y)。之后的所有绘图操作都将以(x,y)为原点执行。同理,rotate()方法也是一样,它将坐标系旋转了一定的角度。大家也许会想,这样的操作有什么用呢?的确,没有这两个方法,同样可以绘图,只要算好坐标,没有什么画不出来的。所以说,这些方法是Android用来帮助我们简化绘图而创建的。

0 0
原创粉丝点击