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

效果展示

實(shí)現(xiàn)步驟
1、計(jì)算出控件寬度的直線路徑
在onSizeChanged方法中進(jìn)行計(jì)算,這時(shí)可以得到一條與控件寬度相同的直線,并把路徑設(shè)置給PathMeasure
@Overrideprotected 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è)置給PathMeasuremPathMeasure.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)的文字來(lái)確定其寬高

另外矩形的顯示位置也是以當(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)寬來(lái)計(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ì)算起來(lái)就比較簡(jiǎn)單了,直接用上一步算出的矩形的中心點(diǎn)即可,不過(guò)這里需要調(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)的偏移來(lái)設(shè)置當(dāng)前的進(jìn)度,具體如下:
@Overridepublic 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è)

而矩形的高度又是文字的大小與邊距之和

@Overrideprotected 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
評(píng)論
圖片
表情
