天天看点

带进度的圆形进度条的实现

今天通过自定义View来实现一个带进度的圆形进度条,实现的最终效果如下图所示:

带进度的圆形进度条的实现

现在来讲一下设计的思路:首先这个进度条可以自定义小圆角矩形的数量、小圆角矩形大小、小圆角矩形的圆角角度、未完成进度时的颜色,完成进度时的颜色、文字大小、文字颜色、圆形半径,所以需要自定义这些参数;那如何画这个圆形进度呢?我们需要先画一个小圆角矩形,再旋转画布再画矩形,如图这里有12个小圆角矩形,每次旋转360/12=30度,画一个小圆角矩形,共画12个。然后画中间的进度值,画中间的进度值,主要需要计算文本的位置,这个稍后再介绍。下面通过代码进行详细的讲解下实现过程。代码如下:

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.support.annotation.FloatRange;
import android.support.annotation.Nullable;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
/**
 * Created by 刘信 on 2018/4/27.
 */

public class LoadView extends View {
    //已经load的颜色值
    private int mHasLoadColor;
    //没有load的颜色值
    private int mNormalLoadColor;
    //小长方形的宽度
    private int mRectangleWidth;
    //小长方形的高度
    private int mRectangleHeight;
    //小长方形的个数
    private int mRectangleNum;
    //小长方形圆角度
    private int mRectangleRadius;
    //内半径大小
    //内半径指不包括方块的大小
    private int mInnerRadius;
    //外半径
    private int mOuterRadius;
    //文字大小
    private int mTextSize;
    //画笔
    private Paint mPaint;
    //文字的画笔
    private TextPaint mTextPaint;
   //小长方形
    private RectF mRectF;

    private Context mContext;
    //百分比字符串
    private String mPercentStr;
    //百分比
    private float mPercent;


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

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

    public LoadView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    private void init(Context context, AttributeSet attrs, int deStyleAttr) {
        mContext = context;
        TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.LoadView, deStyleAttr, );
        //下面是一些自定义参数的默认值
        mHasLoadColor = mContext.getResources().getColor(R.color.color_3398ab);
        mNormalLoadColor = mContext.getResources().getColor(R.color.color_e0e0e0);
        mRectangleWidth = dipToPixels(mContext, );
        mRectangleHeight = dipToPixels(mContext, );
        mRectangleNum = ;
        mInnerRadius = dipToPixels(mContext, );
        mTextSize = dipToPixels(mContext, );
        mRectangleRadius = dipToPixels(mContext, );
        mPercentStr = "0%";
        mPercent = f;
        if (typedArray != null) {
        //从xml中读取这些参数设置
            mHasLoadColor = typedArray.getColor(R.styleable.LoadView_HasLoadColor, mHasLoadColor);
            mNormalLoadColor = typedArray.getColor(R.styleable.LoadView_NormalLoadColor, mNormalLoadColor);
            mRectangleWidth = typedArray.getDimensionPixelSize(R.styleable.LoadView_RectangleWidth, mRectangleWidth);
            mRectangleHeight = typedArray.getDimensionPixelSize(R.styleable.LoadView_RectangleHeight, mRectangleHeight);
            mRectangleNum = typedArray.getInteger(R.styleable.LoadView_RectangleNum, mRectangleNum);
            mInnerRadius = typedArray.getDimensionPixelSize(R.styleable.LoadView_InnerRadius, mInnerRadius);
            mTextSize = typedArray.getDimensionPixelSize(R.styleable.LoadView_TextSize, mTextSize);
            mRectangleRadius = typedArray.getDimensionPixelSize(R.styleable.LoadView_RectangleRadius, mRectangleRadius);
            typedArray.recycle();
        }
        //外半径等于用户设置的内半径加上小矩形的高度
        mOuterRadius = mInnerRadius + mRectangleHeight;
        //这里是画第一个圆形矩形需要的RetcF。通过左上角和右下角坐标确定,
       // 这里左上角坐标(mOuterRadius - mRectangleWidth / 2f,0),
       //右下角坐标(mOuterRadius + mRectangleWidth / 2f, mRectangleHeight);        
       mRectF = new RectF(mOuterRadius - mRectangleWidth / f, , mOuterRadius + mRectangleWidth / f, mRectangleHeight);
        mPaint = new Paint();
        mPaint.setColor(mNormalLoadColor);
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
        mTextPaint = new TextPaint();
        //这里还可以设置文本的字体,我去掉了
        /*try {
            Typeface typeface = Typeface.createFromFile("fonts/MyriadPro-Bold.otf");
            mTextPaint.setTypeface(typeface);
        } catch (Exception e) {
            e.printStackTrace();
        }*/
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setColor(mHasLoadColor);

    }

    private int dipToPixels(Context context, float dip) {
        final float SCALE = context.getResources().getDisplayMetrics().density;
        float valueDips = dip;
        int valuePixels = (int)(valueDips * SCALE + f);
        return valuePixels;
    }

    /**
     *供外界调用
     * 请在主线程调用,设置进度 0到1
     * @param percent
     */
    public void setPercent(@FloatRange(from=, to=) float percent){
        this.mPercent=percent;
        this.mPercentStr=Math.round(percent*)+"%";
        invalidate();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //强制宽高度,在xml文件中设置宽高无效
        //设置大小请根据半径、小长方形大小来设置
        int defaultSize =  * (mInnerRadius + mRectangleHeight);
        setMeasuredDimension(defaultSize, defaultSize);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        int hasDownCount = ;//已经下载百分比换算成需要画多少个已经load的长方形
        hasDownCount = Math.round(mPercent * mRectangleNum);//才用四舍五入
        //旋转之前先保存下画布的状态
        canvas.save();
        //每次旋转需要的角度
        float degress = f / mRectangleNum;
        for (int i = ; i < mRectangleNum; i++) {

            if (hasDownCount > i) {
              //已经load的颜色
                mPaint.setColor(mHasLoadColor);
            } else {
            //没有load的颜色
                mPaint.setColor(mNormalLoadColor);
            }
            //画圆角矩形
            canvas.drawRoundRect(mRectF, mRectangleRadius, mRectangleRadius, mPaint);
            //以中心点旋转画布,这里想象下你现实中拿只笔在纸上画,每画一个你就旋转纸,你就知道了
            canvas.rotate(degress, mOuterRadius, mOuterRadius);
        }
       //回到之前的画布状态
        canvas.restore();
      //文本宽度
        float textWidth = mTextPaint.measureText(mPercentStr);
        //计算高度要注意了
        //drawText是以文本的BaseLine为准的
        //所以计算高度步骤:文本高度(descent-ascent)的一半减去descent;得到的结果
        // 是Baseline离中心圆点的偏移量,再加上半径就是高度了
        //还不懂?看下面的图
        Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
        float diffBaseLine = (-(fontMetrics.ascent + fontMetrics.descent)) / ;
        //x默认是这个字符串的左边在屏幕的位置,如果设置了paint.setTextAlign(Paint.Align.CENTER);那就是字符的中心,y是指定这个字符baseline在屏幕上的位置
        canvas.drawText(mPercentStr, mOuterRadius - textWidth / , mOuterRadius + diffBaseLine, mTextPaint);

    }
}
           
带进度的圆形进度条的实现

代码已上传,链接地址LoadView