## 前言
每次在讲正事前总要啰嗦几句,其实我也不想,只是文章没得一个开头,看起来就有点不爽,所以大家见谅,不喜欢的可以直接跳过。Android的绘图功能还是十分强大的,只要你有一个敢于创新的心,就没有什么画不出来的,接下来我将,从画图时所使用的工具开始介绍,希望能够帮助到大家,我自己也能够更好的巩固这方面的知识。
绘图工具(一)–画布(canvas)
canvas类相当于一个画布,我们通常把它理解成系统提供给我们的一块内存区(但实际上它只是一套画图的API),并且提供了一套在这个区域操作的一系列方法,着些方法全是用来画图API
以下是canvas常用画图的方法:
drawRect(RectF rect, Paint paint) //绘制区域,参数一为RectF一个区域
drawPath(Path path, Paint paint) //绘制一个路径,参数一为Path路径对象
drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) //贴图,参数一就是我们常规的Bitmap对象,参数二是源区域(这里是bitmap),参数三是目标区域(应该在canvas的位置和大小),参数四是Paint画刷对象,因为用到了缩放和拉伸的可能,当原始Rect不等于目标Rect时性能将会有大幅损失。
drawLine(float startX, float startY, float stopX, float stopY, Paintpaint) //画线,参数一起始点的x轴位置,参数二起始点的y轴位置,参数三终点的x轴水平位置,参数四y轴垂直位置,最后一个参数为Paint 画刷对象。
drawPoint(float x, float y, Paint paint) //画点,参数一水平x轴,参数二垂直y轴,第三个参数为Paint对象。
drawText(String text, float x, floa ty, Paint paint) //渲染文本,Canvas类除了上面的还可以描绘文字,参数一是String类型的文本,参数二x轴,参数三y轴,参数四是Paint对象。
drawOval(RectF oval, Paint paint)//画椭圆,参数一是扫描区域,参数二为paint对象;
drawCircle(float cx, float cy, float radius,Paint paint)// 绘制圆,参数一是中心点的x轴,参数二是中心点的y轴,参数三是半径,参数四是paint对象;
drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)//画弧,参数一是RectF对象,一个矩形区域椭圆形的界限用于定义在形状、大小、电弧,参数二是起始角(度)在电弧的开始,参数三扫描角(度)开始顺时针测量的,参数四是如果这是真的话,包括椭圆中心的电弧,并关闭它,如果它是假这将是一个弧线,参数五是Paint对象;
除此以外还有一些方法特别有用:
Canvas.save() 将之前所有已经绘制的图像保存起来,让后续的操作好像在一个新的图层中操作一样
Canvas.restore() 该方法可以理解为PS中的合并图层操作,作用是将save()之前后的图像进行合并
Canvas.translate(x,y) 该方法的意思是将坐标系的原点移动到(x,y) 处,之后所有的操作都将以(x,y)为原点进行
Canvas.rotate() 将坐标系选装一定的角度
绘图工具(二)–画笔(Paint)
系统通过Canvas对象来提供绘图方法,但是还缺少一个绘图的笔来完成整个绘图的操作。可以看到Canvas的很多方法的最后都需要传入一个Paint对象,由此可见Paint画笔的重要性,Paint对象在画图中主要起到对线条颜色,效果,风格的控制上。接下来是一些常用的Paint的属性和对应功能。
setAntiAlias(); 设置画笔的锯齿效果
setColor(); 设置画笔的颜色
setARGB(); 设置画笔的A , R , G , B值
setAlpha(); 设置画笔的Alpha值(透明度 取值0~255之间,越小透明度越高)
setTextSize(); 设置字体的尺寸(有时在绘图中也需要绘画字体)
setStyle(); 设置画笔的风格(空心或者实心)
setStrokeWidth(); 设置画笔的宽度
绘图工具(三)–路径(Path)
path类用于记录一些路径,可以当作是看不见的线,也可以用作记录手指接触屏幕滑动的路径,绘制复杂图形轮廓等等,在绘图和动画中,path类都发挥着重要的作用。
构造方法:
创建一个含有src的path对象(目前还没有用过)
public Path(Path src) {
long valNative = 0;
if (src != null) {
valNative = src.mNativePath;
isSimplePath = src.isSimplePath;
if (src.rects != null) {
rects = new Region(src.rects);
}
}
mNativePath = init2(valNative);
}
接下来列举一些常用的方法
moveTo(float x , float y) 移动绘制起点到(x,y)的位置开始绘制
lineTo(float x , float y) 连接从起点到(x,y)的直线,默认起点为(0,0)
rMoveTo(float dx, float dy) 在上一次绘制的终点的基础上,移动dx,dy的距离得到新的起点。(这里的dx , dy为移动的距离)
rLineTo(float dx , float dy)在上一次绘制的基础上,移动dx , dy的连线。和lineTo()的效果一样,只是起点是上一次绘制的终点,坐标表示的是距离。
quadTo(float x1 , float y1 , float x2 , float y2) 根据两个点你控制,绘制贝塞尔曲线
cubicTo(float x1, float y1, float x2, float y2, float x3, float y3)根据三点控制,绘制贝尔塞曲线
arcTo(RectF oval, float startAngle, float sweepAngle)用于绘制圆弧
addRect(float left, float top, float right, float bottom, Direction dir)添加矩形
addOval(RectF oval, Direction dir) 添加一个闭环圆环路径
addCircle(float x, float y, float radius, Direction dir) 添加一个圆形路径
addRoundRect(RectF rect, float rx, float ry, Direction dir)添加一个圆角矩形路径
offset(float dx, float dy, Path dst) 通过dx,dy移动路径,如图dst为null则修改原路径,即src
reset() 清空当前路径中的设置信息
rewind() 清空当前path,清空所有的直线曲线,但保留其数据结构,方便重用
注:
Path.Direction 用来指定添加到path中的闭合图形的绘制方向(两个值,分别为顺时和逆时)
CW 表示顺时针
CCW表示逆时针
FillType表示填充模式:
FillType.WINDING 默认值,当两个图形香蕉,正常相交情况显示
FillType.EVEN_ODD 取path所在并不相交的区域
FillType.INVERSE_WINDING 取path的外部区域
FillType.INVERSE_EVEN_ODD 取path外部和相交区域
(INVERSE就是取反的意思)下面是几个填充结果
FillType.WINDING
FillType.EVEN_ODD
FillType.INVERSE_WINDING
INVERSE_EVEN_ODD
接下来通过一个实例来巩固一下这个绘制的内容。我们要做的是一个时钟的图形,如下图:
首先分析这个图形所需要绘制的元素有:表盘,刻度,指针,数字
首先我们选择绘制表盘,表盘就是一个大圆盘,直接调用canvas.drawCircle()方法绘制一个圆就行了,关键是定圆心和半径,这里直接确定在屏幕中间了
private Paint paintCircle;
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();//获取屏幕宽高
paintCircle = new Paint();//初始化画笔
paintCircle.setColor(Color.BLACK);//设置颜色
paintCircle.setAntiAlias(true);
paintCircle.setStyle(Paint.Style.STROKE);
paintCircle.setStrokeWidth(5);//设置笔宽
canvas.drawCircle(mWidth/2 , mHeight/2 , mWidth/2 , paintCircle);//画圆
完成了表盘后就开始画刻度线,这个也不难就是一条线段,只要确定了两端点的坐标就可以轻松搞定,关键问题是斜着的线段如何获取坐标呢,这里利用到了旋转坐标系,看如下代码:
paintDegree = new Paint();
paintCircle.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 +60,paintDegree);
String degree = String.valueOf(i);
canvas.drawText(degree,mWidth/2 - paintDegree.measureText(degree)/2, mHeight/2 - mWidth/2 + 90,paintDegree);
}
canvas.rotate(15 ,mWidth/2 , mHeight/2);//旋转坐标系
}
最后绘制时针,计时从原点开始的两条线段
paintHour = new Paint();
paintHour.setStrokeWidth(20);
Paint paintMinute = new Paint();
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();//组合画布
到此为止就完成了整个绘画的过程,当然其中没有用到path,不过不要紧,在后续的动画讲解中,path会有很多作用的,还有前面有一篇介绍SurfaceView使用的文章中,就有使用path来记录手指在屏幕滑动的轨迹。绘图机制中的绘制就到此结束了,当然还有3D的绘图,我自己还在学习当中~希望这篇文章能够帮助到大家~我们一起加油~