Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android自定義水平和圓形的progressBar

Android自定義水平和圓形的progressBar

編輯:關於Android編程

雖然只是模仿,但我覺得這是學習自定義view的必經之路,所以還是把我所學到的東西拿出來與大家一起分享。

先貼出一張progressBar的gif圖,其中有水平的進度條,和圓形的進度條:

\

這裡我們的實現的思路是繼承的progressBar,然後重新去測量和繪制相關代碼,我們直接貼出源碼:

attrs相關屬性:

 




    
    
    
    
    
    
    

    
        
        
        
        
        
        
        
    

    
        
    

 

水平進度條源碼:

 

package com.example.asiatravel.learnprogressbar.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.ProgressBar;

import com.example.asiatravel.learnprogressbar.R;

/**
 * Created by kuangxiaoguo on 16/9/8.
 *
 * 水平進度條
 */
public class HorizontalProgressbarWithProgress extends ProgressBar {

    private static final int DEFAULT_TEXT_SIZE = 10;
    private static final int DEFAULT_TEXT_COLOR = 0xFFFC00D1;
    private static final int DEFAULT_COLOR_UNREACH = 0XFFD3D6DA;
    private static final int DEFAULT_HEIGHT_UNREACH = 2;
    private static final int DEFAULT_COLOR_REACH = DEFAULT_TEXT_COLOR;
    private static final int DEFAULT_HEIGHT_REACH = 2;
    private static final int DEFAULT_TEXT_OFFSET = 10;

    protected int mTextSize = sp2px(DEFAULT_TEXT_SIZE);
    protected int mTextColor = DEFAULT_TEXT_COLOR;
    protected int mUnReachColor = DEFAULT_COLOR_UNREACH;
    protected int mUNReachHeight = dp2px(DEFAULT_HEIGHT_UNREACH);
    protected int mReachColor = DEFAULT_COLOR_REACH;
    protected int mReachHeight = dp2px(DEFAULT_HEIGHT_REACH);
    protected int mTextOffSet = dp2px(DEFAULT_TEXT_OFFSET);

    protected Paint mPaint = new Paint();
    protected int mRealWidth;

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

    public HorizontalProgressbarWithProgress(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public HorizontalProgressbarWithProgress(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        obtainStyleAttrs(attrs);
    }

    /**
     * 獲取自定義屬性
     */
    private void obtainStyleAttrs(AttributeSet attrs) {
        TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.HorizontalProgressbarWithProgress);
        mTextSize = (int) ta.getDimension(R.styleable.HorizontalProgressbarWithProgress_progress_text_size, mTextSize);
        mTextColor = ta.getColor(R.styleable.HorizontalProgressbarWithProgress_progress_text_color, mTextColor);
        mUnReachColor = ta.getColor(R.styleable.HorizontalProgressbarWithProgress_progress_unreach_color, mUnReachColor);
        mUNReachHeight = (int) ta.getDimension(R.styleable.HorizontalProgressbarWithProgress_progress_unreach_height, mUNReachHeight);
        mReachColor = ta.getColor(R.styleable.HorizontalProgressbarWithProgress_progress_reach_color, mReachColor);
        mReachHeight = (int) ta.getDimension(R.styleable.HorizontalProgressbarWithProgress_progress_reach_height, mReachHeight);
        mTextOffSet = (int) ta.getDimension(R.styleable.HorizontalProgressbarWithProgress_progress_text_offset, mTextOffSet);
        ta.recycle();

        mPaint.setTextSize(mTextSize);
    }

    @Override
    protected synchronized void onDraw(Canvas canvas) {

        /**
         * save:用來保存Canvas的狀態。save之後,可以調用Canvas的平移、放縮、旋轉、錯切、裁剪等操作。
         */
        canvas.save();
        canvas.translate(getPaddingLeft(), getHeight() / 2);

        /**
         * 判斷是否需要繪制右邊的部分
         */
        boolean noNeedUnReach = false;
        /**
         * getProgress() 獲取當前進度
         * getMax()獲取progressBar的最大進度
         */
        float radio = getProgress() * 1.0f / getMax();
        String text = getProgress() + "%";
        int textWidth = (int) mPaint.measureText(text);
        float progressX = radio * mRealWidth;
        if (progressX + textWidth > mRealWidth) {
            progressX = mRealWidth - textWidth;
            noNeedUnReach = true;
        }
        float endX = progressX - mTextOffSet / 2;
        if (endX > 0) {
            mPaint.setColor(mReachColor);
            mPaint.setStrokeWidth(mReachHeight);
            canvas.drawLine(0, 0, endX, 0, mPaint);
        }

        //draw text
        mPaint.setColor(mTextColor);
        /**
         * descent()是文字的底部y坐標, ascent()是文字頂部y坐標
         */
        int y = (int) -(mPaint.descent() + mPaint.ascent() / 2);
        canvas.drawText(text, progressX, y, mPaint);

        //draw unReach bar
        if (!noNeedUnReach) {
            float start = progressX + mTextOffSet / 2 + textWidth;
            mPaint.setColor(mUnReachColor);
            mPaint.setStrokeWidth(mUNReachHeight);
            canvas.drawLine(start, 0, mRealWidth, 0, mPaint);
        }
        /**
         * restore:用來恢復Canvas之前保存的狀態。防止save後對Canvas執行的操作對後續的繪制有影響。
         * 另外,save和restore要配對使用(restore可以比save少,但不能多),如果restore調用次數比save多,會引發Error.
         */
        canvas.restore();
    }

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = measureHeight(heightMeasureSpec);
        setMeasuredDimension(width, height);
        /**
         * 上面已經通過setMeasuredDimension()確定了view的寬度和高度
         * 所以可以直接通過getMeasuredWidth()獲取view的寬度
         */
        mRealWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
    }

    private int measureHeight(int heightMeasureSpec) {
        int result;
        int mode = MeasureSpec.getMode(heightMeasureSpec);
        int size = MeasureSpec.getSize(heightMeasureSpec);

        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        } else {
            /**
             * descent()是文字的底部y坐標, ascent()是文字頂部y坐標
             * 所以兩者之差即為文字高度
             */
            int textHeight = (int) (mPaint.descent() - mPaint.ascent());
            /**
             * 三者之中的最大值即為我們所繪制的view的高度.
             */
            result = getPaddingTop() + getPaddingBottom() + Math.max(Math.max(mReachHeight, mUNReachHeight), Math.abs(textHeight));
            /**
             * 如果為wrap_content的話,則result取計算result和size的最小值
             */
            if (mode == MeasureSpec.AT_MOST) {
                result = Math.min(result, size);
            }
        }
        return result;
    }

    protected int dp2px(int dp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
    }

    protected int sp2px(int sp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
    }
}

圓形進度條源碼,我們直接繼承的上面的水平進度條:

package com.example.asiatravel.learnprogressbar.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;

import com.example.asiatravel.learnprogressbar.R;

/**
 * Created by kuangxiaoguo on 16/9/8.
 *
 * 圓形進度條
 */
public class RoundProgressbarWithProgress extends HorizontalProgressbarWithProgress {

    private int mRadius = dp2px(30);
    private int mMaxPaintWidth;

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

    public RoundProgressbarWithProgress(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RoundProgressbarWithProgress(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.RoundProgressbarWithProgress);
        mRadius = (int) ta.getDimension(R.styleable.RoundProgressbarWithProgress_radius, mRadius);
        ta.recycle();
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
    }

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        /**
         * 獲取最大的繪制寬度
         */
        mMaxPaintWidth = Math.max(mReachHeight, mUNReachHeight);
        //默認四個padding一致
        int expect = mRadius * 2 + mMaxPaintWidth + getPaddingLeft() + getPaddingRight();

        /**
         * resolveSize()方法就類似於我們自己根據MeasureSpec自己計算view的寬和高
         */
        int width = resolveSize(expect, widthMeasureSpec);
        int height = resolveSize(expect, heightMeasureSpec);

        int realWidth = Math.min(width, height);
        mRadius = (realWidth - getPaddingLeft() - getPaddingRight() - mMaxPaintWidth) / 2;
        setMeasuredDimension(realWidth, height);
    }

    @Override
    protected synchronized void onDraw(Canvas canvas) {
        String text = getProgress() + "%";
        float textWidth = mPaint.measureText(text);
        /**
         * descent()是文字的底部y坐標, ascent()是文字頂部y坐標
         */
        float textHeight = (mPaint.descent() + mPaint.ascent()) / 2;

        canvas.save();
        canvas.translate(getPaddingLeft() + mMaxPaintWidth / 2, getPaddingTop() + mMaxPaintWidth / 2);
        //draw unreach bar
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(mUnReachColor);
        canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);

        //draw reach bar
        mPaint.setColor(mReachColor);
        mPaint.setStrokeWidth(mReachHeight);
        float sweepAngle = getProgress() * 1.0f / getMax() * 360;
        canvas.drawArc(new RectF(0, 0, mRadius * 2, mRadius * 2), 0, sweepAngle, false, mPaint);

        //draw text
        mPaint.setColor(mTextColor);
        mPaint.setTextSize(mTextSize);
        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawText(text, mRadius - textWidth / 2, mRadius - textHeight, mPaint);
        canvas.restore();
    }
}

xml中的引用:




    

        

        

        

        

        

        


    

最後是MainActivity對相關progressBar的進度更新:

package com.example.asiatravel.learnprogressbar;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;

import com.example.asiatravel.learnprogressbar.view.HorizontalProgressbarWithProgress;
import com.example.asiatravel.learnprogressbar.view.RoundProgressbarWithProgress;

public class MainActivity extends AppCompatActivity {

    private static final int HORIZONTAL_WHAT = 0;
    private HorizontalProgressbarWithProgress progressBar;
    private RoundProgressbarWithProgress roundProgressBar;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            int progress = progressBar.getProgress();
            int nextProgress = ++progress;
            progressBar.setProgress(nextProgress);
            roundProgressBar.setProgress(nextProgress);
            mHandler.sendEmptyMessageDelayed(HORIZONTAL_WHAT, 100);
            if (progress >= 100) {
                mHandler.removeMessages(HORIZONTAL_WHAT);
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        progressBar = (HorizontalProgressbarWithProgress) findViewById(R.id.progressBar);
        roundProgressBar = (RoundProgressbarWithProgress) findViewById(R.id.round_progressBar);
        mHandler.sendEmptyMessage(HORIZONTAL_WHAT);
    }
}

總結:總得代碼量就是這麼多,主要還是實現的思路,然後根據思路去理解每一行代碼,如果哪個方法或者變量不明白的話,就去網上查一下相關知識,相信通過循序漸進的努力,大家都可以很好的掌握android自定義view。最後附上github源碼地址:https://github.com/kuangxiaoguo0123/LearnProgressBar
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved