自定義NumProgressBar(圓形,線形)

之前在GitHub上看到一個開源的控件NumProgressBar,它與系統(tǒng)的控件ProgressBar沒有任何繼承關(guān)系,是通過繼承View自定義的,代碼寫的也是相對比較多,只有線性的進(jìn)度。
在這里我們可以通過繼承系統(tǒng)的ProgressBar來達(dá)到相同的效果且支持圓形進(jìn)度。

效果圖:

NumProgressBar

分析

自定義的流程無非就是測量,繪制的過程,無可避免需要重寫onMeasure,onDraw方法,同時也需要定義相關(guān)的屬性
1:圓形進(jìn)度條

通過觀察可以發(fā)現(xiàn),圓形進(jìn)度條有七個屬性需要定義:圓形的半徑,圓環(huán)的顏色,圓環(huán)的寬度,進(jìn)度的顏色,進(jìn)度的寬度,字體的顏色,字體的寬度。實現(xiàn)也是比較簡單,就是在onMeasure方法中進(jìn)行測量,來確定半徑的大小,在onDraw方法中繪制圓,文字及圓弧進(jìn)度,是通過繼承系統(tǒng)的ProgressBar實現(xiàn)的,所以關(guān)于進(jìn)度的獲取可以通過getProgress()直接獲取。

2:線形進(jìn)度條
線性進(jìn)度條和圓形進(jìn)度條屬性定義差不多,也是七個屬性,只不過不再需要半徑屬性,但它需要一個偏移量的屬性,來確定字體左右之間的距離。

圓形進(jìn)度條實現(xiàn)

需要在values文件夾下新建attrs.xml文件,添加需要的 屬性

<declare-styleable name="CircleNumberProgressBar">
        <attr name="circleprogress_reache_color" format="color"/>
        <attr name="circleprogress_reache_height" format="dimension"/>
        <attr name="circleprogress_unreache_color" format="color"/>
        <attr name="circleprogress_unreache__height" format="dimension"/>
        <attr name="circleprogress_text_color" format="color"/>
        <attr name="circleprogress_text_size" format="dimension"/>
        <attr name="circleprogress_radius" format="dimension"/>
    </declare-styleable>

新建CircleNumberProgressBar類,繼承系統(tǒng)的ProgressBar,添加默認(rèn)屬性,其中字體大小使用sp,間距使用dp,需要把dp,sp轉(zhuǎn)化為px;

    private static final int DEFAULT_TEXT_COLOR = 0XFFFF4081;
    private static final int DEFAULT_TEXT_SIZE = 14;
    private static final int DEFAULT_UNREACH_COLOR = 0XFF3F51B5;
    private static final int DEFAULT_UNREACH_HEIGHT = 2;
    private static final int DEFAULT_REACH_COLOR = DEFAULT_TEXT_COLOR;
    private static final int DEFAULT_REACH_HEIGHT = 3;
    private static final int DEFAULT_RADIUS = 32;

    protected int mTextColor = DEFAULT_TEXT_COLOR;
    protected int mTextSize = sp2px(DEFAULT_TEXT_SIZE);
    protected int mUnreachColor = DEFAULT_UNREACH_COLOR;
    protected int mUnreachHeight = dp2px(DEFAULT_UNREACH_HEIGHT);
    protected int mReachColor = DEFAULT_REACH_COLOR;
    protected int mReachHeight = dp2px(DEFAULT_REACH_HEIGHT);
    protected int mRadius = dp2px(DEFAULT_RADIUS);

    protected Paint mPaint;
    protected  int mMaxPaintWidth;

dp,sp與px之間轉(zhuǎn)化方法

public int sp2px(int spVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spVal, getResources().getDisplayMetrics());
    }

    public int dp2px(int dpVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, getResources().getDisplayMetrics());

    }

在構(gòu)造方法中添加init()方法,用于初始化

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

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

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

init()初始化方法

    public void init(AttributeSet attrs){
        final TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.CircleNumberProgressBar);
        mTextColor = ta.getColor(R.styleable.CircleNumberProgressBar_circleprogress_text_color, mTextColor);
        mTextSize = (int) ta.getDimension(R.styleable.CircleNumberProgressBar_circleprogress_text_size, mTextSize);
        mReachColor = ta.getColor(R.styleable.CircleNumberProgressBar_circleprogress_reache_color, mReachColor);
        mReachHeight = (int) ta.getDimension(R.styleable.CircleNumberProgressBar_circleprogress_reache_height, mReachHeight);
        mUnreachColor = ta.getColor(R.styleable.CircleNumberProgressBar_circleprogress_unreache_color, mUnreachColor);
        mUnreachHeight = (int) ta.getDimension(R.styleable.CircleNumberProgressBar_circleprogress_unreache__height, mUnreachHeight);
        mRadius = (int) ta.getDimension(R.styleable.CircleNumberProgressBar_circleprogress_radius, mRadius);
        ta.recycle();
        mPaint = new Paint();
        mPaint.setTextSize(mTextSize);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
    }

測量寬度與高度
mMaxPaintWidth變量用來表示邊緣繪制的一個寬度,該寬度取決于mUnreachHeight和mReachHeight的最大值
expect變量表示該控件請求的一個大小值,然后通過resolveSize方法交個系統(tǒng)進(jìn)行計算和分配。

@Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mMaxPaintWidth=Math.max(mReachHeight,mUnreachHeight);
        int expect=getPaddingRight() + getPaddingLeft() + mRadius * 2 + mMaxPaintWidth;
        int weight = resolveSize(expect,widthMeasureSpec);
        int height = resolveSize(expect,heightMeasureSpec);
        int readWidth=Math.min(weight,height);
        mRadius=(readWidth-getPaddingLeft()-getPaddingRight()-mMaxPaintWidth)/2;
        setMeasuredDimension(readWidth, readWidth);
    }

繪制

@Override
    protected synchronized void onDraw(Canvas canvas) {
        String text=getProgress()+"%";
        float textWeight=mPaint.measureText(text);
        float textHeight=(mPaint.descent()+mPaint.ascent())/2;

        canvas.save();
        canvas.translate(getPaddingLeft()+mMaxPaintWidth/2,getPaddingRight()+mMaxPaintWidth/2);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(mUnreachColor);
        mPaint.setStrokeWidth(mUnreachHeight);
        canvas.drawCircle(mRadius,mRadius,mRadius,mPaint);

        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(mTextColor);
        canvas.drawText(text,mRadius-textWeight/2,mRadius-textHeight,mPaint);


        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(mReachColor);
        mPaint.setStrokeWidth(mReachHeight);
        float sweepAngle=getProgress()*1.0f/getMax()*360;
        canvas.drawArc(new RectF(0,0,mRadius*2,mRadius*2),-90,sweepAngle,false,mPaint);
        canvas.restore();
    }

線形進(jìn)度條實現(xiàn)

自定義屬性

    <declare-styleable name="NumberProgressBar">
        <attr name="numprogress_reache_color" format="color"/>
        <attr name="numprogress_reache_height" format="dimension"/>
        <attr name="numprogress_unreache_color" format="color"/>
        <attr name="numprogress_unreache__height" format="dimension"/>
        <attr name="numprogress_text_color" format="color"/>
        <attr name="numprogress_text_size" format="dimension"/>
        <attr name="numprogress_offset" format="dimension"/>
    </declare-styleable>

默認(rèn)屬性

    private static final int DEFAULT_TEXT_COLOR = 0XFFFF4081;
    private static final int DEFAULT_TEXT_SIZE = 12;
    private static final int DEFAULT_UNREACH_COLOR = 0XFF3F51B5;
    private static final int DEFAULT_UNREACH_HEIGHT = 2;
    private static final int DEFAULT_REACH_COLOR = DEFAULT_TEXT_COLOR;
    private static final int DEFAULT_REACH_HEIGHT = 2;
    private static final int DEFAULT_OFFSET = 10;


    protected int mTextColor = DEFAULT_TEXT_COLOR;
    protected int mTextSize = sp2px(DEFAULT_TEXT_SIZE);
    protected int mUnreachColor = DEFAULT_UNREACH_COLOR;
    protected int mUnreachHeight = dp2px(DEFAULT_UNREACH_HEIGHT);
    protected int mReachColor = DEFAULT_REACH_COLOR;
    protected int mReachHeight = dp2px(DEFAULT_REACH_HEIGHT);
    protected int mOffset = dp2px(DEFAULT_OFFSET);

    protected Paint mPaint ;
    protected  Paint mTextPaint;
    protected int mRealWidth;

構(gòu)造方法

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

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

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

初始化方法init()

public void init(AttributeSet attrs){
        final TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.NumberProgressBar);
        mTextColor = ta.getColor(R.styleable.NumberProgressBar_numprogress_text_color, mTextColor);
        mTextSize = (int) ta.getDimension(R.styleable.NumberProgressBar_numprogress_text_size, mTextSize);
        mReachColor = ta.getColor(R.styleable.NumberProgressBar_numprogress_reache_color, mReachColor);
        mReachHeight = (int) ta.getDimension(R.styleable.NumberProgressBar_numprogress_reache_height, mReachHeight);
        mUnreachColor = ta.getColor(R.styleable.NumberProgressBar_numprogress_unreache_color, mUnreachColor);
        mUnreachHeight = (int) ta.getDimension(R.styleable.NumberProgressBar_numprogress_unreache__height, mUnreachHeight);
        mOffset = (int) ta.getDimension(R.styleable.NumberProgressBar_numprogress_offset, mOffset);
        ta.recycle();
        mPaint = new Paint();
        mTextPaint=new Paint();
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setAntiAlias(true);

        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
    }

測量方法
在改方法中我們只需要對高度進(jìn)行測量計算就可以
它的高度由文字,mReachHeight,mUnreachHeight三者的高度來確定,我們需要獲取三者的最大值作為控件的高度,至于寬度,使用系統(tǒng)分配的就可以

@Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int textHeight = (int) (mTextPaint.descent() - mTextPaint.ascent());
        int maxHeight=Math.max(textHeight,Math.max(mReachHeight,mUnreachHeight));
        int expectHeight=getPaddingTop()+getPaddingBottom()+maxHeight;
        int heightVal=resolveSize(expectHeight,heightMeasureSpec);
        int widthVal = MeasureSpec.getSize(widthMeasureSpec);
        setMeasuredDimension(widthVal,heightVal);
        mRealWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
    }

繪制
在繪制過程中需要一個布爾的變量noNeedUnreach 來標(biāo)識是否繪制完成
該繪制分為三個部分,未繪制,已繪制和進(jìn)度文字,當(dāng)繪制的寬度超過控件寬度,說明繪制已經(jīng)完成

@Override
    protected synchronized void onDraw(Canvas canvas) {
        canvas.save();
        canvas.translate(getPaddingLeft(), getHeight() / 2);
        boolean noNeedUnreach = false;
        float radio = getProgress() * 1.0f / getMax();

        String text = getProgress() + "%";

        int textWidth = (int) mTextPaint.measureText(text);

        float progressX = radio * mRealWidth;
        if (progressX + textWidth > mRealWidth) {
            progressX = mRealWidth - textWidth;
            noNeedUnreach = true;
        }
        float endx = progressX - mOffset / 2;
        if (endx > 0) {
            mPaint.setColor(mReachColor);
            mPaint.setStrokeWidth(mReachHeight);
            canvas.drawLine(0, 0, endx, 0, mPaint);
        }

        mTextPaint.setColor(mTextColor);
        mTextPaint.setStrokeWidth(mUnreachHeight);
        int y = (int) (-(mTextPaint.descent() + mTextPaint.ascent()) / 2);
        canvas.drawText(text, progressX, y, mTextPaint);
        if (!noNeedUnreach) {
            float startX = progressX + mOffset / 2 + textWidth;
            mPaint.setColor(mUnreachColor);
            mPaint.setStrokeWidth(mUnreachHeight);
            canvas.drawLine(startX, 0, mRealWidth, 0, mPaint);
        }
        canvas.restore();
    }

測試代碼

布局文件
如果使用自定屬性,需要添加

    xmlns:app="http://schemas.android.com/apk/res-auto"
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity"
    >

    <com.mersens.view.CircleNumberProgressBar
        android:id="@+id/progressbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="10dp"
        android:layout_margin="10dp"
        app:circleprogress_radius="32dp"
        app:circleprogress_text_size="16sp"
        />
    <com.mersens.view.NumberProgressBar
        android:id="@+id/numberProgressBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        app:numprogress_reache_color="#03a9f4"
        app:numprogress_text_color="#ff5722"
        app:numprogress_unreache_color="#673ab7"
       />
</LinearLayout>

MainActivity

public class MainActivity extends AppCompatActivity {

    private CircleNumberProgressBar progressbar;
    private NumberProgressBar numberProgressBar;
    private MyRunnable myRunnable=new MyRunnable();
    private static final int DELAYED_TIME=100;
    private  int progress=0;

    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
    }

    public void init() {
        progressbar = (CircleNumberProgressBar) findViewById(R.id.progressbar);
        numberProgressBar=(NumberProgressBar)findViewById(R.id.numberProgressBar);
        handler.postDelayed(myRunnable,DELAYED_TIME);
    }

    class MyRunnable implements Runnable{
        @Override
        public void run() {
            progress++;
            if(progress<=100){
                progressbar.setProgress(progress);
                numberProgressBar.setProgress(progress);
                handler.postDelayed(myRunnable,DELAYED_TIME);
            }else{
                handler.removeCallbacksAndMessages(myRunnable);
            }
        }
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        handler.removeCallbacksAndMessages(myRunnable);
    }
}

源碼地址:https://github.com/Mersens/CircleNumberProgressBar

使用方法

Add it in your root build.gradle at the end of repositories:

    allprojects {
        repositories {
            ...
            maven { url "https://jitpack.io" }
        }
    }

Add the dependency

dependencies {
           compile 'com.github.Mersens:CircleNumberProgressBar:1.0'
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容