需求:以儀表盤的形式展示數據,要求區間分明,且靈活。
功能(可設置屬性):
當前指示值
儀表盤種類是不超過半圓,還是超過半圓
大刻度份數和大刻度內小刻度的份數
指針和原點的顏色(默認爲指針是紅色,圓點是灰色)
外弧的色條,即數據區間
刻度和刻度值的顏色(默認爲跟隨外弧的顏色)
原點上方的Text字體的大小和顏色(默認爲黑色,字體大小默認爲半徑的一定比例)
原點下方的Text字體的大小和顏色(默認爲黑色,字體大小默認爲半徑的一定比例)
項目地址:https://github.com/AndroidCloud/PieDashboard 如有不足,歡迎各位issues和開支優化
GitHub地址:https://github.com/AndroidCloud
最終實現效果:
技術路線(簡要技術思路,具體實現詳見GitHub的Demo):
1,封裝DashboradBean對象和HighlightCR對象。用於傳遞屬性到View中進行繪製
public class DashboradBean { private int maxValue; //刻度盤最大值 private int minValue; //刻度盤最小值 private int bigSliceCount; //刻度盤大刻度區間數 private int smallSliceCount; //刻度盤大刻度中小刻度區間數 private int StartAngle; //開始角度 private int allAngle; //需要畫的總角度 private List<HighlightCR> highlightCRList; //外弧色帶的集合 private boolean isHalf; //是否是半圓以內 private int scaleColor; //刻度值的顏色,默認0時,顏色隨色帶變化 private int scaleTextColor; //刻度值的讀數的顏色,默認0時,顏色隨色帶變化 private int centerPointColor;//中心原點的顏色 private int pointerColor; //指針的顏色 //get和set忽略
//外弧色帶 public class HighlightCR { private int mStartAngle;//開始角度 private int mSweepAngle;//需要畫的度數 private int mColor;//色帶顏色 //get和set忽略
2,根據傳遞的屬性對View進行繪製
首先是控制View的大小適配(根據設定的寬來適配高),如果總弧度小於半圓,View的高度取View的寬度的1/2再 多一點,以爲畫外弧的畫筆要設置粗一點,所以爲了繪製原點,需要留出部分位置。如果總弧度大於半圓, View的高度取值和寬度一樣。
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width=MeasureSpec.getSize(widthMeasureSpec); int height = 0; if (dashboradBean!=null){ //總弧度是半圓以內 if (dashboradBean.isHalf()){ height =(int) (width/2f+(width/2f/7.5f)); }else{ //總弧度大於半圓 height=width; } setMeasuredDimension(width, height); } }
3,進行繪製,分別繪製刻度盤的弧形,刻度盤的和刻度值,指針和中心原點,原點上方Text,原點下方Text
@Override protected void onDraw(Canvas canvas) { mRadius=getWidth()/2f; mHighlightRadius=mRadius/7.5f; mCenterX=mRadius; mCenterY=mRadius; super.onDraw(canvas); if (dashboradBean != null) { drawMeasures(canvas);/*** 刻度線和刻度值*/ drawStripe(canvas);/**刻度盤的弧形*/ drawTopTexts(canvas);/**畫中心原點上方文本*/ drawEndTexts(canvas);/**畫中心原點下方文本*/ drawPointer(canvas); /**畫指針和中心原點*/ } }繪製刻度線和刻度值(刻度線的長度和刻度值的字體大小都是根據View的寬度來按照一定比例換算得來,以達到 適配的目的),原理是旋轉畫布後畫好,在旋轉回來,依次把所有刻度線和刻度值畫好
/*** 刻度線和刻度值*/ private void drawMeasures(Canvas canvas) { paint.setStrokeCap(Paint.Cap.ROUND); paint.setStrokeWidth(2); Paint p=new Paint(); p.setStyle(Paint.Style.FILL); p.setStrokeWidth(1); p.setTextSize(mHighlightRadius / 1.2f); p.setAntiAlias(true); p.setTextAlign(Paint.Align.CENTER); float averageBigangle=((float)dashboradBean.getAllAngle()) /((float)dashboradBean.getBigSliceCount()); for (int i = 0; i <= dashboradBean.getBigSliceCount(); i++) { //繪製大刻度 float angle = i *averageBigangle+dashboradBean.getStartAngle() ; float[] point1 = getCoordinatePoint(mRadius-mHighlightRadius/0.85f, angle); float[] point2 = getCoordinatePoint(mRadius-mHighlightRadius/0.85f-mHighlightRadius/5f*2.8f, angle); if ( dashboradBean.getHighlightCRList() != null) { for (int j = 0; j < dashboradBean.getHighlightCRList().size(); j++) { HighlightCR highlightCR = dashboradBean.getHighlightCRList().get(j); if (highlightCR.getColor() == 0 || highlightCR.getSweepAngle() == 0) continue; if (angle>=highlightCR.getStartAngle()&&angle <= highlightCR.getStartAngle() + highlightCR.getSweepAngle()) { paint.setColor(highlightCR.getColor()); p.setColor(highlightCR.getColor()); break; } } } else { paint.setColor(Color.BLACK); p.setColor(Color.BLACK); } if (dashboradBean.getScaleColor()!=0){ paint.setColor(dashboradBean.getScaleColor()); } paint.setStrokeWidth(mHighlightRadius / 6f); canvas.drawLine(point1[0], point1[1], point2[0], point2[1], paint); //繪製圓盤上的數字 String number =trimFloat(((float) (dashboradBean.getMaxValue()-dashboradBean.getMinValue())) /((float)dashboradBean.getBigSliceCount())*i+(float)dashboradBean.getMinValue()) +""; if (dashboradBean.getScaleTextColor()!=0){ p.setColor(dashboradBean.getScaleTextColor()); } //旋轉繪製 canvas.rotate(360f - (float) dashboradBean.getAllAngle() / 2f + (float) i * averageBigangle, mCenterX, mCenterY); canvas.drawText(number, mCenterX, mHighlightRadius * 2.75f, p); canvas.rotate(-360f+(float)dashboradBean.getAllAngle()/2f-(float)i*averageBigangle,mCenterX,mCenterY); } //繪製小的子刻度 float averageSmallangle=((float)dashboradBean.getAllAngle() )/(float)(dashboradBean.getSmallSliceCount()*dashboradBean.getBigSliceCount()); for (int i = 0; i < dashboradBean.getSmallSliceCount()*dashboradBean.getBigSliceCount(); i++) { float angle = i * averageSmallangle + dashboradBean.getStartAngle(); float[] point1 = getCoordinatePoint(mRadius-mHighlightRadius/0.85f, angle); float[] point2 = getCoordinatePoint(mRadius-mHighlightRadius/0.85f-mHighlightRadius/5f*1.4f, angle); if ( dashboradBean.getHighlightCRList() != null) { for (int j = 0; j < dashboradBean.getHighlightCRList().size(); j++) { HighlightCR highlightCR = dashboradBean.getHighlightCRList().get(j); if (highlightCR.getColor() == 0 || highlightCR.getSweepAngle() == 0) continue; if (angle>=highlightCR.getStartAngle()&&angle <= highlightCR.getStartAngle() + highlightCR.getSweepAngle()) { paint.setColor(highlightCR.getColor()); break; } } } else { paint.setColor(Color.BLACK); } if (dashboradBean.getScaleColor()!=0){ paint.setColor(dashboradBean.getScaleColor()); } paint.setStrokeWidth(mHighlightRadius/6f/2f); canvas.drawLine(point1[0], point1[1], point2[0], point2[1], paint); } }繪製刻度盤的外弧,包括粗的外弧和細的外弧(外弧的粗細程度都是根據View的寬度來按照一定比例換算得來, 以達到適配的目的)
/**刻度盤的弧形*/ private void drawStripe(Canvas canvas) { if (dashboradBean.getHighlightCRList()!=null){ for (int i = 0; i < dashboradBean.getHighlightCRList().size(); i++) { HighlightCR highlightCR = dashboradBean.getHighlightCRList().get(i); if (highlightCR.getColor() == 0 || highlightCR.getSweepAngle() == 0) continue; paint.setColor(highlightCR.getColor()); paint.setStrokeWidth(mHighlightRadius); paint.setStrokeCap(Paint.Cap.ROUND); paint.setStyle(Paint.Style.STROKE); RectF rectF1=new RectF(mHighlightRadius/2f,mHighlightRadius/2f,getWidth()-mHighlightRadius/2f,getWidth()-mHighlightRadius/2f); RectF rectF2=new RectF(mHighlightRadius/0.85f,mHighlightRadius/0.85f,getWidth()-mHighlightRadius/0.85f,getWidth()-mHighlightRadius/0.85f); canvas.drawArc(rectF1, highlightCR.getStartAngle(), highlightCR.getSweepAngle(), false, paint); paint.setStrokeWidth(mHighlightRadius/6f); canvas.drawArc(rectF2, highlightCR.getStartAngle(), highlightCR.getSweepAngle(), false, paint); } } }
繪製指針和原點,指針爲一個封閉的三角形,指針的頂點根據傳入的需要顯示的當前值來進行計算(計算前先判 斷該值是否在有效區間內,超過最大值顯示最大值,超過最小值顯示最小值)指針長度按照View的寬度的一 定比例換算,達到適配,原點爲圓形。
/**畫指針和中心原點*/ private void drawPointer(Canvas canvas) { float degree=0; if (realTimeValue>=dashboradBean.getMinValue()&&realTimeValue<=dashboradBean.getMaxValue()){ degree=(float)(dashboradBean.getStartAngle())+((realTimeValue-dashboradBean.getMinValue())/ (dashboradBean.getMaxValue()-dashboradBean.getMinValue()))*(float)dashboradBean.getAllAngle(); }else if(realTimeValue<dashboradBean.getMinValue()){ degree=(float)dashboradBean.getStartAngle(); }else{ degree=(float)(dashboradBean.getStartAngle())+(float)dashboradBean.getAllAngle(); } float point[]=getCoordinatePoint(mRadius-mHighlightRadius*2.4f,degree); paint.setStyle(Paint.Style.FILL); Path p=new Path(); p.moveTo(point[0], point[1]); float point_left[]=getCoordinatePoint(mHighlightRadius / 2f,degree-90); float point_right[]=getCoordinatePoint(mHighlightRadius / 2f,degree+90); p.lineTo(point_left[0], point_left[1]); p.lineTo(point_right[0], point_right[1]); p.close(); if(dashboradBean.getPointerColor()!=0){ paint.setColor(dashboradBean.getPointerColor()); }else{ paint.setColor(Color.RED); } canvas.drawPath(p, paint); if (dashboradBean.getCenterPointColor()!=0){ paint.setColor(dashboradBean.getCenterPointColor()); } else { paint.setColor(Color.parseColor("#A9AFAE")); } canvas.drawCircle(mCenterX,mCenterY,mHighlightRadius/1.65f,paint); }
最後繪製原點上方和下方的Text,字體繪製的位置和字體的大小都是根據View的寬度來按照一定比例換算得到的。
/**畫中心原點下方文本*/ private void drawEndTexts(Canvas canvas) { if (EndTextSize==0){ paint.setTextSize(mHighlightRadius / 1.2f); }else { paint.setTextSize(EndTextSize); } if (EndTextColor==0){ paint.setColor(Color.BLACK); }else{ paint.setColor(EndTextColor); } paint.setStrokeWidth(1); paint.setStyle(Paint.Style.FILL); paint.setTextAlign(Paint.Align.CENTER); canvas.drawText(endText,mCenterX,mCenterY+3f*mHighlightRadius,paint); } /**畫指針和中心原點*/ private void drawTopTexts(Canvas canvas) { if (TopTextSize==0){ paint.setTextSize(mHighlightRadius / 1.4f); }else { paint.setTextSize(TopTextSize); } if (TopTextColor==0){ paint.setColor(Color.BLACK); }else{ paint.setColor(TopTextColor); } paint.setStrokeWidth(1); paint.setStyle(Paint.Style.FILL); paint.setTextAlign(Paint.Align.CENTER); canvas.drawText(topText,mCenterX,mCenterY-mHighlightRadius,paint); }
4,在Activity中使用,Demo的第一組示例佈局如下所示,寬度取屏幕的一半
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:layout_marginBottom="10dp" android:orientation="horizontal"> <com.example.vmmet.mypiedashboard.view.NewDashboardView android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:id="@+id/view1" /> <com.example.vmmet.mypiedashboard.view.NewDashboardView android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:id="@+id/view2" /> </LinearLayout>
如圖中所示的第一個示例。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); /**大刻度份數和小刻度份數(半圓)*/ setView_1_2();
/**大刻度份數和小刻度份數(半圓)*/ private void setView_1_2() { List<HighlightCR> highlights = new ArrayList<>(); highlights.add(new HighlightCR(180, 40, Color.parseColor("#4CAF50"))); highlights.add(new HighlightCR(220, 50, Color.parseColor("#EEC900"))); highlights.add(new HighlightCR(270, 90, Color.parseColor("#F44336"))); DashboradBean dashboradBean=new DashboradBean(); dashboradBean.setHighlightCRList(highlights); dashboradBean.setAllAngle(180); dashboradBean.setStartAngle(180); dashboradBean.setBigSliceCount(5); dashboradBean.setSmallSliceCount(3); dashboradBean.setMaxValue(100); dashboradBean.setMinValue(0); dashboradBean.setIsHalf(true); view1.setDashboradBean(dashboradBean); view1.setRealTimeValue(60); /////////////////////////////// DashboradBean dashboradBean2=new DashboradBean(); dashboradBean2.setHighlightCRList(highlights); dashboradBean2.setAllAngle(180); dashboradBean2.setStartAngle(180); dashboradBean2.setBigSliceCount(10); dashboradBean2.setSmallSliceCount(5); dashboradBean2.setMaxValue(200); dashboradBean2.setMinValue(100); dashboradBean2.setIsHalf(true); view2.setDashboradBean(dashboradBean2); view2.setRealTimeValue(120); }
做開發,需要腳踏實地,日積月累,願你我共勉