自定义View(二)圆形进度条

作者: 请你吃鱼 | 来源:发表于2016-08-17 15:31 被阅读333次

    在实际开发中,有时候会用到圆形的进度条来显示所占的百分比或者进度,但是Android原生SDK中并没有提供这样的控件,这就需要我们自定义了。
    首先,我们分析一下自定义这样一个控件都需要用到哪些属性,长宽自然就不必说了,还有进度、线条的宽度、颜色等等。我们现在values文件夹中创建attrs.xml文件,下面是所需要的属性:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <attr name="color" format="color"/>
        <attr name="lineWidth" format="dimension"/>
        <attr name="progress" format="integer"/>
        <attr name="duration" format="integer"/>
        <attr name="useAnim" format="boolean"/>
    
        <declare-styleable name="CircleProgressBar">
            <attr name="color"/>
            <attr name="lineWidth"/>
            <attr name="progress"/>
            <attr name="duration"/>
            <attr name="useAnim"/>
        </declare-styleable>
    </resources>
    

    我们可以看到这里有我们需要的几个属性,duration和useAnim的作用我们稍后再讲。创建CircleProgressBar类继承View,并创建构造函数,然后获取控件的属性。

        private Paint paint;
        private int color;
        private int lineWidth;
        private int progress;
        private int duration;
        private boolean useAnim;
    
        public CircleProgressBar(Context context) {
            this(context, null);
        }
    
        public CircleProgressBar(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public CircleProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CircleProgressBar, defStyleAttr, 0);
            color = array.getColor(R.styleable.CircleProgressBar_color, Color.BLACK);
            lineWidth = array.getDimensionPixelOffset(R.styleable.CircleProgressBar_lineWidth, 0);
            progress = array.getInt(R.styleable.CircleProgressBar_progress, 30);
            duration = array.getInt(R.styleable.CircleProgressBar_duration, 2000);
            useAnim = array.getBoolean(R.styleable.CircleProgressBar_useAnim, true);
            array.recycle();
            paint = new Paint();
        }
    

    先抛出几个异常:

     private void throwException() {
            if (getWidth() != getHeight()) {
                throw new RuntimeException("width and height must be equal");
            }
            if (progress > 100) {
                throw new RuntimeException("progress can't be bigger than hundred");
            }
            if (progress < 0) {
                throw new RuntimeException("progress can't be smaller than zero");
            }
        }
    

    可以看到,如果控件的宽高不相等就会抛出一个异常,这也是为了节省一下时间。然后重写onDraw方法,调用throwException。

     @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            throwException();
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeWidth(lineWidth);
            paint.setColor(Color.parseColor("#e9e9e9"));
            canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2 - lineWidth / 2, paint);
            paint.setColor(color);
            RectF oval = new RectF(lineWidth / 2, lineWidth / 2, getWidth() - lineWidth / 2, getHeight() - lineWidth / 2);
            canvas.drawArc(oval, -90, progress * 360 / 100, false, paint);
        }
    

    我们先来分析一下上面的代码,先设置画笔的颜色,然后画一个圆,这里的圆是作为进度条的背景颜色用的,drawCircle中的参数分别是圆心的坐标,圆的半径还有画笔,这里需要注意的一点是由于进度条的线条是有宽度的,所以圆的半径并不是简单的用控件的宽度除以2,而是圆心到线条中间一点的位置,所以是控件宽度的一半减去线条宽度的一半。然后画弧度,这里弧线所在矩形的四条边的位置原理和画圆的一样。
    我们在布局文件中使用一下,可以看到刚才自定义的属性全部都可以提示出来。



    设置颜色和宽度,运行一下就是这个样子了:



    那前面的duration和useAnim属性是做什么的呢,我们经常看到有些进度条设置时是有动画的,前面这两个属性就是做这个的,我们来看一下绘制进度的这句代码
    canvas.drawArc(oval, -90, progress * 360 / 100, false, paint);
    

    这里我们是用当前进度去设置所画进度的角度,如果我们需要在设置进度时有一个动画过程,那这个progress是一个渐变的,所以我们需要用到属性动画,顾名思义,我们用这个动画是不断改变控件的属性的,所以我么需要在设置进度将progress从0到某个进度进行变化:

    public void setProgress(int p) {
            if (useAnim)
                startAnim(p);
            else
                invalidate();
        }
    
        private void startAnim(int p) {
            ValueAnimator animator = ValueAnimator.ofInt(0, p);
            animator.setDuration(duration);
            animator.start();
            animator.addUpdateListener(valueAnimator -> {
                progress = (int) valueAnimator.getAnimatedValue();
                invalidate();
            });
        }
    

    这里我们用到了ValueAnimator,在动画监听中将progress从设置的区间中取出,然后刷新,这样就形成了一个动画的效果。


    相关文章

      网友评论

      本文标题:自定义View(二)圆形进度条

      本文链接:https://www.haomeiwen.com/subject/ktumsttx.html