1. 程式人生 > >android自定義view學習(一)之時尚錶盤

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 int 
endAngle = 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獲取座標點算是不同處得亮點(嘿嘿)