Android 繪製百分比圓環進度條

在學習自定義Vew的時候,看到一篇文章,http://blog.csdn.net/nugongahou110/article/details/49159189,然後順着其思路寫了一下,實現效果如下:

這裏寫圖片描述

我們要做的事情:實現一個百分比的圓環進度條,包含三部分,分別是:

  1. 百分比文字
  2. 背景圓
  3. 動態圓環

我們要做的思路:

  1. 先分別繪製對應的圖形;
  2. 使圖形能動態變化;
  3. 文字顯示百分比與圓環進度對應;

會涉及到的基本知識點:

  1. 自定義View 會重寫三個方法;
  2. onMeasure()方法中,測量規則、測量大小、測量模式;
  3. TypedArray 獲取自定義屬性;
  4. canvas 繪製矩形內切圓;

自定義View PercentCircle.java:

public class PercentCircle extends View{

    /**
     * 繪製百分比的圓,一共有三部分,分別是裏面的文字、背景圓、圓環;
     * 思路:首先需要三支畫筆, 設置畫筆對應的屬性等;
     */

    private Paint mTextPaint;
    private Paint mBackgroundPaint;
    private Paint mRingPaint;

    private int mCircleX;
    private int mCircleY;

    private float mCurrentAngle;
    private RectF mArcRectF;
    private float mStartSweepValue;

    private float mTargetPercent;
    private float mCurrentPercent;

    private int mDefaultRadius = 60;
    private int mRadius;

    private int mDefaultBackgroundColor = 0xffafb4db;
    private int mBackgroundColor;

    private int mDefaultRingColor = 0xff6950a1;
    private int mRingColor;

    private int mDefaultTextSize;
    private int mTextSize;


    private int mDefaultTextColor = 0xffffffff;
    private int mTextColor;

    public PercentCircle(Context context) {
        super(context);
        init(context);

    }

    public PercentCircle(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 自定義屬性,attrs
        // 使用TypedArray
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PercentCircle);

        // 背景圓的半徑
        mRadius = typedArray.getInt(R.styleable.PercentCircle_radius, mDefaultRadius);

        // 背景圓的顏色
        mBackgroundColor = typedArray.getColor(R.styleable.PercentCircle_background_color, mDefaultBackgroundColor);

        // 文字的顏色 默認白色
        mTextColor = typedArray.getColor(R.styleable.PercentCircle_text_color, mDefaultTextColor);

        // 外圓環的顏色
        mRingColor = typedArray.getColor(R.styleable.PercentCircle_ring_color, mDefaultRingColor);

        // Be sure to call recycle() when done with them
        typedArray.recycle();

        init(context);
    }

    public PercentCircle(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context){

        //圓環開始角度 -90° 正北方向
        mStartSweepValue = -90;
        //當前角度
        mCurrentAngle = 0;
        //當前百分比
        mCurrentPercent = 0;

        //設置中心園的畫筆
        mBackgroundPaint = new Paint();
        mBackgroundPaint.setAntiAlias(true);
        mBackgroundPaint.setColor(mBackgroundColor);
        mBackgroundPaint.setStyle(Paint.Style.FILL);

        //設置文字的畫筆
        mTextPaint = new Paint();
        mTextPaint.setColor(mTextColor);
        mTextPaint.setAntiAlias(true);
        mTextPaint.setStyle(Paint.Style.FILL);
        mTextPaint.setStrokeWidth((float) (0.025*mRadius));
        mTextPaint.setTextSize(mRadius/2);   //文字大小爲半徑的一半
        mTextPaint.setTextAlign(Paint.Align.CENTER);

        //設置外圓環的畫筆
        mRingPaint = new Paint();
        mRingPaint.setAntiAlias(true);
        mRingPaint.setColor(mRingColor);
        mRingPaint.setStyle(Paint.Style.STROKE);
        mRingPaint.setStrokeWidth((float) (0.075*mRadius));

        //獲得文字的字號 因爲要設置文字在圓的中心位置
        mTextSize = (int) mTextPaint.getTextSize();
    }

    // 主要是測量wrap_content時候的寬和高,因爲寬高一樣,只需要測量一次寬即可,高等於寬
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measure(widthMeasureSpec), measure(widthMeasureSpec));
    }

    // 當wrap_content的時候,view的大小根據半徑大小改變,但最大不會超過屏幕
    private int measure(int measureSpec){
        int result = 0;
        //1、先獲取測量模式 和 測量大小
        //2、如果測量模式是MatchParent 或者精確值,則寬爲測量的寬
        //3、如果測量模式是WrapContent ,則寬爲 直徑值 與 測量寬中的較小值;否則當直徑大於測量寬時,會繪製到屏幕之外;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        if( specMode == MeasureSpec.EXACTLY){
            result = specSize;
        }else {
            //result = 2*mRadius;
            //result =(int) (1.075*mRadius*2);
            result =(int) (mRadius*2 + mRingPaint.getStrokeWidth()*2);
            if(specMode == MeasureSpec.AT_MOST){
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        //1、如果半徑大於圓心的橫座標,需要手動縮小半徑的值,否則畫到屏幕之外;
        //2、改變了半徑,則需要重新設置字體大小;
        //3、改變了半徑,則需要重新設置外圓環的寬度
        //4、畫背景圓的外接矩形,用來畫圓環;
        mCircleX = getMeasuredWidth()/2;
        mCircleY = getMeasuredHeight()/2;
        if(mRadius > mCircleX){
            mRadius = mCircleX;
            mRadius = (int) (mCircleX-0.075*mRadius);
            mTextPaint.setStrokeWidth((float) (0.025*mRadius));
            mTextPaint.setTextSize(mRadius/2);
            mRingPaint.setStrokeWidth((float) (0.075*mRadius));
            mTextSize = (int) mTextPaint.getTextSize();
        }
        mArcRectF = new RectF(mCircleX-mRadius, mCircleY-mRadius, mCircleX+mRadius, mCircleY+mRadius);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //1、畫中間背景圓
        //2、畫文字
        //3、畫圓環
        //4、判斷進度,重新繪製
        canvas.drawCircle(mCircleX, mCircleY, mRadius, mBackgroundPaint);
        canvas.drawText(String.valueOf(mCurrentPercent)+"%", mCircleX, mCircleY+mTextSize/4, mTextPaint);
        canvas.drawArc(mArcRectF, mStartSweepValue, mCurrentAngle, false, mRingPaint);

        if(mCurrentPercent < mTargetPercent){
            //當前百分比+1
            mCurrentPercent+=1;
            //當前角度+360
            mCurrentAngle+=3.6;
            //每10ms重畫一次
            postInvalidateDelayed(10);
        }

        //canvas.drawRect(mArcRectF, mRingPaint);
    }

    public void setTargetPercent(float targetPercent){
        mTargetPercent = targetPercent;
    }
}
發佈了102 篇原創文章 · 獲贊 76 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章