Android自定義控件實(shí)現(xiàn)帶數(shù)值和動(dòng)畫(huà)的圓形進(jìn)度條
本文實(shí)例實(shí)現(xiàn)一個(gè)如下圖所示的Android自定義控件,可以直觀地展示某個(gè)球隊(duì)在某個(gè)賽季的積分?jǐn)?shù)和勝場(chǎng)、負(fù)場(chǎng)、平局?jǐn)?shù)
首先對(duì)畫(huà)布進(jìn)行區(qū)域劃分,整個(gè)控件分上下兩部分
上邊是個(gè)大的圓環(huán),圓環(huán)中間兩行文字,沒(méi)什么難度,選好圓心坐標(biāo)和半徑后直接繪制即可,繪制文字也是如此。
下部分是三個(gè)小的圓弧進(jìn)度條,弧的末端繪制一個(gè)小的實(shí)心圓
首先選好坐標(biāo)和半徑,然后先繪制三個(gè)圓環(huán)作為弧形進(jìn)度條的背景
之后從12點(diǎn)鐘開(kāi)始繪制進(jìn)度弧,知道了圓環(huán)的圓心和半徑,也知道了弧對(duì)應(yīng)于12點(diǎn)鐘和圓環(huán)圓心的偏移角度
通過(guò)三角函數(shù)可以計(jì)算出進(jìn)度弧終點(diǎn)坐標(biāo),以進(jìn)度弧終點(diǎn)坐標(biāo)為圓心繪制一個(gè)小的實(shí)心圓即可
動(dòng)畫(huà)效果通過(guò)Handler的postDelayed方法觸發(fā)重繪即可實(shí)現(xiàn)
在項(xiàng)目中的效果如圖所示:
測(cè)試代碼如下:
final Random random=new Random(); final ScoreBoardView myView=(ScoreBoardView)findViewById(R.id.custom_view); myView.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v){ myView.setColor(Color.BLUE); myView.setScore(random.nextInt(28)); myView.setWinDrawLose(random.nextInt(12),random.nextInt(15),random.nextInt(26)); } });
完整代碼如下:
public class ScoreBoardView extends View { private Context context; /*弧的顏色*/ private int mColor; /*積分?jǐn)?shù),勝場(chǎng)數(shù),平局?jǐn)?shù),負(fù)場(chǎng)數(shù)*/ private int mScore, mWinNumber, mDrawNumber, mLoseNumber; /*傳入數(shù)字的最大值*/ private final int FULL_SCORE = 30; /*動(dòng)畫(huà)插值器*/ DecelerateInterpolator mDecelerateInterpolator = new DecelerateInterpolator(); /*動(dòng)畫(huà)持續(xù)時(shí)間(刷新次數(shù))*/ private int mDuration = 10; /*動(dòng)畫(huà)刷新過(guò)程中的當(dāng)前值*/ private int mCurrentTime = 0; private TypedValue typedValue; private TypedValue typedValue1; private Handler mHandler = new Handler(); private Runnable mAnimation = new Runnable() { @Override public void run() { if (mCurrentTime < mDuration) { mCurrentTime++; /*導(dǎo)致重繪調(diào)用onDraw,onDraw最后再次postDelay執(zhí)行此動(dòng)畫(huà),直到達(dá)到指定的次數(shù)*/ ScoreBoardView.this.invalidate(); } } }; /*繪制圖形*/ private Paint paint = new Paint(); /*繪制文字*/ private Paint paintText = new Paint(); public ScoreBoardView(Context context) { super(context); this.context=context; init(); } public ScoreBoardView(Context context, AttributeSet attrs) { super(context, attrs); this.context=context; init(); } public ScoreBoardView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.context=context; init(); } private void init() { /*數(shù)據(jù)初始化,默認(rèn)屬性*/ mColor = Color.rgb(95, 112, 72); mScore = 0; mWinNumber = 0; mDrawNumber = 0; mLoseNumber = 0; typedValue=new TypedValue(); typedValue1=new TypedValue(); context.getTheme().resolveAttribute(R.attr.maintextclor, typedValue, true); context.getTheme().resolveAttribute(R.attr.maintextclor_reverse,typedValue1,true); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); /*獲取控件總的寬高*/ float totalWidth = getWidth(); float totalHeight = getHeight(); /* * DecelerateInterpolator:動(dòng)畫(huà)從開(kāi)始到結(jié)束,變化率是一個(gè)減速的過(guò)程。 * AccelerateInterpolator:動(dòng)畫(huà)從開(kāi)始到結(jié)束,變化率是一個(gè)加速的過(guò)程。 * CycleInterpolator:動(dòng)畫(huà)從開(kāi)始到結(jié)束,變化率是循環(huán)給定次數(shù)的正弦曲線 * AccelerateDecelerateInterpolator:動(dòng)畫(huà)從開(kāi)始到結(jié)束,變化率是先加速后減速的過(guò)程。 * LinearInterpolator:動(dòng)畫(huà)從開(kāi)始到結(jié)束,變化率是線性變化。 * */ /*計(jì)算當(dāng)前時(shí)刻動(dòng)畫(huà)進(jìn)度值*/ float AnimCurrentValue =mDecelerateInterpolator.getInterpolation(1.0f * mCurrentTime / mDuration); /*圖形畫(huà)筆設(shè)置*/ paint.setAntiAlias(true); paint.setStyle(Paint.Style.STROKE); /*積分?jǐn)?shù),上邊的大圓*/ paint.setStrokeWidth(4); paint.setColor(mColor); /*積分大圓的中心坐標(biāo)和半徑*/ float score_radius = totalHeight * 1 / 5, score_circle_x = totalWidth / 2, score_circle_y = totalHeight / 3; /*繪制圓弧*/ canvas.drawCircle(score_circle_x, score_circle_y, score_radius, paint); /*文字畫(huà)筆基本設(shè)置*/ paintText.setAntiAlias(true); paintText.setStyle(Paint.Style.STROKE); /*文字從中間開(kāi)始繪制*/ /*Paint.Align.CENTER:The text is drawn centered horizontally on the x,y origin*/ paintText.setTextAlign(Paint.Align.CENTER); /*文字畫(huà)筆大小和顏色設(shè)置*/ paintText.setTextSize(score_radius * 3 / 4); paintText.setColor(getResources().getColor(typedValue.resourceId)); /*圓心位置繪制積分?jǐn)?shù)值*/ canvas.drawText("" + mScore, score_circle_x, score_circle_y, paintText); /*縮小字體繪制文本信息*/ paintText.setTextSize(score_radius * 1 / 4); paintText.setAlpha(80); /*圓心下邊繪制文本*/ canvas.drawText("積分", score_circle_x, score_circle_y + score_radius / 2, paintText); /*設(shè)置畫(huà)筆,畫(huà)下邊的三個(gè)小圓*/ paint.setStrokeWidth(4); paint.setAlpha(255); /*下邊三個(gè)小圓的半徑*/ float small_radius = totalHeight / 10; /*三個(gè)小圓的圓心的x坐標(biāo)*/ float[] circleXs = new float[]{totalWidth / 2 - score_radius * 3 / 2, totalWidth / 2, totalWidth / 2 + score_radius * 3 / 2}; /*三個(gè)小圓的圓心的y坐標(biāo)*/ float circleY = totalHeight * 3 / 4; /*計(jì)算三個(gè)小圓弧掃過(guò)的角度*/ float[] theta_values = new float[]{360 * mWinNumber / FULL_SCORE* AnimCurrentValue, 360 * mDrawNumber / FULL_SCORE* AnimCurrentValue, 360 * mLoseNumber / FULL_SCORE* AnimCurrentValue}; /*設(shè)置畫(huà)筆顏色,繪制外圍圓環(huán)*/ paint.setColor(getResources().getColor(typedValue1.resourceId)); /*分別繪制三個(gè)外圍圓環(huán)*/ canvas.drawCircle(circleXs[0], circleY, small_radius, paint);//畫(huà)WIN背景圓 canvas.drawCircle(circleXs[1], circleY, small_radius, paint);//畫(huà)DRAW背景圓 canvas.drawCircle(circleXs[2], circleY, small_radius, paint);//畫(huà)LOSE背景圓 /*更改畫(huà)筆顏色,繪制圓弧進(jìn)度條*/ paint.setColor(mColor); /*drawArc的起始角度是3點(diǎn)鐘方向,因此要從12點(diǎn)鐘方向開(kāi)始繪制,起始角度為270度*/ canvas.drawArc(new RectF(circleXs[0] - small_radius, circleY - small_radius, circleXs[0] + small_radius, circleY + small_radius), 270, theta_values[0], false, paint);//畫(huà)WIN圓形進(jìn)度條 canvas.drawArc(new RectF(circleXs[1] - small_radius, circleY - small_radius, circleXs[1] + small_radius, circleY + small_radius), 270, theta_values[1], false, paint);//畫(huà)DRAW圓形進(jìn)度條 canvas.drawArc(new RectF(circleXs[2] - small_radius, circleY - small_radius, circleXs[2] + small_radius, circleY + small_radius), 270, theta_values[2], false, paint);//畫(huà)LOSE圓形進(jìn)度條 /*繪制圓弧結(jié)束處的小圓點(diǎn),實(shí)心圓*/ paint.setStyle(Paint.Style.FILL); /*已知半徑、圓心位置、便宜角度,根據(jù)三角函數(shù)很方便計(jì)算出小實(shí)心圓圓心坐標(biāo)*/ canvas.drawCircle(circleXs[0] + small_radius * (float) Math.sin(theta_values[0] * Math.PI / 180), circleY - small_radius * (float) Math.cos(theta_values[0] * Math.PI / 180), 6, paint);//畫(huà)WIN末尾小圓點(diǎn) canvas.drawCircle(circleXs[1] + small_radius * (float) Math.sin(theta_values[1] * Math.PI / 180), circleY - small_radius * (float) Math.cos(theta_values[1] * Math.PI / 180), 6, paint);//畫(huà)DRAW末尾小圓點(diǎn) canvas.drawCircle(circleXs[2] + small_radius * (float) Math.sin(theta_values[2] * Math.PI / 180), circleY - small_radius * (float) Math.cos(theta_values[2] * Math.PI / 180), 6, paint);//畫(huà)LOSE末尾小圓點(diǎn) /*繪制文字*/ paintText.setColor(getResources().getColor(typedValue.resourceId)); paintText.setTextSize(small_radius * 2 / 3); /*測(cè)量文字大小,確定繪制文字時(shí)垂直方向的位置*/ Paint.FontMetrics fontMetrics = paint.getFontMetrics(); float textBaseLineOffset = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom; /*在三個(gè)小圓的正中心位置繪制相應(yīng)的數(shù)字*/ canvas.drawText("" + (int)(mWinNumber * AnimCurrentValue), circleXs[0], circleY + textBaseLineOffset, paintText); canvas.drawText("" + (int)(mDrawNumber * AnimCurrentValue), circleXs[1], circleY + textBaseLineOffset, paintText); canvas.drawText("" + (int)(mLoseNumber * AnimCurrentValue), circleXs[2], circleY + textBaseLineOffset, paintText); /*調(diào)整字體大小,繪制文本信息*/ paintText.setTextSize(small_radius * 4 / 9); canvas.drawText("勝場(chǎng)", circleXs[0], circleY - small_radius*4/3, paintText); canvas.drawText("平局", circleXs[1], circleY - small_radius*4/3, paintText); canvas.drawText("負(fù)場(chǎng)", circleXs[2], circleY - small_radius*4/3, paintText); /*20ms刷新一次數(shù)據(jù)*/ mHandler.postDelayed(mAnimation, 20);//啟動(dòng)動(dòng)畫(huà) } public void setColor(int mColor) { this.mColor = mColor; invalidate(); } public void setScore(int score) { this.mScore = score; invalidate(); } public void setWinDrawLose(int win,int draw,int lose) { this.mWinNumber = win; this.mDrawNumber = draw; this.mLoseNumber = lose; mCurrentTime =0; invalidate(); } }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android-實(shí)現(xiàn)切換Fragment頁(yè)功能的實(shí)現(xiàn)代碼
本篇文章主要介紹了Android-實(shí)現(xiàn)切換Fragment頁(yè)功能的實(shí)現(xiàn)代碼,具有一定的參加價(jià)值,有興趣的可以了解一下。2017-02-02Android帶清除功能的輸入框控件EditTextWithDel
這篇文章主要為大家詳細(xì)介紹了Android帶清除功能的輸入框控件EditTextWithDel,感興趣的小伙伴們可以參考一下2016-09-09Android中TextView文本高亮和點(diǎn)擊行為的封裝方法
這篇文章主要介紹了Android中TextView文本高亮和點(diǎn)擊行為的封裝方法,文中介紹的非常詳細(xì),相信對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-03-03android底部菜單欄實(shí)現(xiàn)原理與代碼
底部菜單欄很重要,我看了一下很多應(yīng)用軟件都是用了底部菜單欄做,我這里使用了tabhost做了一種通用的(就是可以像微信那樣顯示未讀消息數(shù)量的,雖然之前也做過(guò)但是layout下的xml寫(xiě)的太臃腫,這里去掉了很多不必要的層,個(gè)人看起來(lái)還是不錯(cuò)的,所以貼出來(lái)方便以后使用2013-01-01Android系統(tǒng)默認(rèn)對(duì)話框添加圖片功能
這篇文章主要介紹了Android系統(tǒng)默認(rèn)對(duì)話框添加圖片的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-01-01Android逆向入門(mén)之常見(jiàn)Davlik字節(jié)碼解析
Dalvik是Google公司自己設(shè)計(jì)用于Android平臺(tái)的虛擬機(jī)。Dalvik虛擬機(jī)是Google等廠商合作開(kāi)發(fā)的Android移動(dòng)設(shè)備平臺(tái)的核心組成部分之一,本篇文章我們來(lái)詳細(xì)解釋常見(jiàn)Davlik字節(jié)碼2021-11-11Android Studio卡很久(loading)的問(wèn)題解決辦法
這篇文章主要介紹了Android Studio卡很久(loading很久)的問(wèn)題的相關(guān)資料,需要的朋友可以參考下2017-05-05親自動(dòng)手編寫(xiě)Android通用刷新控件
這篇文章主要記錄了親自動(dòng)手編寫(xiě)Android通用刷新控件的詳細(xì)過(guò)程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06