Android自定義View仿支付寶芝麻信用分儀表盤(pán)
先看下iOS的芝麻信用分截圖
這是我做的效果,還是有點(diǎn)差距的
支付寶9.9版本芝麻信用分的實(shí)現(xiàn)
首先初始化各種畫(huà)筆,默認(rèn)的size
,padding
,小圓點(diǎn).
(因?yàn)閷?shí)在找不到原版芝麻信用的帶點(diǎn)模糊效果的小圓點(diǎn),所以只好用這個(gè)代替)
//View的默認(rèn)大小 defaultSize = dp2px(250); //默認(rèn)Padding大小 arcDistance = dp2px(14); //外層圓環(huán)畫(huà)筆 mMiddleArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mMiddleArcPaint.setStrokeWidth(8); mMiddleArcPaint.setColor(Color.WHITE); mMiddleArcPaint.setStyle(Paint.Style.STROKE); mMiddleArcPaint.setAlpha(80); //內(nèi)層圓環(huán)畫(huà)筆 mInnerArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mInnerArcPaint.setStrokeWidth(30); mInnerArcPaint.setColor(Color.WHITE); mInnerArcPaint.setAlpha(80); mInnerArcPaint.setStyle(Paint.Style.STROKE); //正中間字體畫(huà)筆 mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mTextPaint.setColor(Color.WHITE); mTextPaint.setTextAlign(Paint.Align.CENTER); //圓環(huán)大刻度畫(huà)筆 mCalibrationPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mCalibrationPaint.setStrokeWidth(4); mCalibrationPaint.setStyle(Paint.Style.STROKE); mCalibrationPaint.setColor(Color.WHITE); mCalibrationPaint.setAlpha(120); //圓環(huán)小刻度畫(huà)筆 mSmallCalibrationPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mSmallCalibrationPaint.setStrokeWidth(1); mSmallCalibrationPaint.setStyle(Paint.Style.STROKE); mSmallCalibrationPaint.setColor(Color.WHITE); mSmallCalibrationPaint.setAlpha(130); //圓環(huán)刻度文本畫(huà)筆 mCalibrationTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mCalibrationTextPaint.setTextSize(30); mCalibrationTextPaint.setColor(Color.WHITE); //外層進(jìn)度畫(huà)筆 mArcProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mArcProgressPaint.setStrokeWidth(8); mArcProgressPaint.setColor(Color.WHITE); mArcProgressPaint.setStyle(Paint.Style.STROKE); mArcProgressPaint.setStrokeCap(Paint.Cap.ROUND); //外層圓環(huán)上小圓點(diǎn)Bitmap畫(huà)筆 mBitmapPaint = new Paint(); mBitmapPaint.setStyle(Paint.Style.FILL); mBitmapPaint.setAntiAlias(true); //初始化小圓點(diǎn)圖片 bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_circle); //當(dāng)前點(diǎn)的實(shí)際位置 pos = new float[2]; //當(dāng)前點(diǎn)的tangent值 tan = new float[2]; matrix = new Matrix();
代碼很簡(jiǎn)單,就是各種初始化,往下看.
View
的測(cè)量,主要在給設(shè)置warp_content
時(shí)候給定一個(gè)默認(rèn)寬高值.
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ setMeasuredDimension(resolveMeasure(widthMeasureSpec, defaultSize), resolveMeasure(heightMeasureSpec, defaultSize));} //根據(jù)傳入的值進(jìn)行測(cè)量 public int resolveMeasure(int measureSpec, int defaultSize){ int result = 0; int specSize = MeasureSpec.getSize(measureSpec); switch (MeasureSpec.getMode(measureSpec)) { case MeasureSpec.UNSPECIFIED: result = defaultSize; break; case MeasureSpec.AT_MOST: //設(shè)置warp_content時(shí)設(shè)置默認(rèn)值 result = Math.min(specSize, defaultSize); break; case MeasureSpec.EXACTLY: //設(shè)置math_parent 和設(shè)置了固定寬高值 break; default: result = defaultSize; } return result;}
然后確定View
的寬高后的回調(diào)方法.
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh){ super.onSizeChanged(w, h, oldw, oldh); width = w; height = h; radius = width / 2; //外層圓環(huán)矩形 mMiddleRect = new RectF(defaultPadding, defaultPadding,width - defaultPadding, height - defaultPadding); //內(nèi)層圓環(huán)矩形 mInnerRect = new RectF(defaultPadding + arcDistance, defaultPadding + arcDistance,width - defaultPadding - arcDistance, height - defaultPadding - arcDistance); // 外層進(jìn)度矩形 mMiddleProgressRect = new RectF(defaultPadding, defaultPadding,width - defaultPadding, height - defaultPadding); }
這里就是初始化圓弧所需要的矩形實(shí)現(xiàn),下邊開(kāi)始進(jìn)行重點(diǎn),繪制,
繪制外層的圓弧,很簡(jiǎn)單, 圓弧的起始角度,角度.
private void drawMiddleArc(Canvas canvas){ canvas.drawArc(mMiddleRect, mStartAngle, mEndAngle, false, mMiddleArcPaint); }
繪制內(nèi)層圓弧
private void drawInnerArc(Canvas canvas){ canvas.drawArc(mInnerRect, mStartAngle, mEndAngle, false, mInnerArcPaint); }
繪制內(nèi)層圓弧上的小刻度,畫(huà)布旋轉(zhuǎn)到圓弧左下角起點(diǎn),計(jì)算出每條刻度線(xiàn)的起始點(diǎn)后,整個(gè)圓弧是210度,
每6角度繪制一條刻度線(xiàn).
private void drawSmallCalibration(Canvas canvas){ //旋轉(zhuǎn)畫(huà)布 canvas.save(); canvas.rotate(-105, radius, radius); //計(jì)算刻度線(xiàn)的起點(diǎn)結(jié)束點(diǎn) int startDst = (int) (defaultPadding + arcDistance - mInnerArcPaint.getStrokeWidth() / 2 - 1); int endDst = (int) (startDst + mInnerArcPaint.getStrokeWidth()); for (int i = 0; i <= 35; i++) { //每旋轉(zhuǎn)6度繪制一個(gè)小刻度 canvas.drawLine(radius, startDst, radius, endDst, mSmallCalibrationPaint); canvas.rotate(6, radius, radius); } canvas.restore(); }
繪制內(nèi)層圓弧上的大刻度,350, 550, 600,650, 700, 950,對(duì)應(yīng)的信用分值,
一樣旋轉(zhuǎn)畫(huà)布,計(jì)算刻度線(xiàn)的起始點(diǎn),計(jì)算出每次旋轉(zhuǎn)的角度,每35度旋轉(zhuǎn)一次,依次繪制對(duì)應(yīng)的大刻度線(xiàn),
然后繪制對(duì)應(yīng)的文本內(nèi)容,使用paint
的measureText
方法測(cè)量出文本的長(zhǎng)度,依次繪制對(duì)應(yīng)的文本內(nèi)容.
private void drawCalibrationAndText(Canvas canvas){ //旋轉(zhuǎn)畫(huà)布進(jìn)行繪制對(duì)應(yīng)的刻度 canvas.save(); canvas.rotate(-105, radius, radius); //計(jì)算刻度線(xiàn)的起點(diǎn)結(jié)束點(diǎn) int startDst = (int) (defaultPadding + arcDistance - mInnerArcPaint.getStrokeWidth() / 2 - 1); int endDst = (int) (startDst + mInnerArcPaint.getStrokeWidth()); //刻度旋轉(zhuǎn)的角度 int rotateAngle = 210 / 10; for (int i = 1; i < 12; i++) { if (i % 2 != 0) { canvas.drawLine(radius, startDst, radius, endDst, mCalibrationPaint); } // 測(cè)量文本的長(zhǎng)度 float textLen = mCalibrationTextPaint.measureText(sesameStr[i - 1]); canvas.drawText(sesameStr[i - 1], radius - textLen / 2, endDst + 40, mCalibrationTextPaint); canvas.rotate(rotateAngle, radius, radius); } canvas.restore();}
繪制中間的信用分值,信用等級(jí),評(píng)估時(shí)間等文本,這個(gè)比較簡(jiǎn)單,直接drawText
,依次高低排列繪制即可.
private void drawCenterText(Canvas canvas){ //繪制Logo mTextPaint.setTextSize(30); canvas.drawText("BETA", radius, radius - 130, mTextPaint); //繪制信用分?jǐn)?shù) mTextPaint.setTextSize(200); mTextPaint.setStyle(Paint.Style.STROKE); canvas.drawText(String.valueOf(mMinNum), radius, radius + 70, mTextPaint); //繪制信用級(jí)別 mTextPaint.setTextSize(80); canvas.drawText(sesameLevel, radius, radius + 160, mTextPaint); //繪制評(píng)估時(shí)間 mTextPaint.setTextSize(30); canvas.drawText(evaluationTime, radius, radius + 205, mTextPaint); }
繪制最外層的進(jìn)度,這里使用的Path
添加要繪制的圓弧,因?yàn)樾枰ゲ粩嗟挠?jì)算坐標(biāo)點(diǎn),主要用到了PathMeasure
這個(gè)類(lèi),將繪制的圓弧加入到path
中,
當(dāng)前點(diǎn)的實(shí)際位置
private float[] pos;
當(dāng)前的tangent值
private float[] tan;
獲取路徑的終點(diǎn)的正切值和坐標(biāo),然后根據(jù)坐標(biāo)點(diǎn)繪制小圓點(diǎn)
PathMeasure pathMeasure = new PathMeasure(path, false); pathMeasure.getPosTan(pathMeasure.getLength() * 1, pos, tan);
private void drawRingProgress(Canvas canvas){ Path path = new Path(); path.addArc(mMiddleProgressRect, mStartAngle, mCurrentAngle); PathMeasure pathMeasure = new PathMeasure(path, false); pathMeasure.getPosTan(pathMeasure.getLength() * 1, pos, tan); matrix.reset(); matrix.postTranslate(pos[0] - bitmap.getWidth() / 2, pos[1] - bitmap.getHeight() / 2); canvas.drawPath(path, mArcProgressPaint); //起始角度不為0時(shí)候才進(jìn)行繪制小圓點(diǎn) if (mCurrentAngle == 0) return; canvas.drawBitmap(bitmap, matrix, mBitmapPaint); mBitmapPaint.setColor(Color.WHITE); canvas.drawCircle(pos[0], pos[1], 8, mBitmapPaint); }
好了,到這里所有繪制完畢了,接下來(lái)讓圓弧進(jìn)度條動(dòng)起來(lái)吧,使用ValueAnimator
,進(jìn)度條動(dòng)畫(huà)定義了圓弧進(jìn)度條的開(kāi)始角度mCurrentAngle
,圓弧角度mTotalAngle
,數(shù)值動(dòng)畫(huà)定義了初始化minNum=0
,maxNum
根據(jù)傳入的數(shù)值進(jìn)行計(jì)算.
public void startAnim(){ ValueAnimator mAngleAnim = ValueAnimator.ofFloat(mCurrentAngle, mTotalAngle); mAngleAnim.setInterpolator(new AccelerateDecelerateInterpolator()); mAngleAnim.setDuration(3000); mAngleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){ @Override public void onAnimationUpdate(ValueAnimator valueAnimator){ mCurrentAngle = (float) valueAnimator.getAnimatedValue(); postInvalidate(); } }); mAngleAnim.start(); ValueAnimator mNumAnim = ValueAnimator.ofInt(mMinNum, mMaxNum); mNumAnim.setDuration(3000); mNumAnim.setInterpolator(new LinearInterpolator()); mNumAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator){ mMinNum = (int) valueAnimator.getAnimatedValue(); postInvalidate(); } }); mNumAnim.start();}
最后根據(jù)傳入的信用分值計(jì)算圓弧進(jìn)度條所到的角度.
public void setSesameValues(int values){ if (values <= 350){ mMaxNum = values; mTotalAngle = 0f; sesameLevel = "信用較差"; evaluationTime = "評(píng)估時(shí)間:" + getCurrentTime(); } else if (values <= 550){ mMaxNum = values; mTotalAngle = (values - 350) * 80 / 400f + 2; sesameLevel = "信用較差"; evaluationTime = "評(píng)估時(shí)間:" + getCurrentTime(); } else if (values <= 700) { mMaxNum = values; if (values > 550 && values <= 600){ sesameLevel = "信用中等"; } else if (values > 600 && values <= 650){ sesameLevel = "信用良好"; } else { sesameLevel = "信用優(yōu)秀"; } mTotalAngle = (values - 550) * 120 / 150f + 43; evaluationTime = "評(píng)估時(shí)間:" + getCurrentTime(); } else if (values <= 950){ mMaxNum = values; mTotalAngle = (values - 700) * 40 / 250f + 170; sesameLevel = "信用極好"; evaluationTime = "評(píng)估時(shí)間:" + getCurrentTime(); } else{ mTotalAngle = 240f; } startAnim(); }
總結(jié)
這篇文章只分析了新版的實(shí)現(xiàn)過(guò)程,舊版的的實(shí)現(xiàn)思路也差不多,代碼也不復(fù)雜。希望這篇文章對(duì)大家開(kāi)發(fā)Android能有所幫助,如果有疑問(wèn)可以留言交流。
相關(guān)文章
AndroidStudio 3.6 中 R.layout 找不到對(duì)應(yīng)的xml文件問(wèn)題及解決方法
這篇文章主要介紹了AndroidStudio 3.6 中 R.layout 找不到對(duì)應(yīng)的xml文件問(wèn)題,本文給出了解決方法對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03Android設(shè)備藍(lán)牙連接掃描槍獲取掃描內(nèi)容
這篇文章主要為大家詳細(xì)介紹了Android設(shè)備藍(lán)牙連接掃描槍獲取掃描內(nèi)容,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09Android 啟動(dòng) Service(startservice和bindservice) 兩種方式的區(qū)別
andrid service 兩種啟動(dòng)方式:第一種startservice,第二種bindservice,大家對(duì)這兩種啟動(dòng)方式的區(qū)別了解嗎,下面跟著小編一起學(xué)習(xí)吧2015-11-11Android中實(shí)現(xiàn)多行、水平滾動(dòng)的分頁(yè)的Gridview實(shí)例源碼
如果單行水平滾動(dòng),可以用Horizontalscrollview實(shí)現(xiàn)。如果是多行水平滾動(dòng),則結(jié)合Gridview(一般是垂直滾動(dòng)的)和Horizontalscrollview實(shí)現(xiàn)2013-06-06Flutter中數(shù)據(jù)存儲(chǔ)的四種方式小結(jié)
在 Flutter 中,存儲(chǔ)是指用于本地和遠(yuǎn)程存儲(chǔ)和管理數(shù)據(jù)的機(jī)制,本給大家介紹了Flutter中不同存儲(chǔ)選項(xiàng)的概述和示例,通過(guò)代碼示例講解的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下2023-11-11實(shí)例講解Android中的AutoCompleteTextView自動(dòng)補(bǔ)全組件
AutoCompleteTextView組件被用在輸入框中能實(shí)現(xiàn)輸入內(nèi)容自動(dòng)補(bǔ)全的功能,類(lèi)似于大家平時(shí)用Google時(shí)的輸入聯(lián)想,這里我們來(lái)用實(shí)例講解Android中的AutoCompleteTextView自動(dòng)補(bǔ)全組件,特別是實(shí)現(xiàn)郵箱地址補(bǔ)全的例子,非常實(shí)用2016-05-05解決Android Studio xml 格式化不自動(dòng)換行的問(wèn)題
這篇文章主要介紹了解決Android Studio xml 格式化不自動(dòng)換行的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03Android開(kāi)發(fā)筆記 改變字體顏色的三種方法
在TextView中添加文本時(shí)有時(shí)需要改變一些文本字體的顏色,今天學(xué)到了三種方法,拿出來(lái)分享一下2012-11-11