1. 程式人生 > >Android 自定義進度條ColorfulProgressBar,原理簡單、效果很棒

Android 自定義進度條ColorfulProgressBar,原理簡單、效果很棒

Android-ColorfulProgressBar

簡介:

這是一個自定義的Progressbar,效果看著還行吧,滾動的雙色斜條作為進度條,有點類似Bootstrap風格。原生Progress的基本操作都有,自行觀摩我的原始碼吧,挺簡單的。

示例:

演示Demo

特性

與原生Progress相比,感覺更漂亮一點,可以顯示進度值,背景凹凸感明顯,進度條效果更加立體。

原理說明

額,挺簡單的。不過感覺我的做法有點複雜了,我先自定義了一個View,專門作為進度條的顯示圖層,如下所示

然後將其佈局在高度不超過20dp的ColorfulProgressBar父佈局中,設定Y方向的偏移量,然後動畫迴圈改變Y座標,實現斜條滾動的動畫效果,當你呼叫setProgress方法時,則改變其在父佈局的X座標實現進度顯示的功能,進度文字同樣原理新增到了父佈局中。

具體程式碼

ColorfulView.java 這個類例項化後新增到檢視當中後,就如上如所示。

package com.capton.colorfulprogressbar;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.support.annotation.Nullable;
import android.view.View;
/**
 *這是一個繪製了多個等腰直角三角形的View,呈現出一個由很多個斜條紋組成的雙色畫布
 * 可以自行通過new ColorfulView(Context context,int width,Paint paint,Paint paint2)方法,構建出來看效果
 * Created by capton on 2017/8/10.
 */
public class ColorfulView extends View { private Paint paint; private Paint paint2; private int mWidth; public ColorfulView(Context context,int width,Paint paint,Paint paint2) { super(context); mWidth=width; this.paint=paint; this.paint2=paint2; ; } @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(mWidth,mWidth); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); float x,y; float x2,y2; for (int i = 30; i > 0; i--) { Path p1=new Path(); x=y=((float)mWidth/30)*i; p1.lineTo(0,y); p1.lineTo(x,0); p1.lineTo(0,0); p1.close(); if(i%2==0) { canvas.drawPath(p1, paint); }else { canvas.drawPath(p1, paint2); } } for (int i = 0; i < 30; i++) { Path p2=new Path(); x2=y2=((float)mWidth/30)*i; p2.moveTo(mWidth,mWidth); p2.lineTo(mWidth,y2); p2.lineTo(x2,mWidth); p2.lineTo(mWidth,mWidth); p2.close(); if(i%2!=0) { canvas.drawPath(p2, paint); }else { canvas.drawPath(p2, paint2); } } } }

主體ColorfulProgressbar.java

package com.capton.colorfulprogressbar;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint; 
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.ViewGroup; 
import android.view.animation.LinearInterpolator;
import android.view.animation.TranslateAnimation; 
import android.widget.TextView;
/**
 * Created by capton on 2017/8/10.
 */
public class ColorfulProgressbar extends ViewGroup {

    public static final String STYLE_NORMAL="normal";  //正常單色樣式
    public static final String STYLE_COLORFUL="colorful"; //雙色樣式
    public   String style="colorful";

    private ColorfulView colofulView;     //雙色View
    private TextView progressView;     // 第二進度條
    private TextView backgroundMaskView;     // 背景罩
    private TextView maskView;         // 進度條白色漸變圖層
    private TextView percentView;       //文字顯示進度層
    private Paint progressPaint=new Paint();  //顏色一畫筆
    private Paint progressPaint2=new Paint();  //顏色二畫筆
    private Paint backgroundPaint=new Paint();  //背景畫筆

    private  int maxHeight;  // 高度最大值
    private  int mHeight;     // 高度
    private  int mWidth;      // 寬度

    private  long progress;     //進度值
    private  long secondProgress;   //第二進度值
    private  long maxProgress=100;  //默然最大進度100
    private  int backgroundColor=getResources().getColor(R.color.progressBg);    //背景顏色
    private  int secondProgressColor=getResources().getColor(R.color.secondProgressColor);  //第二進度條顏色
    private  int progressColor=getResources().getColor(R.color.colorAccent);    //進度條顏色一
    private  int progressColor2=getResources().getColor(R.color.ltcolorAccent);  //進度條顏色二
    private  int percentColor=Color.YELLOW;          //進度文字的顏色,預設黃色
    private  int percentShaderColor=Color.DKGRAY;   //進度文字的陰影顏色,預設暗灰色

    private TranslateAnimation translateAnimation; //雙色進度條的動畫1
    private TranslateAnimation translateAnimation2; //雙色進度條的動畫2
    private boolean animationOn=true;      //動畫開啟的標誌位
    private boolean animationCancle;         //動畫取消的標誌位

    private boolean showPercent=true; // 是否顯示進度文字的標誌位
    private boolean setBackgroudColor; // 是否改變背景顏色的標誌位

    private boolean isSetBackgroudColorByXml;

    public ColorfulProgressbar(Context context) {
        super(context);
        setWillNotDraw(false);  //自定義ViewGroup,預設不呼叫onDraw方法,而這裡有很多步驟需要在ondraw中操作,所以呼叫setWillNotDraw(false)
        initSettings();
    }
    public ColorfulProgressbar(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public ColorfulProgressbar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setWillNotDraw(false);  //自定義ViewGroup,預設不呼叫onDraw方法,而這裡有很多步驟需要在ondraw中操作,所以呼叫setWillNotDraw(false)
        getParameter(context,attrs);
        isSetBackgroudColorByXml=true;
    }

    /**
     * 從xml中獲取各個屬性
     * @param context
     * @param attrs
     */
    private void getParameter(Context context, AttributeSet attrs){
        if(attrs!=null) {
            TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ColorfulProgressbar);
            style = ta.getString(R.styleable.ColorfulProgressbar_style);
            if (!STYLE_NORMAL.equals(style) && !STYLE_COLORFUL.equals(style)) {
                style = STYLE_COLORFUL;  //如果沒有在xml中顯示設定style,預設使用雙色進度條
            }
            progress = ta.getInteger(R.styleable.ColorfulProgressbar_progress, (int)progress);
            secondProgress = ta.getInteger(R.styleable.ColorfulProgressbar_secondProgress,(int)secondProgress);
            maxProgress = ta.getInteger(R.styleable.ColorfulProgressbar_max, (int) maxProgress);
            backgroundColor = ta.getColor(R.styleable.ColorfulProgressbar_backgroundColor, backgroundColor);
            progressColor = ta.getColor(R.styleable.ColorfulProgressbar_progressColor1, progressColor);
            progressColor2 = ta.getColor(R.styleable.ColorfulProgressbar_progressColor2, progressColor2);
            animationOn = ta.getBoolean(R.styleable.ColorfulProgressbar_animation,animationOn);
            ta.recycle();
            initSettings();
        }
    }

    private void initSettings() {
        partition2= (float)this.progress/maxProgress; //進度條百分比
        partition= (float)this.secondProgress/maxProgress; //第二進度條百分比
        backgroundPaint.setAntiAlias(true);
        backgroundPaint.setColor(backgroundColor);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSize=MeasureSpec.getSize(widthMeasureSpec);
        int widthMode=MeasureSpec.getMode(widthMeasureSpec);
        int heightSize=MeasureSpec.getSize(heightMeasureSpec);
        int heightMode=MeasureSpec.getMode(heightMeasureSpec);

        widthSize=widthMode==MeasureSpec.EXACTLY?widthSize:DisplayUtil.dip2px(getContext(),200);
        heightSize=heightMode==MeasureSpec.EXACTLY?heightSize:DisplayUtil.dip2px(getContext(),4);

        /*
        * 當你設定高度大於20dp時,強制高度變為20dp,太高了不美觀。
        * */
        maxHeight=DisplayUtil.dip2px(getContext(),20);
        if(mHeight>maxHeight) {
            mHeight = maxHeight;
        }
        /*
        * 設定高度
        * */
        if(mHeight>0){
            heightSize=mHeight;
        } else {
            heightSize=heightSize>maxHeight?maxHeight:heightSize;
        }

        /*
        * 在高度小於10dp時,強制不能使用文字顯示進度,因為高度實在是太小了,在這個高度下字型看不清楚,放在進度條外又不美觀,只好折中設計了。
        * */
        if(heightSize<DisplayUtil.dip2px(getContext(),10)){
            showPercent=false;
        }

        /*
        * 設定寬度
        * */
        if(mWidth>0){
            widthSize=mWidth;
        }
        setMeasuredDimension(widthSize,heightSize); //確定主檢視寬高

    }

    boolean once;
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

        if(!once) {
            progressPaint.setColor(progressColor);
            progressPaint2.setColor(progressColor2);

            progressPaint.setAntiAlias(true);
            progressPaint2.setAntiAlias(true);

            progressView = new TextView(getContext());
            progressView.setWidth(getMeasuredWidth());
            progressView.setHeight(getMeasuredHeight());
            progressView.setBackgroundColor(secondProgressColor);

            backgroundMaskView= new TextView(getContext());
            backgroundMaskView.setWidth(getMeasuredWidth());
            backgroundMaskView.setHeight(getMeasuredHeight());
            backgroundMaskView.setBackgroundResource(R.drawable.background);

            switch (style) {
                case STYLE_COLORFUL:
                    colofulView = new ColorfulView(getContext(), getMeasuredWidth(), progressPaint, progressPaint2);
                    break;
                case STYLE_NORMAL:
                    colofulView = new ColorfulView(getContext(), getMeasuredWidth(), progressPaint, progressPaint);
                    break;
            }

            percentView = new TextView(getContext());
            percentView.setText((int)((float)partition2*100)+"%");
            percentView.setTextSize(DisplayUtil.px2sp(getContext(), (float) (getMeasuredHeight()*0.8)));
            percentView.setGravity(Gravity.CENTER);
            percentView.setShadowLayer(2,1,2,percentShaderColor);
            percentView.setTextColor(percentColor);
            percentView.measure(0,0);
            int textWidth = percentView.getMeasuredHeight()*2;
            int textHeight = percentView.getMeasuredHeight();

            maskView = new TextView(getContext());
            maskView.setWidth(getMeasuredWidth());
            maskView.setHeight(getMeasuredHeight() * 2 / 3);
            maskView.setBackgroundResource(R.drawable.progress_mask);

             /*
             * 依次新增第二進度條,背景罩,雙色進度條(第一進度條),白色漸變層,百分比文字顯示層等四個子View
             * */
            addView(progressView);
            addView(backgroundMaskView);
            addView(colofulView);
            addView(maskView);
            addView(percentView);

            getChildAt(0).layout(0, 0, getMeasuredWidth(), getMeasuredHeight()); //佈局第二進度條位置
            getChildAt(1).layout(0, 0, getMeasuredWidth(), getMeasuredHeight()); //佈局背景罩

            int ChildHeight = getMeasuredWidth();
            getChildAt(2).layout(0, -ChildHeight + getMeasuredHeight(), getMeasuredWidth(), getMeasuredWidth()); //佈局雙色進度條
             /*
             * 根據標識位,為雙色進度條設定位移動畫(無限上下移動,視覺上達到斜條移動的效果)
             * */
            if (animationOn) {
                translateAnimation = new TranslateAnimation(0, 0, 0, ChildHeight - getMeasuredHeight());
                translateAnimation.setDuration((long) (8000 * (float) getMeasuredWidth() / DisplayUtil.getScreenWidthPx(getContext())));
                 translateAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
                translateAnimation.setRepeatCount(-1);
                translateAnimation.setRepeatMode(Animation.REVERSE);
                getChildAt(2).setAnimation(translateAnimation);
                translateAnimation.start();
            }

            getChildAt(3).layout(0, 0, getMeasuredWidth(), getMeasuredHeight() * 2 / 3); //佈局白色漸變層

            getChildAt(4).layout(0, 0, textWidth,textHeight); //佈局百分比文字顯示層
             /*
             * 根據標誌位,確定是否顯示百分比文字顯示層。
             * */
            if(showPercent){
                getChildAt(4).setVisibility(VISIBLE);
            }else {
                getChildAt(4).setVisibility(GONE);
            }

             /*
             *  設定預設背景圖,你當然也可以使用純色的資源。這裡我用了一個黑色透明漸變的背景,呈現一個由陰影效果的凹槽
             * */
            setBackgroundResource(R.drawable.background);
            once=true;
        }
    }


    public void showPercentText(boolean showPercent){
        this.showPercent=showPercent;
        postInvalidate();
    }

    public int getSecondProgressColor() {
        return secondProgressColor;
    }

    public void setSecondProgressColor(int secondProgressColor) {
        this.secondProgressColor = secondProgressColor;
        postInvalidate();
    }
    public void setSecondProgressColorRes(int secondProgressColorRes) {
        this.secondProgressColor =  getResources().getColor(secondProgressColorRes);
        postInvalidate();
    }

    public int getPercentColor() {
        return percentColor;
    }

    public void setPercentColor(int percentColor) {
        this.percentColor = percentColor;
    }

    public void setPercentColorRes(int percentColorRes) {
        this.percentColor = getResources().getColor(percentColorRes);
    }

    public int getPercentShadeColor() {
        return percentShaderColor;
    }

    public void setPercentShaderColor(int percentShadeColor) {
        this.percentShaderColor = percentShadeColor;
    }
    public void setPercentShaderColorRes(int percentShaderColorRes) {
        this.percentShaderColor = getResources().getColor(percentShaderColorRes);
    }

    public String getStyle() {
        return style;
    }

    public void setStyle(String style) {
        this.style = style;
    }

    public int getProgressColor() {
        return progressColor;
    }

    public void setProgressColor(int progressColor) {
        this.progressColor = progressColor;
    }
    public void setProgressColorRes(int progressColorRes) {
        this.progressColor = getResources().getColor(progressColorRes);
    }

    public int getProgressColor2() {
        return progressColor2;
    }

    public void setProgressColor2(int progressColor2) {
        this.progressColor2 = progressColor2;
    }
    public void setProgressColor2Res(int progressColor2Res) {
        this.progressColor2 = getResources().getColor(progressColor2Res);
    }

    public void setAnimation(boolean animationOn){
        this.animationOn=animationOn;
    }

    public long getSecondProgress() {
        return secondProgress;
    }
    private float partition;
    public void setSecondProgress(long secondProgress) {
        this.secondProgress = secondProgress;
        partition= (float)this.secondProgress/maxProgress;
        postInvalidate();
    }

    public int getBackgroundColor() {
        return backgroundColor;
    }

    public void setBackgroundColor(int backgroundColor) {
        this.backgroundColor = backgroundColor;
        setBackgroudColor=true;
        postInvalidate();
    }

    public void setBackgroundColorRes(int backgroundColorRes) {
        this.backgroundColor = getResources().getColor(backgroundColorRes);
        setBackgroudColor=true;
        postInvalidate();
    }

    public void setHeight(int height){
        mHeight=height;
    }
    public void setWidth(int width){
        mWidth=width;
    }

    public void setMaxProgress(long progress){
        maxProgress=progress;
    }

    public long getMaxProgress(){
        return maxProgress;
    }

    private float partition2;
    public void setProgress(long progress){
        this.progress=progress;
        partition2= (float)this.progress/maxProgress;
        postInvalidate();
    }

    public long getProgress(){
        return this.progress;
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (getChildAt(0) != null) {
            int moveX = getMeasuredWidth() - (int) (partition * getMeasuredWidth());
            getChildAt(0).setX(-moveX);
        }
        if (getChildAt(2) != null) {
            int moveX = getMeasuredWidth() - (int) (partition2 * getMeasuredWidth());
            getChildAt(2).setX(-moveX);
        }

        if (getChildAt(3) != null) {
            int moveX = getMeasuredWidth() - (int) (partition2 * getMeasuredWidth());
            getChildAt(3).setX(-moveX);
        }
        if (getChildAt(4) != null) {

            if(getChildAt(2).getX()+getMeasuredWidth()>getChildAt(4).getMeasuredHeight()*2) {
                getChildAt(4).setX(getChildAt(2).getX()+getMeasuredWidth()-getChildAt(4).getMeasuredHeight()*2);
            }
            percentView.setText((int) ((float) partition2 * 100) + "%");

                 /*
                 * 根據標誌位,確定是否顯示百分比文字顯示層。
                 * */
            if(showPercent){
                getChildAt(4).setVisibility(VISIBLE);
            }else {
                getChildAt(4).setVisibility(GONE);
            }
        }

        if (!animationOn) {
            if (translateAnimation != null) {
                translateAnimation.cancel();
                animationCancle = true;
            }
        } else {
            if (animationCancle) {
                Log.w("onDraw", "translateAnimation  animationCancle");
                translateAnimation.reset();
                getChildAt(2).setAnimation(translateAnimation);
                translateAnimation.startNow();
                animationCancle = false;
            }
        }

        if(setBackgroudColor) {
            backgroundPaint.setColor(backgroundColor);
            canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), backgroundPaint);
        } else{
            setBackgroundResource(R.drawable.background);
            if(isSetBackgroudColorByXml)
            canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), backgroundPaint);
        }
    }
}

還有一個工具類DisplayUtil就不貼了,主要是負責dp,px與sp的轉換任務。

專案地址

作者的話

挺漂亮的ProgressBar,就是沒啥技術含量。。。。具體需求大家自己隨便研究琢磨