在前面我们说了Android的基本绘图技巧,下面我们聊聊Android中常用的一些绘图技巧。通过这些技巧学习来简化、优化Android的绘图操作。
一、Canvas
Canvas作为绘制图形的直接对象,提供了以下几个非常有用的方法。
● Canvas.save()
● Canvas.restore()
● Canvas.translate()
● Canvas.rotate()
首先,来看Canvas.save()和Canvas.restore()这两个方法。
在讲解这两个方法之前,首先来了解一下Android的绘图坐标体系,在Android中,默认的坐标零点位于屏幕左上角,向下为Y轴正方形,向右为X轴的正方向。Android中的绘图,一般情况下困难在于坐标的计算上。因此绘图者不应该把自己看作是一个完成任务的人,而是应该把自己当成一个设计者,你是在设计这些图形而不是在机械地绘制。
Canvas.save()这个方法,从字面上可以理解为保存画布。它的作用就是将之前的所有已绘制图像保存起来,让后续的操作就好像在一个新的图层上操作一样,这一点与Photoshop中的图层理解基本一致。
而Canvas.restore()这个方法,则可以理解为Photoshop中的合并图层操作。它的作用是将我们在save()之后绘制的所有图像与save()之前的图像进行合并。
那么translate()方法与rotate()方法呢?虽热从字面上看,可以将它们理解为画布平移、画布翻转,但是把它理解为坐标系的平移和翻则会更加形象。前面说了,默认绘图坐标零点位于屏幕左上角,那么在调用translate(x,y)方法之后,则将原点(0,0)移动到了(x,y)。之后所有的绘图操作都将以(x,y)为原点执行。同理,rotate()方法也是一样,它将坐标系旋转一定的角度。大家也想会想,这样的操作有什么用呢?的确,没有这两个方法,同样可以绘图,只要计算好坐标,没有什么画画不出来的。所以说,这些方法是Android用来帮助我们简化绘图而创建的。这么说可能比较抽象,来看一个具体的实例,大家就能明白其中的奥妙了。例如,需要创建一和如图(1)所示的仪表盘。
(1)仪表盘
先来分析一下要画的这个图形。要完成这样一个图形,可以将它分解成以下几个元素。
1、仪表盘——外面的大圆盘
2、刻度线——包含四个长的刻度线和其他短的刻度线
3、刻度值——包含长刻度线对应的大的刻度值和其他小的刻度值
4、指针——中间的指针,一粗一细两根指针
相信如果在现实中叫你去画这样一个仪表盘,你应该也会这样去画。实际上,Android中的绘图与实现中的绘图十分相似,与Photoshop中的绘图则更是相似。所以,当你要绘制一个复杂的图形的时候,不妨想想自己在显示中该如何去做,顺着这个思路也许就能找到解决问题的方法了。
在这个实例中,第一步画圆盘,已经可以轻松的实现了,也就是调用xanvas.drawCircle(方法绘制一个圆就可以了。关键在于确定圆心和半径。这里为了简单演示,我们把圆心定在屏幕中心而半径就为屏幕的一半,代码如下所示。
Paint painCircle = new Paint();
painCircle.setColor(Color.RED);
painCircle.setStyle(Paint.Style.STROKE);
painCircle.setAntiAlias(true);
painCircle.setStrokeWidth(5);
canvas.drawCircle(mWidth/2,mHeight/2,mWidth/2,painCircle);
下面画刻度线,这个也很简单,一条线而已,只需要确定线段两个端点的坐标就可以了。第一根线还还比较简单,坐标非常容易确定。可后面的问题就来了,那些斜着的线的端点坐标无法通过简单的坐标加减来获得,必须通过角度来计算三角偶数用个遍才能算出这些点的坐标。不仅运输量大,而且非常容易出错,这当然也是非常满意接受的。所以我们来看看Android是如何简化这些运算的。
从图一中可以看出,我们之所以感觉不好画,就是因为那恶心的角度。那么如果将画布已圆心为原点为原点旋转到需要的角度呢?当灭画好一根线,就旋转相应的角度。虽然下一次画线的时候,依然是第一根线的坐标,但是实际上当我们把画布重新还原当选择前的位置时,所有的刻度就已经全部画好了。通过旋转画布——实际上是旋转了画图的坐标系,这就避免了进行复杂的三角偶数运算。通过这的相对论的变换,间接简化了绘图,这样在去绘制这些刻度线,就只需要区别整点与非整点刻度线就可以了,代码如下所示。
Paint paintDegree = new Paint();
paintDegree.setColor(Color.RED);
painCircle.setStrokeWidth(3);
for (int i =0;i<24;i++){
if (i==0||i==6||i==12||i==18){
paintDegree.setStrokeWidth(5);
paintDegree.setTextSize(30);
canvas.drawLine(mWidth/2,mHeight/2-mWidth/2,mWidth/2,mHeight/2-mWidth/2+60,paintDegree);
String degree = String.valueOf(i);
canvas.drawText(degree,mWidth/2-paintDegree.measureText(degree)/2,mHeight/2-mWidth/2+90,paintDegree);
}else {
paintDegree.setStrokeWidth(3);
paintDegree.setTextSize(15);
canvas.drawLine(mWidth/2,mHeight/2-mWidth/2,mWidth/2,mHeight/2-mWidth/2+30,paintDegree);
String degree = String.valueOf(i);
canvas.drawText(degree,mWidth/2-paintDegree.measureText(degree)/2,mHeight/2-mWidth/2+60,paintDegree);
}
canvas.rotate(15,mWidth/2,mHeight/2);
}
最后来画那两根指针。同样只是简单地画两个线段而已,只要算好起始点的坐标就可以了。起点自然是圆心,终点就是在圆心的坐标基础上的加减。但前面介绍了rotate()方法,那么自然也不能亏待了translate()方法。所以,我们通过画这两根指针来看看如何使用translat()方法。在前文中,我们讲了translate()方法用来平移坐标原点,将其从(0,0)的位置上移动到新的坐标。这个新的坐标自然是能够帮助我们简化绘图的坐标。那么通过translate()方法将原点移动到原点,这样再画线段的时候就可以理解为从原点开始画一条线段了。使用translate()方法,代码如下所示。
Paint paintHour = new Paint();
paintHour.setColor(Color.RED);
paintHour.setStrokeWidth(20);
Paint paintMinute = new Paint();
paintMinute.setColor(Color.RED);
paintMinute.setStrokeWidth(10);
canvas.save();
canvas.translate(mWidth/2,mHeight/2);
canvas.drawLine(0,0,100,100,paintHour);
canvas.drawLine(0,0,100,200,paintMinute);
canvas.restore();
最后绘图的效果就和图(1)所示的一样了。
二、Layer图层
Andriud中的绘图API,很大程度上都来自与现实生活中的绘图,特别是借鉴了很多Photoshop中的概念,比如图层概念。那么什么是图层呢?相信用过Photoshop的朋友一定会非常清楚,一张复杂的图可以很多个图层叠加起来,形成一个复杂的图像。在Android中,使用saveLayer()方法来创建一个图层,图层同样是基于栈的结构进行管理的,如图(2)所示。
(2)图层的栈管理结构
Android通过调用saveLayer()方法、savaLayerAlpha()方法将一个图层入栈,使用restore()方法、restoreToCount()方法将一个图层出栈。入栈的时候,后面所有的操作都发生在这个图层上,而出栈的时候,则会把图像绘制到上层。下面我么就仿照API Demos里面的一个示例来看看该如何使用Layer,代码如下所示。
Paint mPaint = new Paint();
canvas.drawColor(Color.WHITE);
mPaint.setColor(Color.BLUE);
canvas.drawCircle(150,150,100,mPaint);
canvas.saveLayerAlpha(0,0,400,400,127,0);
mPaint.setColor(Color.RED);
canvas.drawCircle(200,200,100,mPaint);
canvas.restore();
在onDraw方法中绘制两个相交的圆,当然这个两个圆位于两个图层上。
接下来,将后面的图层透明度设置为0~255不同的数组值,看看不同的透明值效果有何不同。
当透明度为127是,即半透明,效果如图(3)所示。
当透明度为255时,即完全不透明,效果如图(4)所示。
当透明度为0时,即完全透明,效果如图(5)所示。
(3) (4) (5)