Android自定義View - 儀表盤
背景
隨著專案開發 越來越多的需求被擺在面前 其中不免涉及到定製的功能
其中儀表盤也是一個很常用的功能
效果圖

設計過程
外側漸變圓環

外側刻度盤及文字顯示

指標顯示

內部圓環及文字展示

程式碼實現
自定義元件顯示優化
設定自定義元件的時候要優化元件的高度
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int heitht = width / 2 / 4 * 5; initIndex(width / 2); //優化元件高度 setMeasuredDimension(width, heitht); }
onDraw()過程
protected void onDraw(Canvas canvas) { //禁用硬體加速 setLayerType(LAYER_TYPE_SOFTWARE, null); //外側顏色指示圓環 initRing(canvas); //刻度文字 initScale(canvas); //指標 initPointer(canvas); //提示內容 initText(canvas); }
主要還是這個四個繪製的過程
外側顏色指示圓環
- 首先繪製的前一部分的紅黃漸變圓環
這個圓環並不是一個180度的圓環 而是一個兩百度的圓環 下側再實現水平的效果 - 繪製後一部分的綠色漸變圓環
- 修正底部的效果 修改成水平的效果
- 繪製內部半圓 遮蓋住漸變的半圓
private void initRing(Canvas canvas) { paint.setAntiAlias(true); paint.setStrokeWidth(2); canvas.save(); //canvas中心移動到中間 canvas.translate(canvas.getWidth()/2, r); //前100紅黃漸變圓環 paint.setStyle(Paint.Style.FILL); //設定漸變的顏色範圍 int[] colors = {Color.parseColor("#F95A37"), Color.parseColor("#f9cf45")}; //設定的漸變起止位置 float[] positions = {0.5f - 10f/180f * 0.5f, 0.5f + 0.5f * 5f / 6f}; //設定漸變的蒙版 SweepGradient sweepGradient = new SweepGradient(0, 0, colors, positions); paint.setShader(sweepGradient); rect = new RectF( -length, -length, length, length); //繪製圓環 canvas.drawArc(rect, 170, 10f + 180f / 6f * 5f, true, paint); //100之後綠色漸變圓環 paint.setStyle(Paint.Style.FILL); canvas.rotate(10,0f,0f); int[] colors2 = {Color.parseColor("#79D062"),Color.parseColor("#3FBF55")}; float[] positions2 = {0.5f + 0.5f * ( 144f / 180f), 1.0f}; sweepGradient = new SweepGradient(0, 0, colors2, positions2); paint.setShader(sweepGradient); rect = new RectF( -length, -length, length, length); canvas.drawArc(rect, 180f + 180f * (140f / 180f), 180f / 6 + 10, true, paint); canvas.restore(); canvas.save(); canvas.translate(canvas.getWidth()/2, r); //繪製描邊效果的畫筆 strokePain = new Paint(paint); strokePain.setColor(0x3f979797); strokePain.setStrokeWidth(10); strokePain.setShader(null); strokePain.setStyle(Paint.Style.STROKE); canvas.drawArc(rect, 170, 200, true, strokePain); canvas.restore(); canvas.save(); canvas.translate(canvas.getWidth()/2, r); //底邊水平 paint.setShader(null); paint.setColor(backGroundColor); paint.setStyle(Paint.Style.FILL); canvas.drawRect(-length, (float) (Math.sin(Math.toRadians(10) ) * length /3f * 2f), length,(float) (Math.sin(Math.toRadians(10)) * length+ 100) , paint); canvas.drawRect(-length, (float) (Math.sin(Math.toRadians(10) ) * length /3f * 2f), length,(float) (Math.sin(Math.toRadians(10) ) * length /3f * 2f) , strokePain); //內部背景色填充 paint.setColor(backGroundColor); paint.setShader(null); rect = new RectF( - (length - length / 3f- 2), -(length / 3f * 2f - 2), length - length / 3f -2 , length / 3f * 2f - 2); canvas.drawArc(rect, 170, 200, true, strokePain); canvas.drawArc(rect, 0, 360, true, paint); }
外側刻度盤及文字顯示
旋轉畫布繪製對應角度的顯示及刻度
private void initScale(Canvas canvas) { canvas.restore(); canvas.save(); canvas.translate(canvas.getWidth()/2, r); paint.setColor(Color.parseColor("#999999")); tmpPaint = new Paint(paint); //刻度畫筆物件 tmpPaint.setStrokeWidth(1); tmpPaint.setTextSize(35); tmpPaint.setTextAlign(Paint.Align.CENTER); canvas.rotate(-90,0f,0f); floaty = length; y = - y; int count = 12; //總刻度數 paint.setColor(backGroundColor); float tempRou = 180 / 12f; //每次旋轉的角度 paint.setColor(Color.WHITE); paint.setStrokeWidth(5); //繪製刻度和百分比 for (int i = 0 ; i <= count ; i++){ if (i % 2 == 0 ) { canvas.drawText(String.valueOf((i) * 10), 0, y - 20f, tmpPaint); } canvas.drawLine(0f, y , 0, y + length / 15, paint); canvas.rotate(tempRou,0f,0f); } }
指標顯示
指標顯示的比較簡單也是唯二需要變化的之一
指標的繪製比較簡單 根據傳入的角度(百分比)旋轉對應的角度 填充繪製一個三角形
private void initPointer(Canvas canvas) { paint.setColor(Color.BLACK); canvas.restore(); canvas.save(); canvas.translate(canvas.getWidth()/2, r); float change; if (perPoint < 1 ){ change = perPoint * 180; }else { change = 180; } //根據引數得到旋轉角度 canvas.rotate(-90 + change,0f,0f); //繪製三角形形成指標 Path path = new Path(); path.moveTo(0 , pointLength); path.lineTo(-10 , 0); path.lineTo(10,0); path.lineTo(0 , pointLength); path.close(); canvas.drawPath(path, paint); }
內部圓環及文字展示
先繪製一個帶陰影的圓環 再居中繪製提示的文字資訊
private void initText(Canvas canvas) { //抗鋸齒 canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG)); canvas.restore(); canvas.save(); canvas.translate(canvas.getWidth()/2, r); float rIndex = length ; //設定文字展示的圓環 paint.setColor(Color.parseColor("#eeeeee")); paint.setShader(null); paint.setShadowLayer(5, 0, 0, 0x54000000); rect = new RectF( - (rIndex/ 3 ), - (rIndex / 3), rIndex / 3, rIndex / 3); canvas.drawArc(rect, 0, 360, true, paint); paint.clearShadowLayer(); canvas.restore(); canvas.save(); canvas.translate(canvas.getWidth()/2f , r); textPaint.setStrokeWidth(1); textPaint.setAntiAlias(true); textPaint.setTextSize(60); textPaint.setColor(Color.parseColor("#fc6555")); textPaint.setTextAlign(Paint.Align.RIGHT); //判斷指數變化及顏色設定 int _per = (int) (per * 120); if (_per < 60){ textPaint.setColor(Color.parseColor("#ff6450")); }else if (_per < 100) { textPaint.setColor(Color.parseColor("#f5a623")); }else { textPaint.setColor(Color.parseColor("#79d062")); } float swidth = textPaint.measureText(String.valueOf(_per)); //計算偏移量 是的數字和百分號整體居中顯示 swidth =(swidth - (swidth + 22) / 2); canvas.translate( swidth , 0); canvas.drawText("" + _per, 0, 0, textPaint); textPaint.setTextSize(30); textPaint.setTextAlign(Paint.Align.LEFT); canvas.drawText("%" , 0, 0, textPaint); textPaint.setTextAlign(Paint.Align.CENTER); textPaint.setColor(Color.parseColor("#999999")); canvas.restore(); canvas.save(); canvas.translate(canvas.getWidth()/2, r + length / 3 /2 ); canvas.drawText("完成率" , 0, 0, textPaint); }
更新動畫
使用ValueAnimator實現指標的轉動動畫效果
public void cgangePer(float per ){ this.perOld = this.per; this.per = per; ValueAnimator va =ValueAnimator.ofFloat(perOld,per); va.setDuration(1000); va.setInterpolator(new OvershootInterpolator()); va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { perPoint = (float) animation.getAnimatedValue(); invalidate(); } }); va.start(); }
【附錄】

資料圖
需要資料的朋友可以加入Android架構交流QQ群聊:513088520
點選連結加入群聊【Android移動架構總群】: 加入群聊
獲取免費學習視訊,學習大綱另外還有像高階UI、效能優化、架構師課程、NDK、混合式開發(ReactNative+Weex)等Android高階開發資料免費分享。