目錄
效果展示
實(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);
}
}