1. 程式人生 > >Android自定義View之Canvas

Android自定義View之Canvas

https://www.jianshu.com/p/fb18c28d6627
用繼承View的方式來自定義View,我們就需要重寫onDraw方法,也就是得咱自己來畫圖了。畫圖就得用到畫筆和畫布,也就是Paint和Canvas。我們來了解下Canvas。
Canvas

Canvas我們可以簡單理解為畫布或是ps裡面的圖層,是繪製圖形的直接物件,控制著圖形的形狀,比如矩形、圓形等。我們在自定義View時,通過呼叫Canvas的API來繪製具體的圖形。

Canvas的常見API

繪製文字

//引數分別表示繪製的內容、繪製起點的座標和畫筆Paint
canvas.drawText(@NonNull String text, float x, float y, @NonNull Paint paint);

繪製點

//引數表示繪製的點的座標和畫筆Paint
canvas.drawPoint(float x, float y, @NonNull Paint paint);

繪製線

//引數分別表示線段的起點和終點座標,以及畫筆Paint
canvas.drawLine(float startX, float startY, float stopX, float stopY,@NonNull Paint paint) 

繪製矩形
//引數分別表示四條邊距離繪製原點的偏離距離,以及畫筆Paint
canvas.drawRect(float left, float top, float right, float bottom, @NonNull Paint paint)
繪製圓角矩形
//引數分別表示矩形的引數、圓角的X方向的半徑以及Y方向的半徑
canvas.drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint)
繪製圓
//引數分別表示圓點的座標以及圓的半徑,畫筆Paint
canvas.drawCircle(float cx, float cy, float radius, @NonNull Paint paint)
繪製圓弧
//引數分別表示用於定義圓弧的邊界橢圓、起始角度、弧度,useCenter表示是否有中心點,以及畫筆Paint
drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,@NonNull Paint paint)
繪製橢圓
//引數分別表示要繪製的橢圓的外接矩形和畫筆Paint
canvas.drawOval(@NonNull RectF oval, @NonNull Paint paint)
Canvas的常見API

Canvas的常見API
Canvas的四大方法

儲存畫布
canvas.save()
作用是將之前的所有已經繪製的影象儲存起來,讓後續的操作就好像在一個新的圖層上操作一樣

合併畫布
canvas.restore()
可以理解為PS中的合併圖層操作。作用是在save()之後繪製的所有影象和save()之前的影象進行合併。

平移畫布
canvas.translate(float dx, float dy)
預設的繪圖原點在(0,0),呼叫translate(x,y)後,則將原點移動到了(x,y)。之後的所有繪圖操作都將以(x,y)為原點執行。

旋轉畫布
canvas.rotate()
將座標系旋轉一定的角度

下面以繪製一個鐘錶盤為例子來實際運用canvas

以繪製一個位於螢幕中間的鐘錶盤為例子,這是一個自定義View,在佈局檔案中LayoutParams屬性都設定為match_parent。這樣子後面呼叫View的getWidth()和getHeight()獲取的都是螢幕的寬高

<android.support.constraint.ConstraintLayout xmlns:android=“http://schemas.android.com/apk/res/android
xmlns:app=“http://schemas.android.com/apk/res-auto
xmlns:tools=“http://schemas.android.com/tools
android:layout_width=“match_parent”
android:layout_height=“match_parent”
tools:context=“com.xxq2dream.learningtests.MainActivity”>

<com.xxq2dream.myview.CanvasTestView
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

</android.support.constraint.ConstraintLayout>
(1)首先需要繪製最外面的圓

準備外面圓的畫筆Paint
Paint circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
circlePaint.setColor(Color.parseColor("#000000"));
circlePaint.setStrokeWidth(5);
circlePaint.setStyle(Paint.Style.STROKE);
畫圓,這裡我們畫在螢幕的中間,半徑為getWidth()/4
//畫圓
canvas.drawCircle(getWidth()/2, getHeight()/2, getWidth()/4, circlePaint);

最外面的圓
(2)畫刻度線和刻度

準備畫筆Paint
//準備畫筆Paint
Paint keduPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
keduPaint.setStrokeWidth(3);
keduPaint.setColor(Color.parseColor("#000000"));
keduPaint.setStyle(Paint.Style.STROKE);
確定刻度線的起點和終點座標
刻度線就是一條線段肯定要用drawLine()方法,所以我們需要計算出每個刻度線的起點終點座標,最上面的12點的刻度線最簡單,起點的座標為(getWidth()/2, getHeight()/2-getWidth()/4),Y軸就是圓心減去半徑,而終點的座標Y值只需要加上個線的長度,終點座標為(getWidth()/2,getHeight()/2-getWidth()/4+50)。

而要計算其他的刻度線座標,就需要將我們的座標系旋轉一個角度,也就是一個刻度的角度。這樣一來刻度線的起點終點座標就和上面的一樣了。

//旋轉座標系
canvas.rotate(15, getWidth()/2, getHeight()/2);
刻度線應該有長有短,我們畫的刻度是24根刻度線,那第0、6、12、18根刻度線要長一些,其他的要短一些,也就是上面的線段的終點Y座標少加一點,比如(getWidth()/2,getHeight()/2-getWidth()/4+25)
我們還要繪製刻度,也就是文字,肯定得用到drawText()方法,文字內容就是刻度值。為了讓文字相對於刻度居中,文字的起點要考慮文字的寬度,文字的縱座標的話離刻度線的終點向下偏移一點就行了
String degree = String.valueOf(i);
canvas.drawText(degree, getWidth()/2-keduPaint.measureText(degree)/2, getHeight()/2-getWidth()/4 + 80, keduPaint);
綜上分析,繪製刻度和刻度線就比較簡單了
//畫刻度線和刻度
Paint keduPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
keduPaint.setStrokeWidth(3);
keduPaint.setColor(Color.parseColor("#000000"));
keduPaint.setStyle(Paint.Style.STROKE);

for (int i=0; i<24; i++) {
if (i0 || i6 || i== 12 || i==18) {
keduPaint.setStrokeWidth(3);
keduPaint.setTextSize(30);
canvas.drawLine(getWidth()/2, getHeight()/2-getWidth()/4, getWidth()/2,getHeight()/2-getWidth()/4+50, keduPaint);
String degree = String.valueOf(i);
canvas.drawText(degree, getWidth()/2-keduPaint.measureText(degree)/2, getHeight()/2-getWidth()/4 + 80, keduPaint);
}else {
keduPaint.setStrokeWidth(1);
keduPaint.setTextSize(15);
canvas.drawLine(getWidth()/2, getHeight()/2-getWidth()/4, getWidth()/2,getHeight()/2-getWidth()/4+25, keduPaint);
String degree = String.valueOf(i);
canvas.drawText(degree, getWidth()/2-keduPaint.measureText(degree)/2, getHeight()/2-getWidth()/4 + 60, keduPaint);

}
canvas.rotate(15, getWidth()/2, getHeight()/2);

}

畫刻度和刻度線
(3)畫一長一短2根指標

2根指標就是2條線段,指標的起點都在圓心,終點的話就是在原點的基礎上偏移一段距離。所以為了方便計算,我們可以直接把座標系移到圓心處

//畫2根指標
Paint hourPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
hourPaint.setColor(Color.parseColor("#000000"));
hourPaint.setStrokeWidth(20);
hourPaint.setStyle(Paint.Style.STROKE);

Paint minutePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
minutePaint.setColor(Color.parseColor("#000000"));
minutePaint.setStrokeWidth(10);
minutePaint.setStyle(Paint.Style.STROKE);

canvas.save();
//將座標系平移到圓點
canvas.translate(getWidth()/2, getHeight()/2);
canvas.drawLine(0, 0, 100, 100, hourPaint);
canvas.drawLine(0, 0, 100, 150, minutePaint);
canvas.restore();

效果圖
總結

Canvas是繪製圖像的直接操作物件,和Paint搭配使用能繪製豐富的影象
要掌握Canvas的常用的一些API
要掌握Canvas的平移和旋轉操作,能幫我們簡化很多影象座標的計算

作者:xxq2dream
連結:https://www.jianshu.com/p/fb18c28d6627
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。