android酷炫下載進(jìn)度條

GIF.gif

拿到項(xiàng)目需求時(shí),不要立馬就去寫代碼,先想下實(shí)現(xiàn)的思路,大致涉及到哪些技術(shù)點(diǎn),哪種實(shí)現(xiàn)方式比較好,就拿上面的效果說,系統(tǒng)的ProgressBar肯定實(shí)現(xiàn)不了該效果,那就要自定義view,自定義view大致包含以下幾步:
1、自定義屬性的聲明與獲取
2、測(cè)量onMeasure
3、布局onLayout(ViewGroup布局容器)
4、繪制onDraw
5、onTouchEvent(涉及用戶手勢(shì)交互)
6、onInterceptTouchEvent(處理父view和子view之間的事件攔截)
7、狀態(tài)的恢復(fù)與保存


TIM截圖20190120142614.png

對(duì)于該效果:
橫向進(jìn)度條包含:左邊一個(gè)進(jìn)度、中間一個(gè)文字、右邊一個(gè)進(jìn)度
圓形進(jìn)度條:最外層一個(gè)圓、加載進(jìn)度的圓弧、顯示進(jìn)度的中間文字
并沒有涉及到view擺放、用戶交互行為,大致就只需要第一、第二、第四、第七步;實(shí)現(xiàn)的流程大致確定后,考慮是繼承自view還是ProgressBar,上面的效果只是對(duì)系統(tǒng)ProgressBar的效果進(jìn)行改動(dòng),繼承自ProgressBar就可以了,這樣可以使用系統(tǒng)ProgressBar的一些屬性和方法,通過getProgress()和setProgress()方法就可以獲取和設(shè)置加載進(jìn)度,不用像繼承自view,還有給view添加屬性動(dòng)畫來實(shí)現(xiàn)加載進(jìn)度,考慮清楚了那就開始擼代碼吧。

按照流程第一步自定義屬性的定義和獲?。?/p>

<declare-styleable name="ProgressBarView">
        <attr name="progress_unreach_color" format="color"></attr>
        <attr name="progress_height" format="dimension"></attr>
        <attr name="progress_reach_color" format="color"></attr>
        <attr name="progress_text_color" format="color"></attr>
        <attr name="progress_text_size" format="dimension"></attr>
        <attr name="progress_text_offset" format="dimension"></attr>
    </declare-styleable>
    <declare-styleable name="RoundProgressBar">
        <attr name="radius" format="dimension"></attr>
    </declare-styleable>
/**
     * 獲取自定義屬性
     *
     * @param attrs
     */
    private void obtainStyledAttrs(AttributeSet attrs) {
        TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressBarView);
        mTextSize = ta.getDimensionPixelSize(R.styleable.ProgressBarView_progress_text_size, sp2px(mTextSize));
        mTextColor = ta.getColor(R.styleable.ProgressBarView_progress_text_color, mTextColor);

        mUnReachColor = ta.getColor(R.styleable.ProgressBarView_progress_unreach_color, mUnReachColor);
        mProgressHeight = (int) ta.getDimension(R.styleable.ProgressBarView_progress_height, dp2px(mProgressHeight));

        mReachColor = ta.getColor(R.styleable.ProgressBarView_progress_reach_color, mReachColor);

        mTextOffset = (int) ta.getDimension(R.styleable.ProgressBarView_progress_text_offset, dp2px(mTextOffset));
        ta.recycle();
    }

第一步弄好了,就是第二步測(cè)量,測(cè)量的時(shí)候需要注意控件的寬高模式,根據(jù)設(shè)置的寬高模式來測(cè)量和指定控件的寬高;

@Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthVal = MeasureSpec.getSize(widthMeasureSpec);

        int height = measureHeight(heightMeasureSpec);
        setMeasuredDimension(widthVal, height);
        //計(jì)算progressbar真正寬度=控件的寬度-paddingleft-paddingright
        mRealWith = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
    }
private int measureHeight(int heightMeasureSpec) {
        int result = 0;
        //獲取高度模式
        int mode = MeasureSpec.getMode(heightMeasureSpec);
        //獲取寬度模式
        int size = MeasureSpec.getSize(heightMeasureSpec);
        if (mode == MeasureSpec.EXACTLY) {
            //精準(zhǔn)模式 用戶設(shè)置為 比如80dp  match_parent fill_parent
            result = size;
        } else {
            //計(jì)算中間文字的高度
            int textHeight = (int) (mPaint.descent() - mPaint.ascent());
            //paddingTop+paddingBottom+ progressbar高度和文字高度的最大值
            result = getPaddingTop() + getPaddingBottom() + Math.max(mProgressHeight, Math.abs(textHeight));
            if (mode == MeasureSpec.AT_MOST) {
                result = Math.min(result, size);
            }
        }
        return result;
    }

測(cè)量完成后,就可以進(jìn)行onDraw繪制了,因?yàn)橐淖兿到y(tǒng)ProgressBar效果就不需要super.onDraw(canvas);

@Override
    protected synchronized void onDraw(Canvas canvas) {
        //保存畫布
        canvas.save();
        //移動(dòng)畫布
        canvas.translate(getPaddingLeft(), getHeight() / 2);
        //定義變量用來控制是否要繪制右邊progressbar  如果寬度不夠的時(shí)候就不進(jìn)行繪制
        boolean noNeedUnRech = false;
        //計(jì)算左邊進(jìn)度在整個(gè)控件寬度的占比
        float radio = getProgress() * 1.0f / getMax();
        //獲取左邊進(jìn)度的寬度
        float progressX = radio * mRealWith;
        //中間文字
        String text = getProgress() + "%";
        //獲取文字的寬度
        int textWidth = (int) mPaint.measureText(text);
        if (progressX + textWidth > mRealWith) {
            //左邊進(jìn)度+文字的寬度超過progressbar的寬度 重新計(jì)算左邊進(jìn)度的寬度 這個(gè)時(shí)候也就意味著不需要繪制右邊進(jìn)度
            progressX = mRealWith - textWidth;
            noNeedUnRech = true;
        }
        //計(jì)算左邊進(jìn)度結(jié)束的位置 如果結(jié)束的位置小于0就不需要繪制左邊的進(jìn)度
        float endX = progressX - mTextOffset / 2;
        if (endX > 0) {
            //繪制左邊進(jìn)度
            mPaint.setColor(mReachColor);
            mPaint.setStrokeWidth(mProgressHeight);
            canvas.drawLine(0, 0, endX, 0, mPaint);
        }
        mPaint.setColor(mTextColor);
        if (getProgress() != 0) {
            //計(jì)算文字基線
            int y = (int) (-(mPaint.descent() + mPaint.ascent()) / 2);
            //繪制文字
            canvas.drawText(text, progressX, y, mPaint);
        }
        if (!noNeedUnRech) {
            //右邊進(jìn)度的開始位置=左邊進(jìn)度+文字間距的一半+文字寬度
            float start;
            if (getProgress() == 0) {
                start = progressX;
            } else {
                start = progressX + mTextOffset / 2 + textWidth;
            }
            mPaint.setColor(mUnReachColor);
            mPaint.setStrokeWidth(mProgressHeight);
            //繪制右邊進(jìn)度
            canvas.drawLine(start, 0, mRealWith, 0, mPaint);
        }
        //重置畫布
        canvas.restore();
    }

完整代碼:

/**
 * 長(zhǎng)方形進(jìn)度條
 */
public class ProgressBarView extends ProgressBar {
    //字體大小
    protected int mTextSize = 12;
    //字體顏色
    protected int mTextColor = Color.BLACK;
    //沒有到達(dá)(右邊progressbar的顏色)
    protected int mUnReachColor = Color.GREEN;
    //progressbar的高度
    protected int mProgressHeight = 6;
    //progressbar進(jìn)度的顏色
    protected int mReachColor = mTextColor;
    //字體間距
    protected int mTextOffset = 10;
    protected Paint mPaint;
    //progressbar真正的寬度
    protected int mRealWith;

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

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

    public ProgressBarView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //獲取自定義屬性
        obtainStyledAttrs(attrs);

        mPaint = new Paint();
        mPaint.setTextSize(mTextSize);
    }

    /**
     * 獲取自定義屬性
     *
     * @param attrs
     */
    private void obtainStyledAttrs(AttributeSet attrs) {
        TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressBarView);
        mTextSize = ta.getDimensionPixelSize(R.styleable.ProgressBarView_progress_text_size, sp2px(mTextSize));
        mTextColor = ta.getColor(R.styleable.ProgressBarView_progress_text_color, mTextColor);

        mUnReachColor = ta.getColor(R.styleable.ProgressBarView_progress_unreach_color, mUnReachColor);
        mProgressHeight = (int) ta.getDimension(R.styleable.ProgressBarView_progress_height, dp2px(mProgressHeight));

        mReachColor = ta.getColor(R.styleable.ProgressBarView_progress_reach_color, mReachColor);

        mTextOffset = (int) ta.getDimension(R.styleable.ProgressBarView_progress_text_offset, dp2px(mTextOffset));
        ta.recycle();
    }

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthVal = MeasureSpec.getSize(widthMeasureSpec);

        int height = measureHeight(heightMeasureSpec);
        setMeasuredDimension(widthVal, height);
        //計(jì)算progressbar真正寬度=控件的寬度-paddingleft-paddingright
        mRealWith = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
    }

    @Override
    protected synchronized void onDraw(Canvas canvas) {
        //保存畫布
        canvas.save();
        //移動(dòng)畫布
        canvas.translate(getPaddingLeft(), getHeight() / 2);
        //定義變量用來控制是否要繪制右邊progressbar  如果寬度不夠的時(shí)候就不進(jìn)行繪制
        boolean noNeedUnRech = false;
        //計(jì)算左邊進(jìn)度在整個(gè)控件寬度的占比
        float radio = getProgress() * 1.0f / getMax();
        //獲取左邊進(jìn)度的寬度
        float progressX = radio * mRealWith;
        //中間文字
        String text = getProgress() + "%";
        //獲取文字的寬度
        int textWidth = (int) mPaint.measureText(text);
        if (progressX + textWidth > mRealWith) {
            //左邊進(jìn)度+文字的寬度超過progressbar的寬度 重新計(jì)算左邊進(jìn)度的寬度 這個(gè)時(shí)候也就意味著不需要繪制右邊進(jìn)度
            progressX = mRealWith - textWidth;
            noNeedUnRech = true;
        }
        //計(jì)算左邊進(jìn)度結(jié)束的位置 如果結(jié)束的位置小于0就不需要繪制左邊的進(jìn)度
        float endX = progressX - mTextOffset / 2;
        if (endX > 0) {
            //繪制左邊進(jìn)度
            mPaint.setColor(mReachColor);
            mPaint.setStrokeWidth(mProgressHeight);
            canvas.drawLine(0, 0, endX, 0, mPaint);
        }
        mPaint.setColor(mTextColor);
        if (getProgress() != 0) {
            //計(jì)算文字基線
            int y = (int) (-(mPaint.descent() + mPaint.ascent()) / 2);
            //繪制文字
            canvas.drawText(text, progressX, y, mPaint);
        }
        if (!noNeedUnRech) {
            //右邊進(jìn)度的開始位置=左邊進(jìn)度+文字間距的一半+文字寬度
            float start;
            if (getProgress() == 0) {
                start = progressX;
            } else {
                start = progressX + mTextOffset / 2 + textWidth;
            }
            mPaint.setColor(mUnReachColor);
            mPaint.setStrokeWidth(mProgressHeight);
            //繪制右邊進(jìn)度
            canvas.drawLine(start, 0, mRealWith, 0, mPaint);
        }
        //重置畫布
        canvas.restore();

    }

    private int measureHeight(int heightMeasureSpec) {
        int result = 0;
        //獲取高度模式
        int mode = MeasureSpec.getMode(heightMeasureSpec);
        //獲取寬度模式
        int size = MeasureSpec.getSize(heightMeasureSpec);
        if (mode == MeasureSpec.EXACTLY) {
            //精準(zhǔn)模式 用戶設(shè)置為 比如80dp  match_parent fill_parent
            result = size;
        } else {
            //計(jì)算中間文字的高度
            int textHeight = (int) (mPaint.descent() - mPaint.ascent());
            //paddingTop+paddingBottom+ progressbar高度和文字高度的最大值
            result = getPaddingTop() + getPaddingBottom() + Math.max(mProgressHeight, Math.abs(textHeight));
            if (mode == MeasureSpec.AT_MOST) {
                result = Math.min(result, size);
            }
        }
        return result;
    }

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

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

圓形加載進(jìn)度條效果的話,文字和顏色可以共用橫向進(jìn)度條的,所有就讓圓形進(jìn)度條直接繼承自橫向進(jìn)度條;

/**
 * 原型進(jìn)度條
 */
public class RoundProgressBar extends ProgressBarView {
    //半徑
    private int mRadius = 30;
    private int mMaxPaintWidth;

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

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

    public RoundProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //繪制圓形進(jìn)度條的寬度   這里設(shè)置為長(zhǎng)方形進(jìn)度條高度的1.5倍
        mRealWith = (int) (mProgressHeight * 1.5f);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundProgressBar);
        mRadius = (int) typedArray.getDimension(R.styleable.RoundProgressBar_radius, dp2px(mRadius));
        typedArray.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) {
        mMaxPaintWidth = mProgressHeight;
        //計(jì)算控件的精準(zhǔn)值
        int expect = mRadius * 2 + mMaxPaintWidth + getPaddingLeft() + getPaddingRight();
        int width = resolveSize(expect, widthMeasureSpec);
        int height = resolveSize(expect, heightMeasureSpec);
        int readWidth = Math.min(width, height);
        mRadius = (readWidth - getPaddingRight() - getPaddingLeft() - mMaxPaintWidth) / 2;
        setMeasuredDimension(readWidth, readWidth);
    }

    @Override
    protected synchronized void onDraw(Canvas canvas) {
        //獲取當(dāng)前進(jìn)度
        String text = getProgress() + "%";
        //測(cè)量文字的寬度
        float textWidth = mPaint.measureText(text);
        //文字的高度
        float textHeight = (mPaint.descent() + mPaint.ascent()) / 2;
        //保存畫布
        canvas.save();
        //平移畫布位置
        canvas.translate(getPaddingLeft() + mMaxPaintWidth / 2, getPaddingTop() + mMaxPaintWidth / 2);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(mUnReachColor);
        mPaint.setStrokeWidth(mRealWith);
        //繪制圓 要注意繪制圓的x y 因?yàn)樯厦鎸?duì)畫布進(jìn)行了平移所以這里就不需要計(jì)算了,如果畫布沒有進(jìn)行平移需要計(jì)算 x y
        canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
        //繪制圓弧
        mPaint.setColor(mReachColor);
        mPaint.setStrokeWidth(mReachColor);
        //計(jì)算圓弧的掃過的幅度
        float sweepAngle = getProgress() * 1.0f / getMax() * 360;
        RectF rectF = new RectF(0, 0, mRadius * 2, mRadius * 2);
        canvas.drawArc(rectF, 0, sweepAngle, false, mPaint);

        //繪制中間文字
        mPaint.setColor(mTextColor);
        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawText(text, mRadius - textWidth / 2, mRadius - textHeight, mPaint);
        canvas.restore();
    }
}

自定義的代碼就基本完成了,在調(diào)用的時(shí)候也比較簡(jiǎn)單,不需要像繼承自View那樣給View設(shè)置屬性動(dòng)畫又調(diào)用invalidate進(jìn)行重繪,直接調(diào)用setProgress方法設(shè)置當(dāng)前的進(jìn)度就可以了;

public class MainActivity extends AppCompatActivity {
    private ProgressBarView progressBarView,progressBarView1;
    private RoundProgressBar roundProgressBar,roundProgressBar1;
    private static final int MSG_UPDATA = 0X110;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            int progress = progressBarView.getProgress();
            progressBarView.setProgress(++progress);
            progressBarView1.setProgress(++progress);
            roundProgressBar.setProgress(++progress);
            roundProgressBar1.setProgress(++progress);
            if (progress >= 100) {
                handler.removeMessages(MSG_UPDATA);
            }
            handler.sendEmptyMessageDelayed(MSG_UPDATA, 100);
        }
    };

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

        progressBarView = findViewById(R.id.progress_bar_view);
        roundProgressBar = findViewById(R.id.round_progressbar);
        progressBarView1=findViewById(R.id.progress_bar_view1);
        roundProgressBar1=findViewById(R.id.round_progressbar1);

        handler.sendEmptyMessageDelayed(MSG_UPDATA, 1000);
    }
}

源碼地址

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

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

  • 前言 最近做項(xiàng)目碰到一個(gè)這樣的一個(gè)需求:需要一個(gè)環(huán)形的進(jìn)度條表示一個(gè)下載請(qǐng)求的進(jìn)度加載。同時(shí)要以各種不同的圖標(biāo)展現(xiàn)...
    chengww閱讀 3,409評(píng)論 0 16
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,421評(píng)論 4 61
  • 一只小螞蟻 劃著小船兒 他要去遠(yuǎn)方 找她媳婦兒
    七根閱讀 267評(píng)論 0 3
  • 老王,帶兒子去買菜呢。 啊。是呀,這不是周末嘛,買點(diǎn)肉啥的給咱小樂樂補(bǔ)補(bǔ)。 奧,那挺好的,我先回去了奧!你們慢點(diǎn)走...
    第九夫人閱讀 616評(píng)論 1 4
  • 《中餐廳2》第十期播出王俊凱特色麻辣燙獲贊! 湖南衛(wèi)視青春合伙人經(jīng)營(yíng)體驗(yàn)節(jié)目《中餐廳2》第二季第十期即將播出上線。...
    37餐飲閱讀 246評(píng)論 0 0

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