項(xiàng)目中的自定義控件比較多檀咙,今天應(yīng)該是最后一個(gè)了,看下UI效果
UI分析
- 內(nèi)側(cè)一個(gè)白色的虛線弧
- 外側(cè)有兩條弧璃诀,一個(gè)是灰色的實(shí)線弧弧可,一個(gè)是白色的虛線弧,實(shí)線弧尾部有一個(gè)小圓點(diǎn)
- 中間是文字劣欢,大小不一致棕诵,顏色是白色
- 加載的時(shí)候需要一個(gè)加載動(dòng)畫(huà),實(shí)線橢圓進(jìn)度條跟可用額度數(shù)字需要同時(shí)從小到達(dá)變化
- 效果圖
下面根據(jù)UI的分析凿将,來(lái)分享一下實(shí)現(xiàn)的過(guò)程
自定義屬性
- 文字的大小
- 線的顏色
- 虛線的間距
代碼
//定義屬性
<declare-styleable name="CircleIndicatorView">
<attr name="largeSize" format="dimension"/>
<attr name="smallSize" format="dimension"/>
<attr name="grayColor" format="color"/>
<attr name="whiteColor" format="color"/>
<attr name="lineSpace" format="dimension"/>
</declare-styleable>
//獲取屬性
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CircleIndicatorView);
mLargeSize = (int) ta.getDimension(R.styleable.CircleIndicatorView_largeSize, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 15, context.getResources().getDisplayMetrics()));
mSmallSize = (int) ta.getDimension(R.styleable.CircleIndicatorView_smallSize, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 13, context.getResources().getDisplayMetrics()));
mGrayColor = ta.getColor(R.styleable.CircleIndicatorView_grayColor, Color.GRAY);
mWhiteColor = ta.getColor(R.styleable.CircleIndicatorView_whiteColor, Color.WHITE);
ta.recycle();
onMeasure
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
}
private int measureWidth(int widthMeasureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
switch (specMode) {
case MeasureSpec.EXACTLY:
result = specSize;
break;
case MeasureSpec.UNSPECIFIED:
case MeasureSpec.AT_MOST:
break;
}
return result;
}
這里其實(shí)不需要考慮MeasureSpec.AT_MOST校套,因?yàn)檫@個(gè)圓的半徑是一般是固定的,所以沒(méi)有處理MeasureSpec.AT_MOST牧抵,如果需求需要處理的話其實(shí)也很簡(jiǎn)單笛匙,跟自定義View之IndexView進(jìn)度條(一)中的一樣,把相應(yīng)的寬高進(jìn)行累加即可犀变,不是分析的重點(diǎn)膳算,所以一筆帶過(guò)。
onDraw
- 繪制內(nèi)側(cè)白色虛線弧度
//不加的話在高版本上虛線不顯示
setLayerType(View.LAYER_TYPE_SOFTWARE,dashPaint);
//設(shè)置虛線間隔
PathEffect effects = new DashPathEffect(new float[]{20, 6}, 0);
dashPaint.setPathEffect(effects);
RectF dashedRectF = new RectF(mCenter - mRadius + 20 + getPaddingLeft(), mCenter - mRadius + 20 + getPaddingTop(), mCenter + mRadius - 20 - getPaddingRight(), mCenter + mRadius - 20 - getPaddingBottom());
float startAngle = 150;
canvas.drawArc(dashedRectF, startAngle, sweepAngle, false, dashPaint);
- 繪制外側(cè)灰色弧度
canvas.drawArc(rectF, startAngle, sweepAngle, false, outPaint);
- 繪制外側(cè)進(jìn)度弧度
canvas.drawArc(rectF, startAngle, getInSweepAngle(), false, inPaint);
4.繪制外側(cè)進(jìn)度弧度的小圓點(diǎn)弛作,實(shí)際上是一個(gè)Bitmap,注釋比較詳細(xì)涕蜂,也比較簡(jiǎn)單,就是畫(huà)布的平移跟旋轉(zhuǎn)這個(gè)需要好好理解一下
//繪制發(fā)光的小圓點(diǎn)
Paint paintCircle = new Paint();
paintCircle.setStyle(Paint.Style.FILL);
paintCircle.setAntiAlias(true);//抗鋸齒功能
canvas.translate(getWidth() / 2, getHeight() / 2);//畫(huà)布平移到圓心
canvas.rotate(getInSweepAngle() + 60);//旋轉(zhuǎn)畫(huà)布使得畫(huà)布的Y軸經(jīng)過(guò)小圓點(diǎn)
canvas.translate(0, getHeight() / 2 - getPaddingLeft());//再次平移畫(huà)布至小圓點(diǎn)繪制的位置
//此處省略了Bitmap的處理過(guò)程bmp
Bitmap dotBitmap = Bitmap.createBitmap(bmp, 0, 0, width, height, matrix, true);
canvas.drawBitmap(dotBitmap, -15, -15, paintCircle);
canvas.rotate(-(getInSweepAngle() + 60));//恢復(fù)canvas
5.繪制文字
這個(gè)在繪制數(shù)字的時(shí)候需要注意一下映琳,因?yàn)閭鬟^(guò)來(lái)的是一個(gè)浮點(diǎn)數(shù)机隙,需要拆分成整數(shù)跟小數(shù)兩部分,但是當(dāng)你講float轉(zhuǎn)化為String然后切割的時(shí)候萨西,注意下需要將小數(shù)點(diǎn)進(jìn)行轉(zhuǎn)義有鹿,不然會(huì)分割失敗
DecimalFormat decimalFormat = new DecimalFormat(".00");//構(gòu)造方法的字符格式這里如果小數(shù)不足2位,會(huì)以0補(bǔ)足.
String value = decimalFormat.format(indexValue);//format 返回的是字符串
Log.d("value---->", value);
String[] split = value.split("\\.");
String text = split[0];//整數(shù)部分
剛進(jìn)來(lái)時(shí)候的加載動(dòng)畫(huà)
這個(gè)其實(shí)就是兩個(gè)屬性動(dòng)畫(huà)同時(shí)播放而已,通過(guò)計(jì)算扇形掃過(guò)的角度與數(shù)字增長(zhǎng)的幅度
谎脯,然后在屬性動(dòng)畫(huà)的update里面進(jìn)行重新繪制整個(gè)View葱跋,就可以搞定
float inSweepAngle = sweepAngle * value / 100;
ValueAnimator angleAnim = ValueAnimator.ofFloat(0f, inSweepAngle);//角度的ValueAnimator
float inValue = value * 8888 / 100;
ValueAnimator valueAnim = ValueAnimator.ofFloat(0, inValue);//數(shù)字變化的ValueAnimator
//一起播放動(dòng)畫(huà)
animatorSet.playTogether(angleAnim, valueAnim);
提供一個(gè)方法,供外部調(diào)用
public void goToPoint(float value) {
//在方法中進(jìn)行播放動(dòng)畫(huà)
}
使用方法
mCircleIndicatorView.goToPoint(value);