android自定義view學習(一)之時尚錶盤
最近在學習自定義view,順便謝謝自己的路程,後面回首看看以前的自己有多菜。。。。下方的是實現圖,首先需要說明的是,這個效果是看了一遍部落格,然後自己想著去實現,並給出此部落格:點選開啟連結
正文:首先學習自定義view需要稍稍入門一下,然後是各種函式的使用,以及大量的練習,至於入門的話,推薦鴻陽大神的部落格,講的非常詳細:點選開啟連結,然後自己的自定義view學習之路暫時跟著一篇部落格走,有興趣的朋友可以一起:點選開啟連結
接下來進入我的實現過程了:(發現錯誤了,但是並不修正此部落格, 算是記錄錯誤:onDraw這個方法,重新整理介面就會執行,因此不要去new物件,不然會導致浪費大量的資源,關於path,因為不能重複的new,所以每次對path進行一下reset就好 )
首先分析組成:一個粗圓,一個細圓,一個粗圓弧,一個細圓弧,一個進度線,幾個刻度線
為了簡便,此處不使用自定義屬性
按照流程走:先測量一下寬高
if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) { mWidth = MeasureSpec.getSize(widthMeasureSpec); } if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) { mHeight = MeasureSpec.getSize(heightMeasureSpec);} setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
因為自己學習過程中,先沒有去弄wrap_content的佈局,直接給的固定寬高,當然用wrap_content的時候,測量模式為ATMOST,只需要再處理一下即可
接著定義了一些成員變數
private int mHeight; private int mWidth; private Paint mPaint; //正方形邊長 private int rectWidth = 400; //開始角度 private int startAngle = 135; //掃過角度 private intendAngle = 270; //外弧形邊距 private int outsidePadding = 50; //每一個線相距角度 private int everyArc = 30; //圓心的x值與y值 private int centerArcX; private int centerArcY; //最內心圓圈直徑 private int insideRadius = 15; //中間圓的直徑 private int insideTwoRadius = 30; //大圓弧和最外層圓弧相距的距離 private int twoArcPadding = 60; //大圓弧的粗度 private int bigArcWidth = 40; //百分比進度 private float haveDownProgress = 0; //一共畫幾次線 private float lineCount = 10; //儲存畫線類的集合 private List<LineEntity> mLineEntities;
(自定義view的時候,瘋狂註釋是好習慣)
這個時候就可以開始繪製了
先畫一個最外層的弧形:
//繪製最外層弧形 mPaint.setColor(Color.WHITE); mPaint.setStrokeWidth(4); mPaint.setStyle(Paint.Style.STROKE); RectF rectf = new RectF(outsidePadding, outsidePadding, outsidePadding + rectWidth, outsidePadding + rectWidth); canvas.drawArc(rectf, startAngle+10, endAngle-20, false, mPaint);
確定好矩形位置與角度,直接呼叫函式繪製即可
然後畫裡面的兩個圓:
//畫最內的圓圈 mPaint.setColor(Color.WHITE); mPaint.setStrokeWidth(8); canvas.drawCircle(centerArcX, centerArcY, insideRadius, mPaint); //畫中間的園 mPaint.setStrokeWidth(2); canvas.drawCircle(centerArcX, centerArcY, insideTwoRadius, mPaint);
接著畫最粗的圓弧,因為是進度表盤,進度分為兩個區域(原諒我懶得製作動態圖),大概為下面這個樣子:
此處採用的方式為繪製兩個不同弧度的圓弧,差別在於弧度和顏色不同
//畫大圓弧,分為藍色與白色區域,用同一個Recf RectF rectFTwo = new RectF(outsidePadding + twoArcPadding, outsidePadding + twoArcPadding, outsidePadding + rectWidth - twoArcPadding, outsidePadding + rectWidth - twoArcPadding); //畫一個藍色區域 mPaint.setColor(Color.YELLOW); mPaint.setStrokeWidth(bigArcWidth); canvas.drawArc(rectFTwo, startAngle, 270 * haveDownProgress + 2, false, mPaint); //畫一個白色區域 mPaint.setColor(Color.WHITE); canvas.drawArc(rectFTwo, 270 * haveDownProgress + 135 - 2, 270 * (1 - haveDownProgress), false, mPaint);
就是這樣。嗯。。。。有點蠢 不過實現了
然後開始畫進度線了,圖上進度有線有10條,利用PathMeasure這個類可以獲取到對應圓弧的x,y座標點,這樣畫線就非常方便,不需要寫演算法去算座標點了:
if (!(mLineEntities.size()>0)){ //獲取小圓弧的座標點(畫線有間隔,設定虛擬矩形) Path path = new Path(); RectF rectFUse = new RectF(outsidePadding + twoArcPadding - 40, outsidePadding + twoArcPadding - 40, outsidePadding + rectWidth - twoArcPadding + 40, outsidePadding + rectWidth - twoArcPadding + 40); path.addArc(rectFUse, startAngle+10, endAngle-20); PathMeasure pathMeasure = new PathMeasure(path, false); for (int i = 0; i < lineCount; i++) { float[] coords = new float[]{0f, 0f}; pathMeasure.getPosTan((i) * pathMeasure.getLength() / (lineCount - 1), coords, null); LineEntity lineEntity = new LineEntity(); lineEntity.setEndX(coords[0]); lineEntity.setEndY(coords[1]); mLineEntities.add(lineEntity); } //獲取大圓弧的座標點 Path pathTwo = new Path(); pathTwo.addArc(rectf, startAngle+10, endAngle-20); PathMeasure pathMeasureTwo = new PathMeasure(pathTwo, false); for (int i = 0; i < lineCount; i++) { float[] coords = new float[]{0f, 0f}; pathMeasureTwo.getPosTan((i) * pathMeasureTwo.getLength() / (lineCount - 1), coords, null); mLineEntities.get(i).setStartX(coords[0]); mLineEntities.get(i).setStartY(coords[1]); } }
此處說明一下:mLineEntities這個類是我用來儲存進度線的類集合,加size>0的目的是因為它是固定的,不需要重複獲取,裡面就是獲取圓弧上點的座標點位置,getPosTan()方法的意思是,將圓弧分為多少段,然後你想取的位置位於第幾段,並且這個點的位置是跟著你畫圓弧的方向走的,也就是順時針第一個
然後開始畫刻度線:
//開始畫線 mPaint.setColor(Color.WHITE); mPaint.setStrokeWidth(4); for (LineEntity lineEntity : mLineEntities) { canvas.drawLine(lineEntity.getStartX(), lineEntity.getStartY(), lineEntity.getEndX(), lineEntity.getEndY(), mPaint); }
大體都完事了,差一個指標,和繪製刻度線差不多
//畫進度線 Path pathProgressSmall=new Path(); RectF rectFSmall=new RectF(centerArcX-insideRadius,centerArcY-insideRadius,centerArcX+insideRadius,centerArcY+insideRadius); pathProgressSmall.addArc(rectFSmall, startAngle-2, endAngle+3); PathMeasure pathMeasureSmall=new PathMeasure(pathProgressSmall,false); float[] coordsSmall=new float[]{0f,0f}; pathMeasureSmall.getPosTan(haveDownProgress*pathMeasureSmall.getLength(),coordsSmall,null); Path pathProgressBig=new Path(); RectF rectFBig=new RectF(outsidePadding + twoArcPadding+bigArcWidth/2, outsidePadding + twoArcPadding+bigArcWidth/2, outsidePadding + rectWidth - twoArcPadding-bigArcWidth/2, outsidePadding + rectWidth - twoArcPadding-bigArcWidth/2); pathProgressBig.addArc(rectFBig, startAngle-2, endAngle+3); PathMeasure pathMeasureBig=new PathMeasure(pathProgressBig,false); float[] coordsBig=new float[]{0f,0f}; pathMeasureBig.getPosTan(haveDownProgress*pathMeasureBig.getLength(),coordsBig,null); mPaint.setColor(Color.WHITE); mPaint.setStrokeWidth(4); canvas.drawLine(coordsSmall[0],coordsSmall[1],coordsBig[0],coordsBig[1],mPaint);
這樣的話,自定義view已經完事了,對外提供一個方法,用於控制進度:
public void onRefresh(float progress) { haveDownProgress = progress; invalidate(); }
到此結束,開始研究自定義view不久,可能寫的很多地方有問題,新手可以借鑑一下,大神請多多包涵,並指出不合理的地方!謝謝各位
另外插一句,之前放上去的部落格,它使用的旋轉畫布的方式實現,效能應該更好,此處只是放出了自己的實現方式,並且利用PathMeasure獲取座標點算是不同處得亮點(嘿嘿)