UI效果仿写自定义Android自定义控件

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

2016-08-17  本文已影响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从设置的区间中取出,然后刷新,这样就形成了一个动画的效果。


上一篇 下一篇

猜你喜欢

热点阅读