Android绘制圆形进度条

一、背景介绍

我们在项目中,经常会见到圆形进度条,看起来很美观、直观。刚好最近项目中有这样的需求,记录一下,顺便回顾下自定义View的知识。

二、实现思路

自定义View,就是在画布中绘制View,需要重写onDraw方法。该View可以拆分成以下几部分:

1)需要画一个浅绿色的圆

2)需要画一个白色的圆

3)圆圈中有进度数字的显示

4)圆圈中可以自定义顶部和底部不同文案的提示

三、主要方法介绍

1、drawArc:由上图可以看出,该圆需要画出圆弧表示进度,所以选择drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)方法。

1)参数:

oval-用于确定圆弧形状与尺寸的椭圆边界(即椭圆外切矩形)

startAngle-开始角度(以时钟3点钟为0°,逆时针为正方向)

sweepAngle-旋转角度(以时钟3点钟为0°,逆时针为正方向)

useCenter-是否包含圆心

paint-画笔

2)绘制原理

  • 当RectF(float left,float top,float right,float bottom)中right-left等于bottom-top时(长=宽),这时画出的就是个圆。

    

  • 以矩形中心为圆心,以3点钟方向为0°,逆时针为正方向,从0°旋转startAngle度,和椭圆相交得到一条直线和一个焦点。

    

  • 从这条直线开始,正方向旋转sweepAngle度,得到另一条直线和焦点,这样就可以得到两个焦点间的圆弧。

    

2、drawText(String text, float x, float y, Paint paint):文本的绘制方法。

参数:

text-文本

x-该文本的左边与屏幕左边的距离

y-该文本baseline在屏幕上的位置

paint-画笔

需要注意的是,参数y不是表示竖直方向上的位置,而是该文本baseline在屏幕上的位置。

根据官方API说明,Paint的TextAlign属性决定了text相对于起始坐标x的相对位置。默认left,文本从x的右边开始绘制,如果是center,则x坐标在文本的中间。

baseline的介绍参考:http://www.xyczero.com/blog/article/20/

四、画圆

1)画一个背景圆

        //1-圆弧的位置:整圆,再绘制进度圆弧
        mArcCirclePaint.setColor(mCircleBackgroundColor);
        mArcCirclePaint.setStrokeWidth(mStrokeWidth);
        //屏幕宽度
        int width = getMeasuredWidth();
        RectF rectF = new RectF();
        rectF.left = (width-mWidth)/2;//左上角X
        rectF.top = mWidth*0.1f;//左上角Y
        rectF.right = (width-mWidth)/2+mWidth;//右上角X
        rectF.bottom = mWidth*0.9f;//右上角Y
        if ((rectF.right - rectF.left) > (rectF.bottom- rectF.top)){//正方形矩形,保证画出的圆不会变成椭圆
            float space = (rectF.right - rectF.left) - (rectF.bottom- rectF.top);
            rectF.left += space/2;
            rectF.right -= space/2;
        }
        canvas.drawArc(rectF,270,360,false,mArcCirclePaint);//第2个参数:时钟3点处为0度,逆时针为正方向

2)画进度圆

使用同一个Paint,改变其颜色,在画布上绘制一样大小的圆,只是旋转角度值不一样。

 mArcCirclePaint.setColor(mProgressColor);
 //设置边角为圆
 mArcCirclePaint.setStrokeCap(Paint.Cap.ROUND);
 mArcCirclePaint.setStrokeWidth(mInnerStrokeWidth);
 canvas.drawArc(rectF,270,mAngleValue,false,mArcCirclePaint);

3)绘制文本

不同文本只是位置不一样,计算好位置就可以绘制出文本了。

        //2-文本的位置:居中显示
        int centerX = width/2;
        //计算文本宽度
        int textWidth = (int) mTextPaint.measureText(mText, 0, mText.length());
        //计算baseline:垂直方向居中
        Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();
        int baseline = (getMeasuredHeight() - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;
        int textX = centerX-textWidth/2;
        mTextPaint.setColor(mTextColor);
        mTextPaint.setTextSize(mTextSize);
        canvas.drawText(mText,textX,baseline,mTextPaint);

        if (mTopText != null && !mTopText.equals("")) {
            textWidth = (int) mTextPaint.measureText(mTopText, 0, mTopText.length());
            textX = centerX - textWidth / 2;
            mTextPaint.setTextSize(mTopTextSize);
            mTextPaint.setColor(mTopTextColor);
            canvas.drawText(mTopText, textX, baseline - 20, mTextPaint);
        }

        if (mBottomText != null && !mBottomText.equals("")) {
            textWidth = (int) mTextPaint.measureText(mBottomText, 0, mBottomText.length());
            textX = centerX - textWidth / 2;
//            mTextPaint.reset();
//            mTextPaint.setAntiAlias(true);
//            mTextPaint.setLinearText(true);
            mTextPaint.setTextSize(mTopTextSize);
            mTextPaint.setColor(mBottomTextColor);
            canvas.drawText(mBottomText, textX, baseline + 20, mTextPaint);
        }

五、总结

其实很多的自定义View都是在画布canvas中画出来的,看着复杂(其实难在位置的计算),但是只要将其拆分成几部分,一一画出再组合就好了。

附上源码:工程demo

package com.example.ViewDemo;

import android.content.Context;
import android.graphics.*;
import android.util.AttributeSet;
import android.view.View;

/**
 * 自定义View方式三:重新绘制,继承View
 * 第一步:画出外圆drawArc
 * 第二步:画出进度圆drawArc
 * 第三步:画出文本:中间文本,顶部文本,底部文本drawText
 * Created by cjy on 17/6/14.
 */
public class ArcCircleView extends View {
    private Context mContext;
    /**
     * 文本画笔
     */
    private Paint mTextPaint;
    /**
     * 圆弧画笔
     */
    private Paint mArcCirclePaint;
    /**
     * 宽度
     */
    private float mWidth = 100.0f;
    /**
     * 文本
     */
    private String mText  ="0%";
    /**
     * 底部文本
     */
    private String mTopText  ="";
    /**
     * 底部文本
     */
    private String mBottomText  ="";
    /**
     * 弧度
     */
    private int mAngleValue = 0;
    /**
     * 圆的背景色:默认浅绿色
     */
    private int mCircleBackgroundColor = 0x4c11af9c;
    /**
     * 进度的颜色,默认白色
     */
    private int mProgressColor = 0xffffffff;
    /**
     * 顶部文本的颜色,默认白色
     */
    private int mTopTextColor = 0xffffffff;
    /**
     * 底部文本的颜色,默认白色
     */
    private int mBottomTextColor = 0xffffffff;
    /**
     * 文本的颜色,默认白色
     */
    private int mTextColor = 0xffffffff;
    /**
     * 边宽
     */
    private int mStrokeWidth = 8;
    /**
     * 进度圆边宽
     */
    private int mInnerStrokeWidth = 7;
    /**
     * 文本大小
     */
    private int mTextSize = 12;
    /**
     * 顶部文本大小
     */
    private int mTopTextSize = 10;

    public ArcCircleView(Context context) {
        super(context);
        init(context);
    }

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

    public ArcCircleView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    private void init(Context context){
        mContext = context;

        mTextPaint  = new Paint();
        //设置抗锯齿
        mTextPaint.setAntiAlias(true);
        //使文本看起来更清晰
        mTextPaint.setLinearText(true);

        mArcCirclePaint  = new Paint();
        mArcCirclePaint.setAntiAlias(true);
        mArcCirclePaint.setStyle(Paint.Style.STROKE);

    }

    public void setWidth(float width){
        mWidth = width;
        invalidate();
    }

    public void setText(String text){
        mText = text;
        invalidate();
    }

    public void setTopText(String text){
        mTopText = text;
        invalidate();
    }

    public void setBottomText(String text){
        mBottomText = text;
        invalidate();
    }

    public void setTextColor(int textColor) {
        this.mTextColor = mContext.getResources().getColor(textColor);
        invalidate();
    }

    public void setBottomTextColor(int bottomTextColor) {
        this.mBottomTextColor = mContext.getResources().getColor(bottomTextColor);
        invalidate();
    }

    public void setTopTextColor(int topTextColor) {
        this.mTopTextColor = mContext.getResources().getColor(topTextColor);
        invalidate();
    }

    public void setProgressColor(int progressColor) {
        this.mProgressColor = mContext.getResources().getColor(progressColor);
        invalidate();
    }

    public void setCircleBackgroundColor(int circleBackgroundColor) {
        this.mCircleBackgroundColor = mContext.getResources().getColor(circleBackgroundColor);
        invalidate();
    }

    public void setStrokeWidth(int strokeWidth){
        this.mStrokeWidth = strokeWidth;
        invalidate();
    }

    public void setInnerStrokeWidth(int innerStrokeWidth){
        this.mInnerStrokeWidth = innerStrokeWidth;
        invalidate();
    }

    public void setTextSize(int textSize){
        this.mTextSize = textSize;
        invalidate();
    }

    public void setTopTextSize(int topTextSize){
        this.mTopTextSize = topTextSize;
        invalidate();
    }

    /**
     * 设置进度
     * @param progress
     */
    public void setProgress(float progress){
        int angleValue = (int) ((progress * 1.0)/100 * 360);
        if (angleValue != 0 && progress <= 100){
            mAngleValue  = angleValue;
            mText = String.valueOf(progress)+"%";
        }
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //1-圆弧的位置:整圆,再绘制进度圆弧
        mArcCirclePaint.setColor(mCircleBackgroundColor);
        mArcCirclePaint.setStrokeWidth(mStrokeWidth);
        //屏幕宽度
        int width = getMeasuredWidth();
        RectF rectF = new RectF();
        rectF.left = (width-mWidth)/2;//左上角X
        rectF.top = mWidth*0.1f;//左上角Y
        rectF.right = (width-mWidth)/2+mWidth;//右上角X
        rectF.bottom = mWidth*0.9f;//右上角Y
        if ((rectF.right - rectF.left) > (rectF.bottom- rectF.top)){//正方形矩形,保证画出的圆不会变成椭圆
            float space = (rectF.right - rectF.left) - (rectF.bottom- rectF.top);
            rectF.left += space/2;
            rectF.right -= space/2;
        }
        canvas.drawArc(rectF,270,360,false,mArcCirclePaint);//第2个参数:时钟3点处为0度,逆时针为正方向

        mArcCirclePaint.setColor(mProgressColor);
        //设置边角为圆
        mArcCirclePaint.setStrokeCap(Paint.Cap.ROUND);
        mArcCirclePaint.setStrokeWidth(mInnerStrokeWidth);
        canvas.drawArc(rectF,270,mAngleValue,false,mArcCirclePaint);

        //2-文本的位置:居中显示
        int centerX = width/2;
        //计算文本宽度
        int textWidth = (int) mTextPaint.measureText(mText, 0, mText.length());
        //计算baseline:垂直方向居中
        Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();
        int baseline = (getMeasuredHeight() - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;
        int textX = centerX-textWidth/2;
        mTextPaint.setColor(mTextColor);
        mTextPaint.setTextSize(mTextSize);
        canvas.drawText(mText,textX,baseline,mTextPaint);

        if (mTopText != null && !mTopText.equals("")) {
            textWidth = (int) mTextPaint.measureText(mTopText, 0, mTopText.length());
            textX = centerX - textWidth / 2;
            mTextPaint.setTextSize(mTopTextSize);
            mTextPaint.setColor(mTopTextColor);
            canvas.drawText(mTopText, textX, baseline - 20, mTextPaint);
        }

        if (mBottomText != null && !mBottomText.equals("")) {
            textWidth = (int) mTextPaint.measureText(mBottomText, 0, mBottomText.length());
            textX = centerX - textWidth / 2;
//            mTextPaint.reset();
//            mTextPaint.setAntiAlias(true);
//            mTextPaint.setLinearText(true);
            mTextPaint.setTextSize(mTopTextSize);
            mTextPaint.setColor(mBottomTextColor);
            canvas.drawText(mBottomText, textX, baseline + 20, mTextPaint);
        }
    }

}

  

 

posted @ 2017-06-16 10:46  ha_cjy  阅读(3040)  评论(0编辑  收藏  举报