Android自定義View實(shí)現(xiàn)可拖拽的進(jìn)度條

目錄

效果展示

實(shí)現(xiàn)步驟

1.計(jì)算出控件寬度的直線路徑

在onSizeChanged方法中進(jìn)行計(jì)算,這時(shí)可以得到一條與控件寬度相同的直線,并把路徑設(shè)置給PathMeasure

 @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        //進(jìn)度條繪制在控件中央,寬度為控件寬度(mProgressHeight/2是為了顯示出左右兩邊的圓角)
        mPathProgressBg.moveTo(mProgressHeight / 2,h / 2f);
        mPathProgressBg.lineTo(w - mProgressHeight / 2,h / 2f);
        //將進(jìn)度條路徑設(shè)置給PathMeasure
        mPathMeasure.setPath(mPathProgressBg,false);
        invalidate();
    }
2.計(jì)算當(dāng)前進(jìn)度的路徑

使用PathMeasure得出當(dāng)前進(jìn)度的路徑并進(jìn)行繪制,這里我將上一步的繪制放在了一起

private void drawProgress(Canvas canvas) {
        mPathProgressFg.reset();
        mPaintProgress.setColor(mColorProgressBg);
        //繪制進(jìn)度背景
        canvas.drawPath(mPathProgressBg, mPaintProgress);
        //計(jì)算進(jìn)度條的進(jìn)度
        float stop = mPathMeasure.getLength() * mProgress;
        //得到與進(jìn)度對(duì)應(yīng)的路徑
        mPathMeasure.getSegment(0,stop,mPathProgressFg,true);
        mPaintProgress.setColor(mColorProgressFg);
        //繪制進(jìn)度
        canvas.drawPath(mPathProgressFg, mPaintProgress);
    }
3.計(jì)算顯示進(jìn)度的圓角矩形

這個(gè)矩形的寬度需要我們用繪制最長(zhǎng)的文字來確定其寬高



另外矩形的顯示位置也是以當(dāng)前進(jìn)度所在的點(diǎn)為中心點(diǎn)


private void drawShowProgressRoundRect(Canvas canvas) {
        float stop = mPathMeasure.getLength() * mProgress;//計(jì)算進(jìn)度條的進(jìn)度
        //根據(jù)要繪制的文字的最大長(zhǎng)寬來計(jì)算要繪制的圓角矩形的長(zhǎng)寬
        Rect rect = new Rect();
        mPaintProgressText.getTextBounds(mProgressMaxText,0, mProgressMaxText.length(),rect);
        //要繪制矩形的寬、高
        float rectWidth = rect.width() + (mProgressStrMarginH * 2);
        float rectHeight = rect.height() + (mProgressStrMarginV * 2);
        //計(jì)算邊界值(為了不讓矩形在左右兩邊超出邊界)
        if(stop < rectWidth / 2f){
            stop = rectWidth / 2f;
        }else if(stop > (getWidth() - rectWidth / 2f)){
            stop = getWidth() - rectWidth / 2f;
        }
        //定義繪制的矩形
        float left = stop - rectWidth / 2f;
        float right = stop + rectWidth / 2f;
        float top = getHeight() / 2f - rectHeight / 2f;
        float bottom = getHeight() / 2f + rectHeight / 2f;
        mProgressRoundRectF = new RectF(left,top,right,bottom);
        //繪制為圓角矩形
        canvas.drawRoundRect(mProgressRoundRectF, mRoundRectRadius, mRoundRectRadius,mPaintRoundRect);
    }
4.計(jì)算文字的顯示位置

文字顯示的位置計(jì)算起來就比較簡(jiǎn)單了,直接用上一步算出的矩形的中心點(diǎn)即可,不過這里需要調(diào)整文字繪制的垂直的偏移,這樣才能實(shí)現(xiàn)文字垂直居中

private void drawProgressText(Canvas canvas) {
        String progressText = (int)Math.floor(100 * mProgress) + "%";
        //讓文字垂直居中的偏移
        int offsetY = (mFontMetricsInt.bottom - mFontMetricsInt.ascent) / 2 - mFontMetricsInt.bottom;
        //將文字繪制在矩形的中央
        canvas.drawText(progressText,mProgressRoundRectF.centerX(),mProgressRoundRectF.centerY() + offsetY,mPaintProgressText);
    }
5.實(shí)現(xiàn)拖拽

實(shí)現(xiàn)拖拽需要對(duì)onTouchEvent方法進(jìn)行處理,也就是當(dāng)手指觸摸矩形區(qū)域的時(shí)候,根據(jù)手指橫向滑動(dòng)的偏移來設(shè)置當(dāng)前的進(jìn)度,具體如下

@Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                //判斷手指是否觸摸了顯示進(jìn)度的圓角矩形塊,這樣才可以拖拽
                if(mProgressRoundRectF != null && mProgressRoundRectF.contains(event.getX(),event.getY())){
                    //記錄手指剛接觸屏幕的X軸坐標(biāo)(因?yàn)橹恍枰赬軸上平移)
                    mStartTouchX = event.getX();
                    mIsTouchSeek = true;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if(mIsTouchSeek){
                    //計(jì)算橫向移動(dòng)的距離
                    float moveX = event.getX() - mStartTouchX;
                    //計(jì)算出當(dāng)前進(jìn)度的X軸所顯示的進(jìn)度長(zhǎng)度
                    float currentProgressWidth = mPathMeasure.getLength() * mProgress;//計(jì)算進(jìn)度條的進(jìn)度
                    //計(jì)算滑動(dòng)后的X軸的坐標(biāo)
                    float showProgressWidth = currentProgressWidth + moveX;
                    //計(jì)算邊界值
                    if(showProgressWidth < 0){
                        showProgressWidth = 0;
                    }else if(showProgressWidth > mPathMeasure.getLength()){
                        showProgressWidth = mPathMeasure.getLength();
                    }
                    //計(jì)算滑動(dòng)后的進(jìn)度
                    mProgress = showProgressWidth / mPathMeasure.getLength();
                    //重繪
                    invalidate();
                    //刷新用于計(jì)算移動(dòng)的X軸坐標(biāo)
                    mStartTouchX = event.getX();
                }
                break;
            case MotionEvent.ACTION_UP:
                mIsTouchSeek = false;
                break;
        }
        return mIsTouchSeek;
    }
6.計(jì)算當(dāng)前自定義View的寬高

為了適配高度的wrap_content屬性,我們需要計(jì)算出控件最小需要顯示的高度



這里我們是用顯示進(jìn)度的矩形的高度作為控件最小的高度的,因?yàn)榫匦蔚母叨仁撬袌D形最高的一個(gè)



而矩形的高度又是文字的大小與邊距之和
 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureSizeWidth(widthMeasureSpec), measureSizeHeight(heightMeasureSpec));
    }

    //計(jì)算寬度
    private int measureSizeWidth(int size) {
        int mode = MeasureSpec.getMode(size);
        int s = MeasureSpec.getSize(size);
        if (mode == MeasureSpec.EXACTLY) {
            return s;
        } else{
            return Math.min(s, 200);
        }
    }
    //計(jì)算高度
    private int measureSizeHeight(int size) {
        int mode = MeasureSpec.getMode(size);
        int s = MeasureSpec.getSize(size);
        if (mode == MeasureSpec.EXACTLY) {
            return s;
        }else {
            //自適應(yīng)模式,返回所需的最小高度
            return (int) (mTextSize + mProgressStrMarginV * 2);
        }
    }

案例源碼

https://gitee.com/itfitness/seek-progress-bar

?著作權(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)容

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