自定义View实现一个三色进度条

移动开发 来源:TommyFen 19℃ 0评论

这大概是一个比较奇怪的需求:进度条随着的进度值的阶段变化而变化。一开始想着不自定义View就不自定义View,参考了一些PragressBar的文章,无奈似乎实现不了用ProgressBar类,或许我的水平不够,总之,最后选择了自定义View来实现该需求。

设计需求和效果展现


设计需求:一个竖直方向的进度条,在进度0~30时显示绿色,进度30~60显示黄色,在进度60~100显示红色。

效果展现:


竖直三色进度条.gif


效果如上所示,略微简陋一点。

创建自定义View


新建一个ColorsProgressBar的类,继承View,并重写前两个构造方法:

public ColorsProgressBar(Context context) {
    super(context);
}

public ColorsProgressBar(Context context, AttributeSet attrs) {
    super(context, attrs);
}

第二构造方法是从布局文件中设置控件用到的构造方法,可以用来加载自定义的属性,比如需求用到的三个颜色值、不同阶段控制颜色显示的进度值等。

自定义进度条的属性


在开始的需求描述中,讲到自定义进度条的三个颜色,颜色变化的进度范围等,当然为了使这个自定义进度条有更高的适用性,不局限于三个进度颜色以及0~100的进度值等,还需要定义进度条背景颜色值、最大进度值等。
因此,在values目下的styles.xml文件中,写上要用上的属性类型:

<declare-styleable name="ColorsProgressBar">
    <attr name="backColor" format="color"/>
    <attr name="firstColor" format="color"/>
    <attr name="secondColor" format="color"/>
    <attr name="thirdColor" format="color"/>
    <attr name="maxProgress" format="float"/>
    <attr name="progress" format="integer"/>
    <attr name="firstProcess" format="integer"/>
    <attr name="secondProcess" format="integer"/>
</declare-styleable>

八个属性,进度条背景色、第一阶段颜色、第二阶段颜色、第三阶段颜色、进度条最大值(float类型是为了方便后面的绘制)、进度值以及两个阶段颜色进度控制值。

定义属性完毕之后,回到ColorsProgressBar类中的第二个构造方法中,读取这些属性,并设置默认值。

public ColorsProgressBar(Context context, AttributeSet attrs) {
    super(context, attrs);
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ColorsProgressBar);

    backColor = a.getColor(R.styleable.ColorsProgressBar_backColor, Color.GRAY);
    firstColor = a.getColor(R.styleable.ColorsProgressBar_firstColor, Color.GREEN);
    secondColor = a.getColor(R.styleable.ColorsProgressBar_secondColor, Color.YELLOW);
    thirdColor = a.getColor(R.styleable.ColorsProgressBar_thirdColor, Color.RED);

    maxProgress = a.getFloat(R.styleable.ColorsProgressBar_maxProgress, 100f);
    mProgress = a.getInteger(R.styleable.ColorsProgressBar_progress, 0);
    firstProcess = a.getInteger(R.styleable.ColorsProgressBar_firstProcess, 30);
    secondProcess = a.getInteger(R.styleable.ColorsProgressBar_secondProcess, 60);
    a.recycle();
}

通过上述代码,已经获得刚才写的属性,并获得其对象。

实现


自定义View的实现始终在onMeasure()方法和onDraw()方法上,一个测量大小,一个绘制图像。

onMeasure()方法中,这里做简化的处理,默认在XML布局文件引用中会指定进度条大小的确定值,因此,在这不做wrap_content情况的处理。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    radius = this.getMeasuredWidth() / 3;
}

在方法中,由于实现的竖直的进度条,所以获取测量到的宽度的1/3作为进图条的圆角的半径。

然后再onDraw()方法中,绘制进度条的背景以及变化的进度条。

@Override
protected void onDraw(Canvas canvas) {

    paint.setColor(backColor);
//      设置画笔的模式为颜色填充
    paint.setStyle(Paint.Style.FILL);
//      绘制进度条的背景:一个圆角矩形,圆角半径在onMeasure()方法中得到的
    canvas.drawRoundRect(new RectF(0, 0, this.getMeasuredWidth(), this.getMeasuredHeight()),
            radius, radius, paint);
//      根据不同的进度阶段,设置画笔不同的颜色,用来绘制不同颜色的进度条
    if (mProgress < firstProcess) {
        paint.setColor(firstColor);
    } else if (mProgress < secondProcess) {
        paint.setColor(secondColor);
    } else {
        paint.setColor(thirdColor);
    }
//     根据progress的值,画不同颜色不同长度的进度条
    canvas.drawRoundRect(
            new RectF(0, this.getMeasuredHeight() * (1 - mProgress / maxProgress),
                    this.getMeasuredWidth(), this.getMeasuredHeight()),
            radius, radius, paint);
}

当然,最后还需要的公开的入口来设置变化的进度值:

public void setProgress(int progress) {
    if (progress > maxProgress) {
        mProgress = (int) maxProgress;
    } else if(progress < 0) {
        maxProgress = 0;
    } else {
        mProgress = progress;
    }
//      可根据需求做局部刷新,节约资源
    postInvalidate();//可在子线程中执行
}

使用

既然ColorsProgressBar已经完成,那么就来使用验证一下。

在布局中添加一个按钮,设置点击事件,用于控制进度条。用全路径名引用ColorsProgressBar:

<tommy.colorprogressbar.ColorsProgressBar
    android:id="@+id/color_progress_bar"
    android:layout_width="12dp"
    android:layout_height="150dp"
    android:layout_centerInParent="true"
    app:backColor="#777"
    app:firstColor="#0f0"
    app:firstProcess="30"
    app:maxProgress="100"
    app:secondColor="#ff0"
    app:secondProcess="60"
    app:thirdColor="#f00"/>

设置各种需要的属性的值。

回到Activity中,初始化ColorsProgressBar的对象,并在点击事件中,设置一个Timer类,让progress的值没隔100ms增加1:

public void start(View view) {
    progress = 0;
     final Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            colorsProgressBar.setProgress(progress);
            progress++;
            if (progress > 100) {
                timer.cancel();
            }
        }
    }, 0, 100);
}

至此,执行代码,就能达到开始的效果展现。

总结

这个自定义View看似简单,实际上也涉及到一定自定义View的知识:

  • styles.xml文件定义自定义View要的属性类型

  • 在构造方法中获取定义的属性

  • 测量和绘制

  • 设置值传递的入口方法

其实步骤简单,同时这样写的方式还具有的一定的拓展性。
附上Demo地址:ColorsProgressBar


2017.1.11 22点