最近在学习自定义view,顺便谢谢自己的路程,后面回首看看以前的自己有多菜。。。。下方的是实现图,首先需要说明的是,这个效果是看了一遍博客,然后自己想着去实现,并给出此博客:点击打开链接
正文:首先学习自定义view需要稍稍入门一下,然后是各种函数的使用,以及大量的练习,至于入门的话,推荐鸿阳大神的博客,讲的非常详细:点击打开链接,然后自己的自定义view学习之路暂时跟着一篇博客走,有兴趣的朋友可以一起:点击打开链接
首先分析组成:一个粗圆,一个细圆,一个粗圆弧,一个细圆弧,一个进度线,几个刻度线
为了简便,此处不使用自定义属性
按照流程走:先测量一下宽高
if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) { mWidth = MeasureSpec.getSize(widthMeasureSpec); } if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) { mHeight = MeasureSpec.getSize(heightMeasureSpec); } setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
因为自己学习过程中,先没有去弄wrap_content的布局,直接给的固定宽高,当然用wrap_content的时候,测量模式为ATMOST,只需要再处理一下即可
接着定义了一些成员变量
private int mHeight; private int mWidth; private Paint mPaint; //正方形边长 private int rectWidth = 400; //开始角度 private int startAngle = 135; //扫过角度 private int endAngle = 270; //外弧形边距 private int outsidePadding = 50; //每一个线相距角度 private int everyArc = 30; //圆心的x值与y值 private int centerArcX; private int centerArcY; //最内心圆圈直径 private int insideRadius = 15; //中间圆的直径 private int insideTwoRadius = 30; //大圆弧和最外层圆弧相距的距离 private int twoArcPadding = 60; //大圆弧的粗度 private int bigArcWidth = 40; //百分比进度 private float haveDownProgress = 0; //一共画几次线 private float lineCount = 10; //存储画线类的集合 private ListmLineEntities;
(自定义view的时候,疯狂注释是好习惯)
这个时候就可以开始绘制了
先画一个最外层的弧形:
//绘制最外层弧形 mPaint.setColor(Color.WHITE); mPaint.setStrokeWidth(4); mPaint.setStyle(Paint.Style.STROKE); RectF rectf = new RectF(outsidePadding, outsidePadding, outsidePadding + rectWidth, outsidePadding + rectWidth); canvas.drawArc(rectf, startAngle+10, endAngle-20, false, mPaint);
确定好矩形位置与角度,直接调用函数绘制即可
然后画里面的两个圆:
//画最内的圆圈 mPaint.setColor(Color.WHITE); mPaint.setStrokeWidth(8); canvas.drawCircle(centerArcX, centerArcY, insideRadius, mPaint); //画中间的园 mPaint.setStrokeWidth(2); canvas.drawCircle(centerArcX, centerArcY, insideTwoRadius, mPaint);
接着画最粗的圆弧,因为是进度表盘,进度分为两个区域(原谅我懒得制作动态图),大概为下面这个样子:
此处采用的方式为绘制两个不同弧度的圆弧,差别在于弧度和颜色不同
//画大圆弧,分为蓝色与白色区域,用同一个Recf RectF rectFTwo = new RectF(outsidePadding + twoArcPadding, outsidePadding + twoArcPadding, outsidePadding + rectWidth - twoArcPadding, outsidePadding + rectWidth - twoArcPadding); //画一个蓝色区域 mPaint.setColor(Color.YELLOW); mPaint.setStrokeWidth(bigArcWidth); canvas.drawArc(rectFTwo, startAngle, 270 * haveDownProgress + 2, false, mPaint); //画一个白色区域 mPaint.setColor(Color.WHITE); canvas.drawArc(rectFTwo, 270 * haveDownProgress + 135 - 2, 270 * (1 - haveDownProgress), false, mPaint);
就是这样。嗯。。。。有点蠢 不过实现了
然后开始画进度线了,图上进度有线有10条,利用PathMeasure这个类可以获取到对应圆弧的x,y坐标点,这样画线就非常方便,不需要写算法去算坐标点了:
if (!(mLineEntities.size()>0)){ //获取小圆弧的坐标点(画线有间隔,设置虚拟矩形) Path path = new Path(); RectF rectFUse = new RectF(outsidePadding + twoArcPadding - 40, outsidePadding + twoArcPadding - 40, outsidePadding + rectWidth - twoArcPadding + 40, outsidePadding + rectWidth - twoArcPadding + 40); path.addArc(rectFUse, startAngle+10, endAngle-20); PathMeasure pathMeasure = new PathMeasure(path, false); for (int i = 0; i < lineCount; i++) { float[] coords = new float[]{0f, 0f}; pathMeasure.getPosTan((i) * pathMeasure.getLength() / (lineCount - 1), coords, null); LineEntity lineEntity = new LineEntity(); lineEntity.setEndX(coords[0]); lineEntity.setEndY(coords[1]); mLineEntities.add(lineEntity); } //获取大圆弧的坐标点 Path pathTwo = new Path(); pathTwo.addArc(rectf, startAngle+10, endAngle-20); PathMeasure pathMeasureTwo = new PathMeasure(pathTwo, false); for (int i = 0; i < lineCount; i++) { float[] coords = new float[]{0f, 0f}; pathMeasureTwo.getPosTan((i) * pathMeasureTwo.getLength() / (lineCount - 1), coords, null); mLineEntities.get(i).setStartX(coords[0]); mLineEntities.get(i).setStartY(coords[1]); } }
此处说明一下:mLineEntities这个类是我用来存储进度线的类集合,加size>0的目的是因为它是固定的,不需要重复获取,里面就是获取圆弧上点的坐标点位置,getPosTan()方法的意思是,将圆弧分为多少段,然后你想取的位置位于第几段,并且这个点的位置是跟着你画圆弧的方向走的,也就是顺时针第一个
然后开始画刻度线:
//开始画线 mPaint.setColor(Color.WHITE); mPaint.setStrokeWidth(4); for (LineEntity lineEntity : mLineEntities) { canvas.drawLine(lineEntity.getStartX(), lineEntity.getStartY(), lineEntity.getEndX(), lineEntity.getEndY(), mPaint); }
大体都完事了,差一个指针,和绘制刻度线差不多
//画进度线 Path pathProgressSmall=new Path(); RectF rectFSmall=new RectF(centerArcX-insideRadius,centerArcY-insideRadius,centerArcX+insideRadius,centerArcY+insideRadius); pathProgressSmall.addArc(rectFSmall, startAngle-2, endAngle+3); PathMeasure pathMeasureSmall=new PathMeasure(pathProgressSmall,false); float[] coordsSmall=new float[]{0f,0f}; pathMeasureSmall.getPosTan(haveDownProgress*pathMeasureSmall.getLength(),coordsSmall,null); Path pathProgressBig=new Path(); RectF rectFBig=new RectF(outsidePadding + twoArcPadding+bigArcWidth/2, outsidePadding + twoArcPadding+bigArcWidth/2, outsidePadding + rectWidth - twoArcPadding-bigArcWidth/2, outsidePadding + rectWidth - twoArcPadding-bigArcWidth/2); pathProgressBig.addArc(rectFBig, startAngle-2, endAngle+3); PathMeasure pathMeasureBig=new PathMeasure(pathProgressBig,false); float[] coordsBig=new float[]{0f,0f}; pathMeasureBig.getPosTan(haveDownProgress*pathMeasureBig.getLength(),coordsBig,null); mPaint.setColor(Color.WHITE); mPaint.setStrokeWidth(4); canvas.drawLine(coordsSmall[0],coordsSmall[1],coordsBig[0],coordsBig[1],mPaint);
这样的话,自定义view已经完事了,对外提供一个方法,用于控制进度:
public void onRefresh(float progress) { haveDownProgress = progress; invalidate(); }
到此结束,开始研究自定义view不久,可能写的很多地方有问题,新手可以借鉴一下,大神请多多包涵,并指出不合理的地方!谢谢各位
另外插一句,之前放上去的博客,它使用的旋转画布的方式实现,性能应该更好,此处只是放出了自己的实现方式,并且利用PathMeasure获取坐标点算是不同处得亮点(嘿嘿)