本文共 8028 字,大约阅读时间需要 26 分钟。
Android自定义View是Android初中级开发工程师向高级工程师进阶所必须掌握的一块内容,其重要性不言而喻。接下来的一段时间,我会连续出几篇跟自定义View相关的文章,从易到难,跟大家一起学习Android自定义View。本文讲一个Android很简单的View——DashBoard(仪表盘),以这个例子带大家去学习自定义View的基本绘制,让大家学会自定义View,并最终掌握。
注:本文的Demo在文章的最后在开始我们的绘制DashBoard之前,有几个点是必须要掌握的,这些是绘制的基础,也是前提。
自定义View的过程就是一个绘制的过程,而绘制就好像我们画画一样,而画画就必须要会画笔,Paint就是我们的画笔。
这里重点讲一下Paint.setStyle(Style style)方法,这个方法设置的是绘制的 Style 。Style 具体来说有三种: FILL, STROKE 和 FILL_AND_STROKE 。FILL 是填充模式,STROKE 是画线模式(即勾边模式),FILL_AND_STROKE 是两种模式一并使用:既画线又填充。它的默认值是 FILL,填充模式。只有当Style是STROKE 和 FILL_AND_STROKE时,Paint.setStrokeWidth(float width)才有意义,你全是填充的就不涉及什么线条宽度了。
Paint是画笔,可画画光有画笔还不行,还必须得有画布,Canvas就是画布。Canvas这个类是绘制最重要的类,没有之一,几乎所有绘制的方法都出自于这个类。
方法先不讲,先讲一下坐标系,在Android 里,每个View 都有一个自己的坐标系,彼此之间是不影响的。这个坐标系的原点是 View 左上角的那个点;水平方向是 x 轴,右正左负;竖直方向是 y 轴,下正上负(注意,是下正上负,不是上正下负,和上学时候学的坐标系方向不一样也就是下面这个样子。
Canvas最重要也最常用的方法都是drawXXX()方法,方法太多了,我不可能一一列举,写几个最常用的,余下的请自行google
以上就是我们开始绘制DashBoard之前还需要掌握的基础,因为都用得到。Paint是画笔,主要就是设置画笔相关的属性,颜色、大小、风格等等;canvas是画布,坐标系的概念必须清楚,重要的几个方法也必须知道。
先上个图
看图其实很简单,基本上就分为三步,第一份画弧;第二步画刻度;第三步画指针。
画弧线之前,我简单讲一下自定义View的流程,创建一个DashBoard的类型继承View,重写构造方法和onDraw(Canvas canvas)
public class DashBoard extends View { public DashBoard(Context context, @Nullable AttributeSet attrs) { super(context, attrs); initPaint();//初始化Paint } protected void onDraw(Canvas canvas) { super.onDraw(canvas); }}
初始化Paint
private void initPaint(){ mPaint=new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿 mPaint.setStyle(Paint.Style.STROKE);//画线模式 mPaint.setStrokeWidth(Utils.px2dp(2));//线宽度 mPaint.setColor(Color.BLACK); }做好以上初始化工作,我们就开始第一步画弧线。调用Canvas.drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint)这个方法上面有介绍过,这里就不详细讲了,前四个参数很好设置,我们定义圆心在View的中心位置,即(getWidth()/2,geHeight()/2),半径150dp,那么前四个参数就有了,第六个参数sweepAngle是划过的度数,这个我们定义为240度,第七个是否连接中心,false,我们不需要连接中心,最后一个放自己的Paint就行了,现在的关键就是第五个参数,开始角度的计算,下面我出张图帮助大家计算一下
图中画得应该比较清楚了,不过多解释,直接上代码
private void drawArc(Canvas canvas){ rectF = new RectF(getWidth() / 2 - RADIUS, getHeight() / 2 - RADIUS, getWidth() / 2 + RADIUS, getHeight() / 2 + RADIUS); canvas.drawArc(rectF,90+(360-SWEEPANGLE)/2,SWEEPANGLE,false,mPaint); }
效果图
关于画刻度,其实就是画线吗,那画线的方法拿过来看一下,Canvas.drawLine(float startX, float startY, float stopX, float stopY, Paint paint) ,需要线的起始点和结束点的坐标,如果我要画21个刻度,那需要21个点的刻度都算一遍,我去,谁可能这么干啊。放心,我们当然不会这么干了,下面提供两种方式。
思路:坐标系的旋转+平移
之前在讲Canvas里提过,每一个Android的View都对应着有一个坐标系,坐标原点在View的左上角(可以看一下上面的那张图),现在如果我们把坐标原点平移到圆心的位置,并且再顺时针旋转30°,那么当前的坐标系就是下面这样的private void drawDegree(Canvas canvas){ canvas.translate(getWidth()/2,getHeight()/2); canvas.rotate(30); for (int i=0;i<20;i++){ //Utils.px2dp(10)是刻度线的长度,为10dp canvas.drawLine(RADIUS-Utils.px2dp(10),0,RADIUS,0,mPaint); canvas.rotate(-SWEEPANGLE/20);//逆时针选择 负值是逆时针 } //最后一根线 canvas.drawLine(RADIUS-Utils.px2dp(10),0,RADIUS,0,mPaint); canvas.rotate(240-30);//旋转回去的角度 canvas.translate(-getWidth()/2,-getHeight()/2); }
画完以后的效果图
private void drawDegree(Canvas canvas){ canvas.translate(getWidth()/2,getHeight()/2); canvas.rotate(30); for (int i=0;i<20;i++){ //纵坐标下正上负,向上提高,加负值,即-mPaint.getStrokeWidth()/2 canvas.drawLine(RADIUS-Utils.px2dp(10),-mPaint.getStrokeWidth()/2,RADIUS,-mPaint.getStrokeWidth()/2,mPaint); canvas.rotate(-SWEEPANGLE/20); } //最后一个点,因坐标系已经旋转了240度,向上提高,加整值,即mPaint.getStrokeWidth()/2 canvas.drawLine(RADIUS-Utils.px2dp(10),mPaint.getStrokeWidth()/2,RADIUS,mPaint.getStrokeWidth()/2,mPaint); canvas.rotate(240-30);//旋转回去的角度 canvas.translate(-getWidth()/2,-getHeight()/2); }看了注释,你会发现第一个点和最后一个点的提高方式不同,这也是为什么上面”相应的“三个字我要加粗了,再看一眼修改后的效果图
思路:使用PathMeasure测量弧线长度,利用PathDashPathEffect来画刻度
简单讲一下PathMeasure和PathDashPathEffectprivate void drawDegree2(Canvas canvas){ //刻度的路径 dash=new Path(); //Path.Direction.CW顺时针方向 同时顺时针切线方向为X轴正向 dash.addRect(0,0,Utils.px2dp(2),Utils.px2dp(10), Path.Direction.CW); //弧线长度的路径 Path length=new Path(); length.addArc(rectF,90+(360-SWEEPANGLE)/2,SWEEPANGLE); //测量弧线长度 pathMeasure=new PathMeasure(length,false); //这里(pathMeasure.getLength()-mPaint.getStrokeWidth())/20 弧线长度之所以减去Paint的宽度跟我第一种方式去掉宽度是一个意思 mPaint.setPathEffect(new PathDashPathEffect(dash, (pathMeasure.getLength()-mPaint.getStrokeWidth())/20,0, PathDashPathEffect.Style.ROTATE)); canvas.drawArc(rectF,90+(360-SWEEPANGLE)/2,SWEEPANGLE,false,mPaint); mPaint.setPathEffect(null); }
这里我就不细讲了,注释还是比较清楚,效果图跟第一种方式是一样的就不贴图,个人还是更加推荐第一种的画刻度方式。
画指针呢,就比较简单了,其实就是调用画线的方法,先把坐标系平移动原点位置,设置一个当前的角度currentAngle还有指针长度INDICATOR,唯一有一点难度的就是计算结束点的横纵坐标,需要用到三角函数的知识
private void drawIndicator(Canvas canvas){ canvas.translate(getWidth()/2,getHeight()/2); canvas.drawLine(0,0, (float) Math.cos(Math.toRadians(currentAngle))*INDICATOR, (float)Math.sin(Math.toRadians(currentAngle))*INDICATOR, mPaint); canvas.translate(getWidth()/2,getHeight()/2); }
Android自定义View是Android比较难的一块内容,本文主要通过绘制DashBoard来讲基本的绘制,Paint和Canvas的基本用法,接下来的一段时间内,我会继续出自定义View相关的内容,下一篇文章会讲绘制文字。
最后放上文章的demo ,觉得还不错的请给个star哈转载地址:http://vluya.baihongyu.com/