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è)矩形的寬度需要我們用繪制最長的文字來確定其寬高



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


private void drawShowProgressRoundRect(Canvas canvas) {
        float stop = mPathMeasure.getLength() * mProgress;//計(jì)算進(jìn)度條的進(jìn)度
        //根據(jù)要繪制的文字的最大長寬來計(jì)算要繪制的圓角矩形的長寬
        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)度長度
                    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)系作者
  • 序言:七十年代末微驶,一起剝皮案震驚了整個(gè)濱河市浪谴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌因苹,老刑警劉巖苟耻,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異容燕,居然都是意外死亡梁呈,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門蘸秘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來官卡,“玉大人蝗茁,你說我怎么就攤上這事⊙爸洌” “怎么了哮翘?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長毛秘。 經(jīng)常有香客問我饭寺,道長,這世上最難降的妖魔是什么叫挟? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任艰匙,我火速辦了婚禮,結(jié)果婚禮上抹恳,老公的妹妹穿的比我還像新娘员凝。我一直安慰自己,他們只是感情好奋献,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布健霹。 她就那樣靜靜地躺著,像睡著了一般瓶蚂。 火紅的嫁衣襯著肌膚如雪糖埋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天窃这,我揣著相機(jī)與錄音瞳别,去河邊找鬼。 笑死钦听,一個(gè)胖子當(dāng)著我的面吹牛洒试,可吹牛的內(nèi)容都是我干的倍奢。 我是一名探鬼主播朴上,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼卒煞!你這毒婦竟也來了痪宰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤畔裕,失蹤者是張志新(化名)和其女友劉穎衣撬,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扮饶,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡具练,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了甜无。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扛点。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡哥遮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出陵究,到底是詐尸還是另有隱情眠饮,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布铜邮,位于F島的核電站仪召,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏松蒜。R本人自食惡果不足惜扔茅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望秸苗。 院中可真熱鬧咖摹,春花似錦、人聲如沸难述。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽胁后。三九已至店读,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間攀芯,已是汗流浹背屯断。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留侣诺,地道東北人殖演。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像年鸳,于是被迫代替她去往敵國和親趴久。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容