自定义圆环形进度条实现

Posted 我叫白小飞

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义圆环形进度条实现相关的知识,希望对你有一定的参考价值。

最近项目里边要用进度条,进度条中间展示进度,底部展示label,因为这个组件用的地方多,所以我就直接封装了一个通用组件。

先看一下效果图:

功能有:
  1. 圆环的颜色和进度可以自定义;
  2. 中间文字可以自定义;
  3. 可以自定义圆环的宽度;
  4. 可以设置底部文字(文字内容、大小和textStyle);
  5. 提供设置进度的接口;
  6. 可以设置进度的最大值;
下边开始编码:
  1. 首先继承定义一个类继承自View,因为我们需要提供一些可配置的自定义属性:
public class CircleProgress extends View 

	private final Paint mPaint; // 画笔
    private final RectF mRectF;
    private final Rect mRect;
    private final Paint mBottomPaint; // 绘制底部文字
    private int mCurrent; // 当前进度
    private int mMax; // 最大进度
    private int mWidth; // 当前view设置的宽度

	public CircleProgress(Context context) 
        this(context,null);
    

    public CircleProgress(Context context, @Nullable AttributeSet attrs) 
        this(context, attrs,0);
    

    public CircleProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        initAttr(context,attrs);
        mPaint = new Paint();
        mBottomPaint = new Paint();
        mBottomPaint.setAntiAlias(true);
        mPaint.setAntiAlias(true);
        mRectF = new RectF();
        mRect = new Rect();
    



  1. 自定义属性,在attr.xml中新建一个declare-styleable,并命名为:CircleProgress
<!-- 圆环进度条 -->
    <declare-styleable name="CircleProgress">
        <attr name="max_progress" format="integer"/>
        <attr name="arc_bg_color" format="color"/>
        <attr name="arc_color" format="color"/>
        <attr name="arc_width" format="dimension"/>
        <attr name="mid_text_size" format="dimension"/>
        <attr name="bottom_text_size" format="dimension"/>
        <attr name="bottom_text" format="string" localization="suggested"/>
        <attr name="textStyle">
            <flag name="normal" value="0" />
            <flag name="bold" value="1" />
        </attr>
    </declare-styleable>
  1. 然后我们在initAttr()方法中解析这些属性,解析出来的属性,后边在绘制的时候需要用到:
  private void initAttr(Context context, AttributeSet attrs) 
        TypedArray attributes = context.obtainStyledAttributes(attrs,R.styleable.CircleProgress);
        mMax = attributes.getInteger(R.styleable.CircleProgress_max_progress, 100);
        mCircleBGColor = attributes.getColor(R.styleable.CircleProgress_arc_bg_color,context.getResources().getColor(R.color.color_d1, null));
        mCircleColor = attributes.getColor(R.styleable.CircleProgress_arc_color,context.getResources().getColor(R.color.colorRed, null));
        mArcWidth = attributes.getDimensionPixelOffset(R.styleable.CircleProgress_arc_width,SizeUtils.dp2px(20));
        mPercentTextSize = attributes.getDimensionPixelOffset(R.styleable.CircleProgress_mid_text_size,SizeUtils.sp2px(12));
        mBottomTextSize = attributes.getDimensionPixelOffset(R.styleable.CircleProgress_bottom_text_size,SizeUtils.sp2px(14));
        mBottomTextId = attributes.getResourceId(R.styleable.CircleProgress_bottom_text,0);
        mBottomText = attributes.getText(R.styleable.CircleProgress_bottom_text).toString();
        mBottomTextType = attributes.getInt(R.styleable.CircleProgress_textStyle,0);
        attributes.recycle();
    
  1. 解析完属性之后,就可以开始绘制了,绘制主要按照顺序绘制,我们先绘制圆环,在绘制百分比,然后绘制底部文字:
@Override
    protected void onDraw(Canvas canvas) 
        super.onDraw(canvas);
        //绘制圆形
        //设置为空心圆,如果不理解绘制弧线是什么意思就把这里的属性改为“填充”,跑一下瞬间就明白了
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setAntiAlias(true);
        //设置圆弧的宽度(圆环的宽度)
        mPaint.setStrokeWidth(mArcWidth);
        mPaint.setColor(mCircleBGColor);
        //大圆的半径
        float bigCircleRadius = mWidth / 2;
        // 小圆的半径
        float smallCircleRadius = bigCircleRadius - mArcWidth;
        // 绘制小圆
        canvas.drawCircle(bigCircleRadius,bigCircleRadius,smallCircleRadius,mPaint);
        mPaint.setColor(mCircleColor);
        mRectF.set(mArcWidth,mArcWidth,mWidth - mArcWidth,mWidth - mArcWidth);
        // 绘制圆弧
        canvas.drawArc(mRectF,90f,mCurrent * 360f / mMax, false, mPaint);
        // 计算百分比
        String percent = mCurrent * 100 / mMax + "%";
        mPaint.setStrokeWidth(0);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setTextSize(mPercentTextSize);
        mPaint.getTextBounds(percent, 0, percent.length(), mRect);
        mPaint.setColor(mCircleColor);
        // 绘制百分比
        canvas.drawText(percent,bigCircleRadius- mRect.width() / 2, bigCircleRadius + mRect.height() / 2, mPaint);
        // 绘制底部文字
        if (!TextUtils.isEmpty(mBottomText)) 
            mBottomPaint.setStrokeWidth(0);
            mBottomPaint.setTextSize(mBottomTextSize);
            mBottomPaint.getTextBounds(mBottomText.toString(),0, mBottomText.toString().length(),mRect);
            mBottomPaint.setColor(Color.BLACK);
            if (mBottomTextType == 1) 
                Typeface tf = Typeface.DEFAULT_BOLD;
                mBottomPaint.setTypeface(tf);
            
            canvas.drawText(mBottomText.toString(),bigCircleRadius - mRect.width() / 2f, mWidth + mRect.height(), mBottomPaint);
         else if(mBottomTextId > 0)
            String bottomText = getResources().getString(mBottomTextId);
            mBottomPaint.setStrokeWidth(0);
            mBottomPaint.setTextSize(mBottomTextSize);
            mBottomPaint.getTextBounds(bottomText,0, bottomText.length(),mRect);
            mBottomPaint.setColor(Color.BLACK);
            if (mBottomTextType == 1) 
                Typeface tf = Typeface.DEFAULT_BOLD;
                mBottomPaint.setTypeface(tf);
            
            canvas.drawText(bottomText,bigCircleRadius - mRect.width() / 2f, mWidth + mRect.height(), mBottomPaint);
        
    
  1. 编译之后就可以在布局文件中使用了:
 <com.towngas.top.commonlibrary.widget.CircleProgress
                    android:id="@+id/cp_timeliness_patrol"
                    android:layout_width="0dp"
                    android:layout_weight="1"
                    android:layout_height="match_parent"
                    app:max_progress="100"
                    app:arc_width="@dimen/dp_12"
                    app:arc_bg_color="#FBCCA0"
                    app:arc_color="#FBCCA0"
                    app:bottom_text_size="@dimen/sp_14"
                    app:textStyle="bold"
                    app:bottom_text="下载"/>
完整代码:
public class CircleProgress extends View 


    private final Paint mPaint; // 画笔
    private final RectF mRectF;
    private final Rect mRect;
    private final Paint mBottomPaint; // 绘制底部文字
    private int mCurrent; // 当前进度
    private int mMax; // 最大进度
    private int mWidth; // 当前view设置的宽度

    //圆弧(也可以说是圆环)的宽度
    private float mArcWidth;
    private int mCircleBGColor; // 自定义圆环背景color
    private int mCircleColor; // 自定义圆环clolr
    private int mBottomTextId = Resources.ID_NULL; // 底部文字
    private CharSequence mBottomText; //底部文字
    private float mPercentTextSize; // 百分比文字大小
    private float mBottomTextSize; // 底部文字大小
    private int mBottomTextType; // 字体样式

    public CircleProgress(Context context) 
        this(context,null);
    

    public CircleProgress(Context context, @Nullable AttributeSet attrs) 
        this(context, attrs,0);
    

    public CircleProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        initAttr(context,attrs);
        mPaint = new Paint();
        mBottomPaint = new Paint();
        mBottomPaint.setAntiAlias(true);
        mPaint.setAntiAlias(true);
        mRectF = new RectF();
        mRect = new Rect();
    

    private void initAttr(Context context, AttributeSet attrs) 
        TypedArray attributes = context.obtainStyledAttributes(attrs,R.styleable.CircleProgress);
        mMax = attributes.getInteger(R.styleable.CircleProgress_max_progress, 100);
        mCircleBGColor = attributes.getColor(R.styleable.CircleProgress_arc_bg_color,context.getResources().getColor(R.color.color_d1, null));
        mCircleColor = attributes.getColor(R.styleable.CircleProgress_arc_color,context.getResources().getColor(R.color.colorRed, null));
        mArcWidth = attributes.getDimensionPixelOffset(R.styleable.CircleProgress_arc_width,SizeUtils.dp2px(20));
        mPercentTextSize = attributes.getDimensionPixelOffset(R.styleable.CircleProgress_mid_text_size,SizeUtils.sp2px(12));
        mBottomTextSize = attributes.getDimensionPixelOffset(R.styleable.CircleProgress_bottom_text_size,SizeUtils.sp2px(14));
        mBottomTextId = attributes.getResourceId(R.styleable.CircleProgress_bottom_text,0);
        mBottomText = attributes.getText(R.styleable.CircleProgress_bottom_text).toString();
        mBottomTextType = attributes.getInt(R.styleable.CircleProgress_textStyle,0);
        attributes.recycle();
    


    /**
     * 设置当前进度
     * @param current
     */
    public void SetCurrent(int current) 
        this.mCurrent = current;
        invalidate();
    

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //getMeasuredWidth获取的是view的原始大小,也就是xml中配置或者代码中设置的大小
        //getWidth获取的是view最终显示的大小,这个大小不一定等于原始大小
        mWidth = getMeasuredWidth();
    

    @Override
    protected void onDraw(Canvas canvas) 
        super.onDraw(canvas);
        //绘制圆形
        //设置为空心圆,如果不理解绘制弧线是什么意思就把这里的属性改为“填充”,跑一下瞬间就明白了
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setAntiAlias(true);
        //设置圆弧的宽度(圆环的宽度)
        mPaint.setStrokeWidth(mArcWidth);
        mPaint.setColor(mCircleBGColor);
        //大圆的半径
        float bigCircleRadius = mWidth / 2;
        // 小圆的半径
        float smallCircleRadius = bigCircleRadius - mArcWidth;
        // 绘制小圆
        canvas.drawCircle(bigCircleRadius,bigCircleRadius,smallCircleRadius,mPaint);
        mPaint.setColor(mCircleColor);
        mRectF.set(mArcWidth,mArcWidth,mWidth - mArcWidth,mWidth - mArcWidth);
        // 绘制圆弧
        canvas.drawArc(mRectF,90f,mCurrent * 360f / mMax, false, mPaint);
        // 计算百分比
        String percent = mCurrent * 100 / mMax + "%";
        mPaint.setStrokeWidth(0);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setTextSize(mPercentTextSize);
        mPaint.getTextBounds(percent, 0, percent.length(), mRect);
        mPaint.setColor(mCircleColor);
        // 绘制百分比
        canv

以上是关于自定义圆环形进度条实现的主要内容,如果未能解决你的问题,请参考以下文章

一起Talk Android吧(第四百九十六回:自定义View实例二:环形进度条)

Qt编写自定义控件14-环形进度条

Android自定义view之圆形进度条

CSS实现不规则自定义进度条效果

酷炫进度条 自定义SeekBar

圆形半径的Android进度条动态变化