前面講解了onMeasure,接下來講解onDraw,onDraw主要就是繪制,也就是我們真正關心的部分,使用的是Canvas繪圖。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
Canvas簡介
Canvas我們可以稱之為畫布,能夠在上面繪制各種東西,是安卓平臺2D圖形繪制的基礎,非常強大。
Canvas的常用操作速查表
操作類型 | 相關API | 備注 |
---|---|---|
繪制顏色 | drawColor、drawRGB、drawARGB | 使用單一顏色填充整個畫布 |
繪制基本形狀 | drawPoint、 drawPoints、 drawLine、 drawLines、 drawRect、 drawRoundRect、 drawOval、 drawCircle、 drawArc | 依次為 點、線、矩形、圓角矩形、橢圓、圓、圓弧 |
繪制圖片 | drawBitmap、 drawPicture | 繪制位圖和圖片 |
繪制文本 | drawText、 drawPosText、 drawTextOnPath | 依次為 繪制文字、繪制文字時指定每個文字位置、根據路徑繪制文字 |
繪制路徑 | drawPath | 繪制路徑,繪制貝塞爾曲線時也需要用到該函數 |
頂點操作 | drawVertices、 drawBitmapMesh | 通過對頂點操作可以使圖像形變,drawVertices直接對畫布作用、 drawBitmapMesh只對繪制的Bitmap作用 |
畫布剪裁 | clipPath、 clipRect | 設置畫布的顯示區域 |
畫布快照 | save、 restore、 saveLayerXxx、 restoreToCount、 getSaveCount | 依次為 保存當前狀態、 回滾到上一次保存的狀態、 保存圖層狀態、 回滾到指定狀態、 獲取保存次數 |
畫布變換 | translate、 scale、 rotate、 skew | 依次為 位移、縮放、 旋轉、錯切 |
Matrix(矩陣) | getMatrix、 setMatrix、 concat | 實際上畫布的位移,縮放等操作的都是圖像矩陣Matrix,只不過Matrix比較難以理解和使用,故封裝了一些常用的方法。 |
PS: Canvas常用方法在上面已經列出,當然還存在一些其他的方法未列出,具體可以參考官方文檔 Canvas
Canvas詳解
本篇內容主要講解如何利用Canvas繪制基本圖形。
- 創建畫筆:
要想繪制內容,首先需要先創建一個畫筆,如下:
// 1.創建一個畫筆
private Paint mPaint = new Paint();
// 3.在構造函數中初始化
public SloopView(Context context, AttributeSet attrs) {
super(context, attrs);
initPaint();
// 2.初始化畫筆
private void initPaint() {
mPaint.setColor(Color.BLACK); //設置畫筆顏色
mPaint.setStyle(Paint.Style.FILL); //設置畫筆模式為填充
mPaint.setStrokeWidth(10f); //設置畫筆寬度為10px
}
在創建完畫筆之后,就可以在Canvas中繪制各種內容了。
- 繪制矩形:
確定確定一個矩形最少需要四個數據,就是對角線的兩個點的坐標值,這里一般采用左上角和右下角的兩個點的坐標。
關于繪制矩形,Canvas提供了三種重載方法,第一種就是提供四個數值(矩形左上角和右下角兩個點的坐標)來確定一個矩形進行繪制。 其余兩種是先將矩形封裝為Rect或RectF(實際上仍然是用兩個坐標點來確定的矩形),然后傳遞給Canvas繪制,如下:
// 第一種
canvas.drawRect(100,100,800,400,mPaint);
// 第二種
Rect rect = new Rect(100,100,800,400);
canvas.drawRect(rect,mPaint);
// 第三種
RectF rectF = new RectF(100,100,800,400);
canvas.drawRect(rectF,mPaint);
以上三種方法所繪制出來的結果是完全一樣的。
為什么會有Rect和RectF兩種?兩者有什么區別嗎?
答案是肯定有區別,兩者最大的區別就是精度不同,Rect是int(整形)的,而RectF是float(單精度浮點型)的。除了精度不同,兩種提供的方法也稍微存在差別,想了解更多參見官方文檔 Rect 和 RectF
- 繪制圓角矩形:
RectF rectF = new RectF(100,100,800,400);
canvas.drawRoundRect(rectF,30,30,mPaint);
這里多出來的兩個參數是橢圓的兩個半徑,繪制出的圓弧是橢圓的圓弧.
紅線標注的 rx 與 ry 就是兩個橢圓半徑,也就是相比繪制矩形多出來的那兩個參數。
我們了解到原理后,就可以為所欲為了,通過計算可知我們上次繪制的矩形寬度為700,高度為300,當你讓 rx大于350(寬度的一半), ry大于150(高度的一半) 時奇跡就出現了, 你會發現圓角矩形變成了一個橢圓, 他們畫出來是這樣的 ( 為了方便確認我更改了畫筆顏色, 同時繪制出了矩形和圓角矩形 ):
// 矩形
RectF rectF = new RectF(100,100,800,400);
// 繪制背景矩形
mPaint.setColor(Color.GRAY);
canvas.drawRect(rectF,mPaint);
// 繪制圓角矩形
mPaint.setColor(Color.BLUE);
canvas.drawRoundRect(rectF,700,400,mPaint);
其中灰色部分是我們所繪制的矩形(參考用),而里面的圓角矩形則變成了一個橢圓,實際上在rx為寬度的一半,ry為高度的一半時,剛好是一個橢圓,通過上面我們分析的原理推算一下就能得到,而當rx大于寬度的一半,ry大于高度的一半時,實際上是無法計算出圓弧的,所以drawRoundRect對大于該數值的參數進行了限制(修正),凡是大于一半的參數均按照一半來處理。
- 繪制橢圓:
相對于繪制圓角矩形,繪制橢圓就簡單的多了,因為他只需要一個矩形矩形作為參數:
// 第一種
RectF rectF = new RectF(100,100,800,400);
canvas.drawOval(rectF,mPaint);
// 第二種
canvas.drawOval(100,100,800,400,mPaint);
同樣,以上兩種方法效果完全一樣,但一般使用第一種。
PS: 如果你傳遞進來的是一個長寬相等的矩形(即正方形),那么繪制出來的實際上就是一個圓。
-
繪制圓:
繪制圓形也比較簡單, 如下:
canvas.drawCircle(500,500,400,mPaint); // 繪制一個圓心坐標在(500,500),半徑為400 的圓。
繪制圓形有四個參數,前兩個是圓心坐標,第三個是半徑,最后一個是畫筆。
繪制圓?。?br> 繪制圓弧就比較神奇一點了,為了理解這個比較神奇的東西,我們先看一下它需要的幾個參數:
// 第一種
public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle,
boolean useCenter, @NonNull Paint paint){}
// 第二種
public void drawArc(float left, float top, float right, float bottom, float startAngle,
float sweepAngle, boolean useCenter, @NonNull Paint paint) {}
從上面可以看出,相比于繪制橢圓,繪制圓弧還多了三個參數:
startAngle // 開始角度
sweepAngle // 掃過角度
useCenter // 是否使用中心
通過字面意思我們基本能猜測出來前兩個參數(startAngle, sweepAngel)的作用,就是確定角度的起始位置和掃過角度, 不過第三個參數是干嘛的?試一下就知道了,上代碼:
RectF rectF = new RectF(100,100,800,400);
// 繪制背景矩形
mPaint.setColor(Color.GRAY);
canvas.drawRect(rectF,mPaint);
// 繪制圓弧
mPaint.setColor(Color.BLUE);
canvas.drawArc(rectF,0,90,false,mPaint);
//-------------------------------------
RectF rectF2 = new RectF(100,600,800,900);
// 繪制背景矩形
mPaint.setColor(Color.GRAY);
canvas.drawRect(rectF2,mPaint);
// 繪制圓弧
mPaint.setColor(Color.BLUE);
canvas.drawArc(rectF2,0,90,true,mPaint);
上述代碼實際上是繪制了一個起始角度為0度,掃過90度的圓弧,兩者的區別就是是否使用了中心點,結果如下:
相比于使用橢圓,我們還是使用正圓比較多的,使用正圓展示一下效果:
RectF rectF = new RectF(100,100,600,600);
// 繪制背景矩形
mPaint.setColor(Color.GRAY);
canvas.drawRect(rectF,mPaint);
// 繪制圓弧
mPaint.setColor(Color.BLUE);
canvas.drawArc(rectF,0,90,false,mPaint);
//-------------------------------------
RectF rectF2 = new RectF(100,700,600,1200);
// 繪制背景矩形
mPaint.setColor(Color.GRAY);
canvas.drawRect(rectF2,mPaint);
// 繪制圓弧
mPaint.setColor(Color.BLUE);
canvas.drawArc(rectF2,0,90,true,mPaint);
簡要介紹Paint
看了上面這么多,相信有有人會有疑問,如果我想繪制一個圓,只要邊不要里面的顏色怎么辦?
很簡單,繪制的基本形狀由Canvas確定,但繪制出來的顏色,具體效果則由Paint確定。
如果你注意到了的話,在一開始我們設置畫筆樣式的時候是這樣的:
mPaint.setStyle(Paint.Style.FILL); //設置畫筆模式為填充
為了展示方便,容易看出效果,之前使用的模式一直為填充模式,實際上畫筆有三種模式,如下:
STROKE //描邊
FILL //填充
FILL_AND_STROKE //描邊加填充
為了區分三者效果我們做如下實驗:
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(40); //為了實驗效果明顯,特地設置描邊寬度非常大
// 描邊
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(200,200,100,paint);
// 填充
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(200,500,100,paint);
// 描邊加填充
paint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawCircle(200, 800, 100, paint);
關于Paint詳細的可以看下官方介紹Paint