欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-02-02 11:21:40
...

今天通过自定义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, 0);
    }

    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, 0);
        //下面是一些自定义参数的默认值
        mHasLoadColor = mContext.getResources().getColor(R.color.color_3398ab);
        mNormalLoadColor = mContext.getResources().getColor(R.color.color_e0e0e0);
        mRectangleWidth = dipToPixels(mContext, 14);
        mRectangleHeight = dipToPixels(mContext, 28);
        mRectangleNum = 12;
        mInnerRadius = dipToPixels(mContext, 85);
        mTextSize = dipToPixels(mContext, 48);
        mRectangleRadius = dipToPixels(mContext, 3);
        mPercentStr = "0%";
        mPercent = 0f;
        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 / 2f, 0, mOuterRadius + mRectangleWidth / 2f, 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 + 0.5f);
        return valuePixels;
    }

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

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

    }

    @Override
    protected void onDraw(Canvas canvas) {
        int hasDownCount = 0;//已经下载百分比换算成需要画多少个已经load的长方形
        hasDownCount = Math.round(mPercent * mRectangleNum);//才用四舍五入
        //旋转之前先保存下画布的状态
        canvas.save();
        //每次旋转需要的角度
        float degress = 360.0f / mRectangleNum;
        for (int i = 0; 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)) / 2;
        //x默认是这个字符串的左边在屏幕的位置,如果设置了paint.setTextAlign(Paint.Align.CENTER);那就是字符的中心,y是指定这个字符baseline在屏幕上的位置
        canvas.drawText(mPercentStr, mOuterRadius - textWidth / 2, mOuterRadius + diffBaseLine, mTextPaint);

    }
}

带进度的圆形进度条的实现
代码已上传,链接地址LoadView