Android自定义View-圆形进度条

好几天不写博客了,这段时间一直没时间,感觉一直在忙,但是进度不大。
好了,言归正传,最近项目里要用到这么一个自定义view,是一个圆形的进度圆环,现在学习下怎么来自定义它。


image.png
源码下载地址

https://github.com/baojie0327/ViewAndGroup

自定义之前先分析一下,这个自定义View主要有以下几个部分组成:

  • 最外层的圆环
  • 圆环上的小圆点,会随着进度移动
  • 圆弧,会随着小圆点移动,圆环上有个渐变度
  • 文字
  • 添加动画

1. 第一步还是先考虑一下进度圆环的属性信息。

我这里可以设置的有,背景颜色,最外层圆环的宽度,最外层圆环的颜色,进度圆环的颜色(只不过这里设置了一个渐进度),圆环上小圆点的颜色,大小,还有一些文字的设置,距离的设置等。

<!--CircleProgress,血压测量进度动画-->
    <declare-styleable name="CircleProgress">
        <!--背景-->
        <attr name="backGroundColor" format="color"></attr>
        <!--圆环的颜色-->
        <attr name="ringColor" format="color"></attr>
        <!--圆环的宽度-->
        <attr name="ringSize" format="dimension"></attr>
        <!--进度圆环的颜色-->
        <attr name="ringprogressColor" format="color"></attr>

        <!--圆环上的小圆点颜色-->
        <attr name="dotColor" format="color"></attr>
        <!--圆环上的小圆点大小-->
        <attr name="dotSize" format="dimension"></attr>

        <!--进度字体颜色-->
        <attr name="textProgressColor" format="color"></attr>
        <!--进度字体大小-->
        <attr name="textProgressSize" format="dimension"></attr>

        <!--百分号颜色-->
        <attr name="textPercentColor" format="color"></attr>

        <!--固定字体设置-->
        <attr name="showProgressText" format="string"></attr>
        <!--固定字体颜色-->
        <attr name="texColor" format="color"></attr>
        <!--固定字体大小-->
        <attr name="texSize" format="dimension"></attr>

        <!--文字之间的距离-->
        <attr name="texMarginSize" format="dimension"></attr>

        <!--固定字体大小-->
        <attr name="setNumber" format="integer"></attr>

    </declare-styleable>

2. 当然,我们自定义view的名字必须叫CircleProgress,和上面attrs.xml里的名字相同,你可以试试如果不同会出现什么后果。

获得我们在attrs.xml中定义的属性
  public CircleProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //获得atts.xml定义的属性值,存储在TypedArray中
        TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CircleProgress, defStyleAttr, 0);
        int n = ta.getIndexCount();
        for (int i = 0; i < n; i++) {
            int attr = ta.getIndex(i);
            switch (attr) {


                case R.styleable.CircleProgress_ringColors: //圆环颜色
                    ring_color = ta.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.CircleProgress_ringSize: //圆环宽度
                    ringSize = ta.getDimension(R.styleable.CircleProgress_ringSize, 13);
                    break;
                case R.styleable.CircleProgress_ringprogressColor: //圆环进度颜色
                    ring_progress_color = ta.getColor(attr, Color.WHITE);
                    break;

                case R.styleable.CircleProgress_dotColor:  //小圆点
                    dot_color = ta.getColor(attr, Color.WHITE);
                    break;
                case R.styleable.CircleProgress_dotSize:  //小圆点大小
                    dotSize = ta.getDimension(R.styleable.CircleProgress_dotSize, 32);
                    break;

                case R.styleable.CircleProgress_textProgressColor: //字体进度的颜色
                    text_progress_color = ta.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.CircleProgress_textProgressSize:  //字体进度大小
                    textProgressSize = ta.getDimension(R.styleable.CircleProgress_textProgressSize, 32);
                    break;

                case R.styleable.CircleProgress_textPercentColor:  //百分号颜色
                    percent_color = ta.getColor(attr, Color.BLACK);
                    break;

                case R.styleable.CircleProgress_showProgressText:  //固定字体显示
                    showText = ta.getString(R.styleable.CircleProgress_showProgressText);
                    break;
                case R.styleable.CircleProgress_texColor: //字体的颜色
                    text_color = ta.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.CircleProgress_texSize:  //字体大小
                    texSize = ta.getDimension(R.styleable.CircleProgress_texSize, 17);
                    break;

                case R.styleable.CircleProgress_texMarginSize:  //字之间的款阿杜
                    texMarginSize = ta.getDimension(R.styleable.CircleProgress_texMarginSize, 9);
                    break;
            }
        }
        ta.recycle();
        init();
    }
重写onMeasure()方法,让控件支持wrap_content属性
 /**
     * 重写onMeasure()方法,支持wrap_content属性
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width;
        int height;
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);  //宽度的测量模式
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);  //宽度的测量值
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);  //高度的测量模式
        int heightSize = MeasureSpec.getSize(heightMeasureSpec); //高度的测量值
        //如果布局里面设置的是固定值,这里取布局里面的固定值;如果设置的是match_parent,则取父布局的大小
        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
        } else {
            //如果布局里面没有设置固定值,这里取布局的宽度的1/2
            width = widthSize * 1 / 2;
        }

        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            //如果布局里面没有设置固定值,这里取布局的高度的3/4
            height = heightSize * 3 / 4;
        }

        setMeasuredDimension(width, height);

    }
画最外层的圆
 // 画最外层的大圆环
        int centre = getWidth() / 2; //获取圆心的x坐标
        int radius = (int) (centre - 2 * ringSize); //圆环的半径
        mPaint.setColor(ring_color); //设置圆环的颜色
        mPaint.setStyle(Paint.Style.STROKE); //设置空心
        mPaint.setStrokeWidth(ringSize); //设置圆环的宽度
        mPaint.setAntiAlias(true);  //消除锯齿
        canvas.drawCircle(centre, centre, radius, mPaint); //画出圆环
画文字
 //画进度文字
        //设置进度值的大小颜色,字体样式
        textPaint.setStrokeWidth(0);
        textPaint.setColor(text_progress_color);
        textPaint.setTextSize(textProgressSize);
        textPaint.setTypeface(Typeface.MONOSPACE);

        //中间的进度百分比,先转换成float在进行除法运算,不然都为0
        int percent = (int) (((float) tempProgress / (float) maxProgress) * 100);
        String percent_draw;
        if (percent == 0) {
            percent_draw = "00";
        } else {
            percent_draw = percent + "";
        }
        float textHeight = textProgressSize; //进度字体的高度
        float textWidth = textPaint.measureText(percent_draw);

        textPaint.setTextSize(textHeight);
        //   canvas.drawText(percent_draw, centre - 5 * textWidth, centre, textPaint);
        canvas.drawText(percent_draw, centre - textWidth / 2, centre + textProgressSize / 6, textPaint);

        //画百分比号
        textPaint.setStrokeWidth(3);
        String text_percent = "%";
        textPaint.setTextSize(textHeight / 3);
        // canvas.drawText(text_percent, centre + 5 * textWidth , centre + textWidth / 2, textPaint);
        canvas.drawText("%", centre + textWidth / 2, centre + textProgressSize / 8, textPaint);

        //画展示文字
        textPaint.setColor(text_color);
        textPaint.setTextSize(texSize);
        canvas.drawText(showText, centre - textProgressSize / 2 - 10, centre + textProgressSize / 3 + texMarginSize, textPaint);
画圆弧

在画圆弧的过程中,我们创建了一个SweepGradient渲染器,来画我们的渐进色圆环。直接通过 ringProgressPaint.setShader(mSweepGradient)设置给Paint即可

        //画圆弧
        ringProgressPaint.setStyle(Paint.Style.STROKE);
        ringProgressPaint.setStrokeWidth(ringSize);

        //  ringProgressPaint.setColor(ring_progress_color);
        RectF oval = new RectF(centre - radius, centre - radius, centre
                + radius, centre + radius);  //用于定义的圆弧的形状和大小的界限

        //创建一个渲染器
        SweepGradient mSweepGradient=new SweepGradient(canvas.getWidth()/2
                ,canvas.getHeight()/2,new int[]{Color.rgb(130,213,131),Color.rgb(150,251,196),Color.rgb(130,213,131)},null);
        Matrix matrix=new Matrix();
        matrix.setRotate(-90f,canvas.getWidth()/2,canvas.getHeight()/2);
        mSweepGradient.setLocalMatrix(matrix);
        ringProgressPaint.setShader(mSweepGradient);
        canvas.drawArc(oval, 90, 360 * tempProgress / maxProgress, false, ringProgressPaint);
画小圆点

其中有个计算来计算小圆点的坐标,你可以修改这个计算部分来设置小圆点的位置,我把它放在了最下面的位置。

//画圆点
        // 画进度点   30°角度 的弧度 = 2 * PI / 360 * 30
        int rangle = 0;
        if (tempProgress == 0) {
            rangle = 360 / maxProgress;
        } else {
            rangle = 360 * (int) tempProgress / maxProgress;
        }

        double a = 0.0;//角度
        int pointX = 0;
        int pointY = 0;


        if (rangle > 0 && rangle <= 90) {
            a = 2 * Math.PI / 360 * (270 - rangle);
            pointX = centre + (int) (radius * Math.cos(a));
            pointY = centre - (int) (radius * Math.sin(a));
        } else if (rangle > 90 && rangle <= 180) {
            a = 2 * Math.PI / 360 * (rangle + 90);
            pointX = centre + (int) (radius * Math.cos(a));
            pointY = centre + (int) (radius * Math.sin(a));
        } else if (rangle > 180 && rangle <= 270) {
            a = 2 * Math.PI / 360 * (rangle);
            pointX = centre - (int) (radius * Math.sin(a));
            pointY = centre + (int) (radius * Math.cos(a));
        } else if (rangle > 270 && rangle <= 360) {
            a = 2 * Math.PI / 360 * (rangle - 90);
            pointX = centre - (int) (radius * Math.cos(a));
            pointY = centre - (int) (radius * Math.sin(a));
        }

        pointPaint.setColor(dot_color);
        pointPaint.setStyle(Paint.Style.FILL);
        pointPaint.setAntiAlias(true);  //消除锯齿
        pointPaint.setShadowLayer(10, 0, 0, Color.GRAY);
        //  Log.d("TAG", "pointX = " + pointX + "||pointY = " + pointY);
        canvas.drawCircle(pointX, pointY, dotSize, pointPaint);
其他的方法

主要是设置圆环的进度和动画的一些方法

 public synchronized void setMax(int maxProgress) {
        if (maxProgress < 0) {
            throw new IllegalArgumentException("maxProgress not less than 0");
        }
        this.maxProgress = maxProgress;
    }

    /**
     * 开启动画
     *
     * @param curProgress
     */
    public void setProgressWithAnimation(int curProgress) {
        this.curProgress = curProgress;
        animator = ValueAnimator.ofInt(0, curProgress);
        animator.setDuration(30000);
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                tempProgress = (int) animation.getAnimatedValue();
                invalidate();
            }
        });
        animator.start();

    }

    /**
     * 完成测量
     *
     * @param curProgress
     */
    public void completeMeasure(int curProgress) {
        animator.cancel();
        this.curProgress = curProgress;
        tempProgress = curProgress;
        invalidate();
    }

    /**
     * 停止Progress
     */
    public void stopProgress(){
        animator.cancel();
        invalidate();
    }

这里自定义的CircleProgress就定义完了,接下来可以在xml文件中引用,然后通过下面两行代码开启

  mCircleProgress.setMax(100);
  mCircleProgress.setProgressWithAnimation(100);

当然你也可以根据自己的需求添加其他的方法,再次就不一一添加了。
直接运行看效果图吧

Animation1.gif
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,846评论 25 707
  • packagecom.andbase.elememe.widget; importandroid.content....
    那个唐僧阅读 476评论 2 1
  • 版权声明:本文为博主原创文章,未经博主允许不得转载。微博:厉圣杰微信公众号:牙锅子(基本不更)源码:CircleP...
    牙锅子阅读 17,413评论 24 86
  • 序言:最近一段时间由于要忙学校毕业的事情和公司项目的事情,很久没有更新博客了,这两天项目中有个需要用到圆形进度条的...
    24K纯帅豆阅读 23,156评论 8 50
  • 蓝天、白云下的湖面,往往能够造就一幅震撼人心的美景。2014年途径米切尔湖(Mitchell Lake)时,曾经见...
    田园读书人阅读 940评论 4 10