android自定义view学习之时尚表盘

Posted LZ涸泽而渔

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android自定义view学习之时尚表盘相关的知识,希望对你有一定的参考价值。

最近在学习自定义view,顺便谢谢自己的路程,后面回首看看以前的自己有多菜。。。。下方的是实现图,首先需要说明的是,这个效果是看了一遍博客,然后自己想着去实现,并给出此博客:点击打开链接


正文:首先学习自定义view需要稍稍入门一下,然后是各种函数的使用,以及大量的练习,至于入门的话,推荐鸿阳大神的博客,讲的非常详细:点击打开链接,然后自己的自定义view学习之路暂时跟着一篇博客走,有兴趣的朋友可以一起:点击打开链接

接下来进入我的实现过程了:(发现错误了,但是并不修正此博客, 算是记录错误:onDraw这个方法,刷新界面就会执行,因此不要去new对象,不然会导致浪费大量的资源,关于path,因为不能重复的new,所以每次对path进行一下reset就好

首先分析组成:一个粗圆,一个细圆,一个粗圆弧,一个细圆弧,一个进度线,几个刻度线

为了简便,此处不使用自定义属性

按照流程走:先测量一下宽高

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 List<LineEntity> mLineEntities;

(自定义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获取坐标点算是不同处得亮点(嘿嘿)

以上是关于android自定义view学习之时尚表盘的主要内容,如果未能解决你的问题,请参考以下文章

Android学习之自定义view详解

自定义控件:绘制圆环的实现过程

Android自定义View初步

Android开发学习之控件架构与自定义控件

Android开发学习之控件架构与自定义控件

Android笔记自定义View之制作表盘界面