通過一個簡單的自定義的View,模仿華為健康里面消耗熱量和計步的圓弧View,來深入的理解RectF以及 canvas的drawArc方法。
華為健康的View是這樣的:

最近寫了一些自定義的view,其中用到RectF,Rect的地方比較多,于是有了這篇文章,來深入理解下RectF對象。先上效果圖:

效果還是有點不太一樣,但是別太在意那些細節(jié)了。。。
閑言少敘,分析下這個簡單的View,可以看到是由兩個圓弧和三段文字組成的,這兩個圓弧,掃過的區(qū)域都是180度,兩個圓弧之間需要留有間隙。
而要實現(xiàn)畫一段圓弧,其實只需要兩個步驟:
1:算出圓弧中線的一個矩形的方陣,這個地方就需要用到RectF了,為什么說是圓弧中線呢,因為圓弧是由寬度的,但是在繪制的時候,事實上是以圓弧最中間的那條線為基礎畫的,如果畫360度的話,剛好是這個正方形的內切圓。
2:通過canvas的drawArc方法,就可以了。
這個oval 參數(shù),就是我們 第一步得到的,圓弧的外切正方形,startAngle是開始的角度,需要注意的是,這個地方開始的0度是幾何里面的180度,也就是中心點右邊開始計數(shù)。sweepAngle,就是圓弧橫掃的角度,很明顯,我們的view都是180度。
useCenter,是否包含中心點,我們這里明顯不需要
paint 就是圓弧的畫筆了
public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) {
throw new RuntimeException("Stub!");
}
分析完成之后,就開始動手操作了,
//默認開始角度
private int startAngle = 180;
//默認掃過的弧度
private int defaultSweepAngle = 180;
//中心點坐標
float centerX = getWidth() / 2;
//熱量外矩形區(qū)域
RectF hotRectF = new RectF();
float hotL = hotStrokeWidth / 2;
float hotT = hotStrokeWidth / 2;
float hotR = centerX * 2 - hotStrokeWidth / 2;
float hotB = hotR;
hotRectF.set(hotL, hotT, hotR, hotB);
//默認的灰色區(qū)域
private void drawDefaultHotStroke(Canvas canvas, RectF f, float strokeWidth) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(defaultStrokeColor);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(strokeWidth);
canvas.drawArc(f, startAngle, defaultSweepAngle, false, paint);
}
//當前進度的圓弧
private void drawProgressHotStroke(Canvas canvas, RectF f) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(hotStrokeProgressColor);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(hotStrokeWidth);
canvas.drawArc(f, startAngle, currentHotLength, false, paint);
}
不知道大家有沒有注意到計算外切正方形的時候,減去了畫筆的寬度的一般,這一點就是上面提到的,繪制弧線的基準是以最中間那條線為準的,如果不減去的話,你會發(fā)現(xiàn),圓弧的上下左右四個點會超出這個view之外。
繪制下面的圓弧的原理和這個一樣,就不再贅述了,現(xiàn)在來分析下RectF
RectF (float left,
float top,
float right,
float bottom)
這是RectF的構造函數(shù),官網的解釋是代表左上右下四個方向的坐標,但是其實這樣解釋并不準確,根據自己的實驗,我們可以這樣理解,前兩個參數(shù)是矩陣左上角點的坐標,后兩個是右下角點的坐標,這樣,一個矩形就出來了。需要注意的是,要保證left <= right , top <= bottom.嗯,差不多,還是很好理解的。
那么 RectF 和 Rect 的區(qū)別是什么呢?
Rect的坐標點為int值,所以精度就沒有RectF高了,RectF的構造函數(shù)可以直接把 RectF或者Rect傳進去構建矩陣。其它的方法,都基本類似。
繪制文字的時候需要注意兩個地方,文字的Y坐標,需要加上自身的高度的一半,避免出現(xiàn)覆蓋的情況,這個地方使用了Paint的一個方法 getTextBounds 事實上把文字的屬性都給 Rect 對象了,這樣就可以拿到文字的所有想要的屬性,而我們只需要傳遞文字內容和長度就可以了。
private void drawText(Canvas canvas, float centerX) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setTextSize(stepTextSize);
paint.setTextAlign(Paint.Align.CENTER);
String textTop = "目標" + " " + targetStepNum;
Rect textF = new Rect();
paint.getTextBounds(textTop, 0, textTop.length(), textF);
//文字高度
stepTextHeight = textF.height();
float textY = textF.height() / 2 + hotStrokeWidth / 2 + stepStrokeWidth + 2 * stokeOffset;
canvas.drawText(textTop, centerX, textY, paint);
}
至此,這個簡單的自定義View 已經寫完了,之所以寫這篇文章,是因為兩點有貓膩的地方,一個是RectF的坐標點的定義,還有一個是canvas的drawArc 方法?,F(xiàn)在只是個簡單的view,并沒有實現(xiàn)計步的功能。后續(xù)再完善。