Android自定義View之Canvas的使用

xxq2dream發表於2018-08-05

Android自定義View系列

用繼承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 (i==0 || i==6 || 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的平移和旋轉操作,能幫我們簡化很多影像座標的計算

歡迎關注我的微信公眾號,和我一起每天進步一點點!

AntDream

相關文章