上一篇,介绍了如何做一个横向的进度条,非常简单。

今天,我们趁热打铁,做一个常见的环形进度条。诺,长下面这个样子↓

环形进度条element 环形进度条 图片_自定义View

一、分析

按照惯例,先分析元素。这里,跟画横向进度条的不同是,横向进度条是画两个圆角矩形,这里,我们需要花两个圆(进度条是根据实时进度值画出的圆弧,最终补全为360°的圆)。

环形进度条element 环形进度条 图片_自定义View_02

 

1、背景环颜色

2、背景环&进度条相同的画笔width

3、背景环设置为空心

4、进度条颜色

5、文字颜色、字体大小

二、实现

因为跟横向进度条有许多相似的地方,不同之处,也就在onDraw的实现上,此处,就在前一篇文章的横向进度条的基础上进行完善。之后,使用这一个工具类,你只需稍作修改就可以实现横向、环形进度条,灵活使用很Happy。

这里,在attr中,多加入一个变量,cp_orientation,来区分进度条的形式,是横向的还是环形的。

<declare-styleable name="CustomProgressView">
        <!--百分比文字大小-->
        <attr name="cp_percent_textsize" format="dimension"/>
        <!--百分比文字颜色-->
        <attr name="cp_percent_textcolor" format="color|integer"/>
        <!--进度条背景颜色-->
        <attr name="cp_background_color" format="color|integer"/>
        <!--进度条背景是否空心-->
        <attr name="cp_background_is_stroke" format="boolean"/>
        <!--进度条颜色-->
        <attr name="cp_progress_color" format="color|integer"/>
        <!--进度条圆角值-->
        <attr name="cp_rect_round" format="dimension"/>
        <!--进度条形式-->
        <attr name="cp_orientation">
            <enum name="horizontal" value="0"/>
            <enum name="circle" value="1"/>
        </attr>

    </declare-styleable>

根据不同的取值,来画不同的进度条

@Override
    protected void onDraw(Canvas canvas) {
        mCenterX = getWidth() / 2;
        mCenterY = getHeight() / 2;

        if (cp_orientation == HORIZONTAL) {
            drawHorProgress(mPaint, canvas);
        } else if (cp_orientation == CIRCLE) {
            drawCircleProgress(mPaint, canvas);
        }

    }

    //画环形进度条
    private void drawCircleProgress(Paint paint, Canvas canvas) {
        //画背景环
        paint.setColor(cp_background_color);
        if (cp_background_is_stroke) {
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeWidth(paint_width);
        } else {
            paint.setStyle(Paint.Style.FILL);
        }

        int radius = (int) (Math.min(getWidth() / 2, getHeight() / 2) - paint.getStrokeWidth() / 2);

        canvas.drawCircle(mCenterX, mCenterY, radius, paint);

        //画进度环
        paint.setColor(cp_progress_color);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(paint_width);

        RectF rectF = new RectF(mCenterX - radius, mCenterY - radius, mCenterX + radius, mCenterY + radius);

        canvas.drawArc(rectF, -90, 360 * progressCurrent / progressMax, false, paint);

        //画文字
        paint.setColor(cp_percent_textcolor);
        paint.setTextSize(cp_percent_textsize);
        paint.setStyle(Paint.Style.FILL);
        String value_str = (int) (progressCurrent * 100 / progressMax) + "%";
        Rect rect = new Rect();
        paint.getTextBounds(value_str, 0, value_str.length(), rect);

        float textWidth = rect.width();
        float textHeight = rect.height();
        if (textWidth >= radius * 2) {
            textWidth = radius * 2;
        }
        Paint.FontMetrics metrics = paint.getFontMetrics();
        float baseline = (getMeasuredHeight() - metrics.bottom + metrics.top) / 2 - metrics.top;
        canvas.drawText(value_str, mCenterX - textWidth / 2, baseline, paint);


    }

这里,在计算半径时,选择getWidth和getHeight的最小值,然后减去画笔宽度的一半。这样,使这个圆被包裹在view的布局之中。圆的大小可以根据布局宽高的变化进行动态调整。

进度环,画的是圆弧;进度背景画的是圆,两者有区别,如果你不熟悉画圆弧,可以去熟悉一下这方面的知识。

最后,注意一下,文字的大小不要超出圆的直径。

三、布局

<net.feelingtech.example_work.custom.ownprogress.CustomProgressView
        android:id="@+id/cpv_circle"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:layout_marginTop="20dp" />

四、跑起来

mCpv_circle = view.findViewById(R.id.cpv_circle);
        mCpv_circle.setCp_orientation(1);
        mCpv_circle.setCp_background_is_stroke(true);
        mCpv_circle.setPaint_width(DensityUtil.dip2px(getContext(),10));
        mCpv_circle.setCp_background_color(Color.parseColor("#A2CD5A"));
        mCpv_circle.setProgressMax(100);
        mCpv_circle.setCp_progress_color(Color.RED);
        mCpv_circle.setCp_percent_textsize(DensityUtil.dip2px(getContext(), 16));
        mCpv_circle.setCp_percent_textcolor(Color.RED);



        view.findViewById(R.id.bt_start).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (progressCurrent != 0) {
                    progressCurrent = 0;
                    return;
                }
                mRunnable = new Runnable() {
                    @Override
                    public void run() {

                        progressCurrent += 1;
                        mCustomProgressView.setProgressCurrent(progressCurrent);
                        mCpv_circle.setProgressCurrent(progressCurrent);
                        mHandler.postDelayed(mRunnable, 5);

                    }
                };
                mHandler.postDelayed(mRunnable, 1);
            }
        });

五、完整代码

public class CustomProgressView extends View {

    private int     cp_percent_textsize     = 18;//百分比字体大小
    private int     cp_percent_textcolor    = 0xff009ACD;
    private int     cp_background_color     = 0xff636363;
    private int     cp_progress_color       = 0xff00C5CD;
    private boolean cp_background_is_stroke = true;
    private int     cp_rect_round           = 5;
    private int     HORIZONTAL              = 0;
    private int     CIRCLE                  = 1;
    private int     cp_orientation          = HORIZONTAL;
    private Paint mPaint;
    private int   mCenterX;
    private int   mCenterY;

    private int progressCurrent = 0;
    private int progressMax     = 100;

    private int paint_width = 10;


    public CustomProgressView(Context context) {
        this(context, null);
    }

    public CustomProgressView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray typedArray = context.obtainStyledAttributes(R.styleable.CustomProgressView);
        cp_percent_textsize = (int) typedArray.getDimension(R.styleable.CustomProgressView_cp_percent_textsize, cp_percent_textsize);
        cp_percent_textcolor = typedArray.getColor(R.styleable.CustomProgressView_cp_percent_textcolor, cp_percent_textcolor);
        cp_background_color = typedArray.getColor(R.styleable.CustomProgressView_cp_background_color, cp_background_color);
        cp_progress_color = typedArray.getColor(R.styleable.CustomProgressView_cp_progress_color, cp_progress_color);
        cp_background_is_stroke = typedArray.getBoolean(R.styleable.CustomProgressView_cp_background_is_stroke, cp_background_is_stroke);
        cp_rect_round = (int) typedArray.getDimension(R.styleable.CustomProgressView_cp_rect_round, cp_rect_round);
        cp_orientation = typedArray.getInteger(R.styleable.CustomProgressView_cp_orientation, cp_orientation);
        typedArray.recycle();

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        mCenterX = getWidth() / 2;
        mCenterY = getHeight() / 2;

        if (cp_orientation == HORIZONTAL) {
            drawHorProgress(mPaint, canvas);
        } else if (cp_orientation == CIRCLE) {
            drawCircleProgress(mPaint, canvas);
        }

    }

    //画环形进度条
    private void drawCircleProgress(Paint paint, Canvas canvas) {
        //画背景环
        paint.setColor(cp_background_color);
        if (cp_background_is_stroke) {
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeWidth(paint_width);
        } else {
            paint.setStyle(Paint.Style.FILL);
        }

        int radius = (int) (Math.min(getWidth() / 2, getHeight() / 2) - paint.getStrokeWidth() / 2);

        canvas.drawCircle(mCenterX, mCenterY, radius, paint);

        //画进度环
        paint.setColor(cp_progress_color);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(paint_width);

        RectF rectF = new RectF(mCenterX - radius, mCenterY - radius, mCenterX + radius, mCenterY + radius);

        canvas.drawArc(rectF, -90, 360 * progressCurrent / progressMax, false, paint);

        //画文字
        paint.setColor(cp_percent_textcolor);
        paint.setTextSize(cp_percent_textsize);
        paint.setStyle(Paint.Style.FILL);
        String value_str = (int) (progressCurrent * 100 / progressMax) + "%";
        Rect rect = new Rect();
        paint.getTextBounds(value_str, 0, value_str.length(), rect);

        float textWidth = rect.width();
        float textHeight = rect.height();
        if (textWidth >= radius * 2) {
            textWidth = radius * 2;
        }
        Paint.FontMetrics metrics = paint.getFontMetrics();
        float baseline = (getMeasuredHeight() - metrics.bottom + metrics.top) / 2 - metrics.top;
        canvas.drawText(value_str, mCenterX - textWidth / 2, baseline, paint);


    }

    //画线性进度条
    private void drawHorProgress(Paint paint, Canvas canvas) {
        //画背景
        paint.setColor(cp_background_color);
        if (cp_background_is_stroke) {
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeWidth(1);
        } else {
            paint.setStyle(Paint.Style.FILL);
        }

        canvas.drawRoundRect(new RectF(mCenterX - getWidth() / 2, mCenterY - getHeight() / 2,
                mCenterX + getWidth() / 2, mCenterY + getHeight() / 2), cp_rect_round, cp_rect_round, paint);

        //画进度条
        paint.setColor(cp_progress_color);
        paint.setStyle(Paint.Style.FILL);

        canvas.drawRoundRect(new RectF(mCenterX - getWidth() / 2, mCenterY - getHeight() / 2,
                (int) (progressCurrent * getWidth() / progressMax), mCenterY + getHeight() / 2), cp_rect_round, cp_rect_round, paint);

        //画文字
        paint.setColor(cp_percent_textcolor);
        paint.setTextSize(cp_percent_textsize);
        paint.setStyle(Paint.Style.FILL);
        String value_str = (int) (progressCurrent * 100 / progressMax) + "%";
        Rect rect = new Rect();
        paint.getTextBounds(value_str, 0, value_str.length(), rect);

        float textWidth = rect.width();
        float textHeight = rect.height();
        if (textWidth >= getWidth()) {
            textWidth = getWidth();
        }
        Paint.FontMetrics metrics = paint.getFontMetrics();
        float baseline = (getMeasuredHeight() - metrics.bottom + metrics.top) / 2 - metrics.top;
        canvas.drawText(value_str, mCenterX - textWidth / 2, baseline, paint);

    }

    public int getCp_percent_textsize() {
        return cp_percent_textsize;
    }

    public void setCp_percent_textsize(int cp_percent_textsize) {
        this.cp_percent_textsize = cp_percent_textsize;
    }

    public int getCp_percent_textcolor() {
        return cp_percent_textcolor;
    }

    public void setCp_percent_textcolor(int cp_percent_textcolor) {
        this.cp_percent_textcolor = cp_percent_textcolor;
    }

    public int getCp_background_color() {
        return cp_background_color;
    }

    public void setCp_background_color(int cp_background_color) {
        this.cp_background_color = cp_background_color;
    }

    public int getCp_progress_color() {
        return cp_progress_color;
    }

    public void setCp_progress_color(int cp_progress_color) {
        this.cp_progress_color = cp_progress_color;
    }

    public boolean isCp_background_is_stroke() {
        return cp_background_is_stroke;
    }

    public void setCp_background_is_stroke(boolean cp_background_is_stroke) {
        this.cp_background_is_stroke = cp_background_is_stroke;
    }

    public int getCp_rect_round() {
        return cp_rect_round;
    }

    public void setCp_rect_round(int cp_rect_round) {
        this.cp_rect_round = cp_rect_round;
    }

    public int getProgressCurrent() {
        return progressCurrent;
    }

    public void setProgressCurrent(int progressCurrent) {
        if (progressCurrent > progressMax) {
            this.progressCurrent = progressMax;
        } else {
            this.progressCurrent = progressCurrent;
        }
        postInvalidate();
    }

    public int getProgressMax() {
        return progressMax;
    }

    public void setProgressMax(int progressMax) {
        this.progressMax = progressMax;
    }

    public int getCp_orientation() {
        return cp_orientation;
    }

    public void setCp_orientation(int cp_orientation) {
        this.cp_orientation = cp_orientation;
    }

    public int getPaint_width() {
        return paint_width;
    }

    public void setPaint_width(int paint_width) {
        this.paint_width = paint_width;
    }
}