版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載
前言
Canvas 本意是畫布的意思,然而將它理解為繪制工具一點也不為過。通過 Canvas 提供的 API,你可以在畫布上繪制出絕大部分圖形,再配合上一些操作畫布的 API,比如旋轉(zhuǎn)剪裁等變換畫布的操作,就能夠巧妙地畫出更加復雜的圖形。本文將通過結(jié)合實例帶你深入學習 Android 中的 Canvas。如有需要進一步學習畫筆 Paint ,請移步另一篇文章—— 一起看Android Paint
drawXXX系列
canvas.drawArc
-
方法:
drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint) drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint) 畫的方向為順時針
-
對參數(shù)的解釋:
-
userCenter 若為true表示此弧會和
RectF中心相連形成扇形,否則,弧的兩頭直接相連形成圖形。 - startAngle,負數(shù)或大于360則對360模除。
- sweepAngle,大于360,則畫出一圈。
- 角度:以
RectF中心為坐標中心,中心所在直線為水平線,負角度弧斜上走,正角度弧斜下走,或者說以時鐘三點鐘為0度,順時針為正,逆時針為負。
-
userCenter 若為true表示此弧會和
-
例子:
mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); RectF mRecF=new RectF(20,20,200,200); canvas.drawArc(mRecF,-45,135,true,mPaint);//以斜上45度為起點,順時針掃過135度
-
useCenter=true
image -
useCenter=false
image
canvas.drawCircle
-
方法:
drawCircle(float cx, float cy, float radius, Paint paint) -
對參數(shù)的解釋:
- cx,cy 為所畫圓的中心坐標,radius 為圓的半徑
-
例子
mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); canvas.drawCircle(100,100,80,mPaint);
- 圓心為(100,100),半徑為80
image
-
注意: 當畫筆設置了
StrokeWidth時,圓的半徑=內(nèi)圓的半徑+StrokeWidth/2
canvas.drawBitmap
-
方法1:
drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)
對參數(shù)的解釋:
- bitmap:要畫在畫布上的位圖
- matrix:構(gòu)建的矩陣作用于將要畫出的位圖
-
方法2:
drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint)
對參數(shù)的解釋:
- src:可為 null,表示畫整個位圖,否則只花出位圖的一塊矩形區(qū)域圖.subset of bitmap
- dst:定義的一個矩形范圍,位圖會平移或縮放來將自身放入矩形內(nèi)
-
方法3:
drawBitmap(Bitmap bitmap, float left, float top, Paint paint) -
方法4:
drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, Paint paint)
網(wǎng)格扭曲,水波等的繪制
知識:https://www.zybuluo.com/cxm-2016/note/506317
- 例子:
-
方法1:
mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); Matrix matrix = new Matrix(); matrix.postTranslate(100,0);//左移100 matrix.postRotate(45);//順時針旋轉(zhuǎn)45度 canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher),matrix,mPaint);

-
方法2:
Rect src = new Rect(20, 20, 40, 40);//取bitmap上src區(qū)域的部分圖像 Rect dst = new Rect(100, 100, 200, 200);//繪制的最終區(qū)域,一定填滿 mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); canvas.drawBitmap(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher),src,dst,mPaint);

-
方法3:
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher), 100, 100, mPaint);

canvas.drawColor,drawRGB
-
方法:
drawColor(int color, PorterDuff.Mode mode)
畫整個畫布的背景,但若區(qū)域受到剪裁,則只繪制剪裁區(qū)域的背景. 關(guān)鍵類 PorterDuff.Mode
- 方法:
drawRGB(int r, int g, int b)
同上
canvas.drawLine(s)
-
方法1:
drawLine(float startX, float startY, float stopX, float stopY, Paint paint)
對參數(shù)的解釋:
- 前四個參數(shù)為直線的起點和終點的 XY 軸坐標
-
方法2:
drawLines(float[] pts,Paint paint) -
方法3:
drawLines(float[] pts, int offset, int count, Paint paint)
對參數(shù)的解釋:
- pts:待畫的坐標點數(shù)組,格式為(x1,y1,x2,y2,...),至少4個值
- offset:要跳過坐標點數(shù)組中幾個值才開始畫(必須是4的倍數(shù))
- count:至少為2,offset 之后數(shù)組的大小。
- 例子
-
方法1:
mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); mPaint.setTextSize(16); canvas.drawText("起點(20,100)", 22, 100, mPaint); canvas.drawText("終點(50,100)", 52, 150, mPaint); canvas.drawLine(20, 100, 50, 150, mPaint);

-
方法2:
mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); mPaint.setTextSize(16); canvas.drawText("A1(20,100)", 0, 90, mPaint); canvas.drawText("A2(100,350)", 40, 370, mPaint); canvas.drawText("B1(100,100)", 80, 90, mPaint); canvas.drawText("B2(180,350)", 150, 370, mPaint); float[] points=new float[]{20,100,100,350,100,350,100,100,100,100,180,350};//至少4個值,即能夠繪制一條直線 canvas.drawLines(points,mPaint);

-
方法3:
mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); mPaint.setTextSize(16); canvas.drawText("A1(100,350)", 40, 370, mPaint); canvas.drawText("B2(100,100)", 80, 90, mPaint); canvas.drawText("B3(180,350)", 150, 370, mPaint); float[] points=new float[]{20,100,100,350,100,350,100,100,100,100,180,350};//至少4個點 canvas.drawLines(points,4,8,mPaint);

canvas.drawRect
-
方法1:
void drawRect(float left, float top, float right, float bottom, Paint paint)
確定矩形四個頂點的位置配上畫筆即可
-
方法2:
void drawRect(Rect r, Paint paint)
矩形的四個位置為整型時使用
-
方法3:
void drawRect(RectF r, Paint paint)
方法1的另一簡版,矩形的四個位置為浮點型時使用
-
例子:
mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); canvas.drawRect(new RectF(20f, 20f, 120f, 120f), mPaint); //canvas.drawRect(new Rect(20, 20, 120, 120), mPaint);

canvas.drawOval
繪制橢圓
類似 drawRect
canvas.drawPaint
方法:
drawPaint(Paint paint)
自定義的 paint 畫在整個畫布上,等于用 paint 在畫布上畫一個無限大的矩形,但當前畫布受到剪裁,則染色區(qū)域僅限于剪裁部分。
canvas.drawPoint(s)
繪制點,方法基本類似drawLine(s)
canvas.drawRoundRect
-
方法1:
drawRoundRect(RectF rect, float rx, float ry, Paint paint) -
方法2:
drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint)
對參數(shù)的解釋:
- rx,ry 表示 left 到 left+rx 與 left 到 left+ry 所圍區(qū)域做弧,其余三個角類似,當 rx=ry>=(right-left)/2 時表示畫一個半徑為 rx(ry) 的圓(剛好外接一個矩形)
- 例子
-
方法1:
mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); RectF mRecf = new RectF(20, 100, 200, 200); canvas.drawRoundRect(mRecf, 30, 50, mPaint);

- 方法2:API level至少21,做法一樣
canvas.drawText
-
方法1:
drawText(String text, float x, float y, Paint paint)
在 x,y 位置開始畫 text
注意:其中 y 表示文字的基線(baseline )所在的坐標,說白了就是我們小學寫字用的那種帶有橫線的本子(一般都是按照一條基線來寫字是吧?),用于規(guī)范你寫的字是否成一條直線,否則很多人寫著寫著就往上飄了。而 x 坐標就是文字繪制的起始水平坐標,但是每個文字本身兩側(cè)都有一定的間隙,故實際文字的位置會比 x 的位置再偏右側(cè)一些。
-
圖:
基線類似下圖深綠色的橫線
image -
方法2:
drawText(CharSequence text, int start, int end, float x, float y, Paint paint)
在 x,y 位置上畫出 start 到 end(不含 end) 之間的字符 CharSequence charSequence = "charSequence";
-
方法3:
drawText(char[] text, int index, int count, float x, float y, Paint paint)
對參數(shù)的解釋:
- index:表示從第幾個字符開始,
- count:表示截取的數(shù)組長度
- 字符數(shù)組的定義: char[] a="abc".toCharArray()
-
方法4:
drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint)
對參數(shù)的解釋:
- path:文本繪制的路徑(關(guān)鍵)
- hOffset:相對于路徑的水平偏移量
- vOffset:相對于路徑的垂直偏移量
-
方法5:
drawTextOnPath(char[] text, int index, int count, Path path, float hOffset, float vOffset, Paint paint)方法3和4的合體
-
方法6:
drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, Paint paint)
對參數(shù)的解釋:
- contextStart:可選,直接=start
- contextEnd:可選,直接=end
- x,y:文字繪制起點
- isRt1(isRightToLeft):文字是否支持rtl
- 0 <= contextStart <= start <= end <= contextEnd <= text.length
-
方法7:
drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount, float x, float y, boolean isRtl, Paint paint)
方法3和方法6合體
```count = end - start, contextCount = contextEnd - contextStart.```
- 例子:
-
方法1:
mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); mPaint.setTextSize(20); canvas.drawText("Canvas學習",50,100,mPaint);

-
方法2:
mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); mPaint.setTextSize(20); CharSequence charSequence="Canvas學習"; canvas.drawText(charSequence,1,charSequence.length(),30,50,mPaint);

-
方法3:
mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); mPaint.setTextSize(20); char[] chars="Canvas學習".toCharArray(); canvas.drawText(chars,1,chars.length-1,30,50,mPaint);
效果圖同方法2
-
方法4:
mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); mPaint.setTextSize(20); Path path=new Path(); String text="Canvas學習"; path.addCircle(100,100,50, Path.Direction.CCW); canvas.drawTextOnPath(text,path,0f,0f,mPaint);

canvas.drawPath
方法:drawPath(Path path, Paint paint)
根據(jù)定義的路徑畫出圖
例子:
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
Path path=new Path();
path.addCircle(100,100,50, Path.Direction.CCW);
canvas.drawPath(path,mPaint);
效果等于畫一個圓
canvas.clipxxx系列
canvas.clipPath
-
方法1:
clipPath(Path path)
按所定義的路線剪裁,默認Region.Op.INTERSECT表示剪裁出相交的部分
-
方法2:
clipPath(Path path, Region.Op op)
解釋:用指定的路徑 path 修改當前的剪裁
對op參數(shù)的理解:以剪裁兩次的區(qū)域分別為A,B來區(qū)別
- Region.Op.DIFFERENCE:剪裁出差異的部分,類似 A-B 部分
- Region.Op.REPLACE:后剪裁B的覆蓋剪裁的A
- Region.Op.REVERSE_DEFFERENCE:剪裁出差異的部分,類似 B-A 部分
- Region.Op.INTERSECT:剪裁出相交的部分,類似 A交B 部分
- Region.Op.UNION:剪裁出AB合并的部分,類似** AUB**
- Region.Op.XOR:是** (AUB)-(A交B)** 剛好與** A交B** 相對
-
方法3:
clipRect(Rect[F] rect, Region.Op op)[]表示可選
解釋:用指定的矩形來修改當前的剪裁
-
方法4:
clipRect(Rect rect)
剪裁一個矩形區(qū)域,還有其他能構(gòu)造矩形的方法不再列出
-
例子(以剪裁路徑為例):
首次剪裁Path path=new Path(); path.addCircle(100,100,50, Path.Direction.CCW); canvas.clipPath(path); canvas.clipPath(path); canvas.drawColor(Color.RED);//紅色區(qū)域即為剪裁的區(qū)域
由于 clipPath 方法剪裁模式默認為Region.Op.INTERSECT,故當前剪裁部分和整個畫布相交即為本身。

-
剪裁模式(綠色區(qū)域為最終得到的剪裁部分)
mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); canvas.drawColor(Color.BLUE); canvas.drawRect(new RectF(20, 20, 120, 120), mPaint); canvas.drawCircle(120, 70, 50, mPaint); canvas.clipRect(new RectF(20, 20, 120, 120)); Path path = new Path(); path.addCircle(120, 70, 50, Path.Direction.CCW); canvas.clipPath(path, Region.Op.INTERSECT); canvas.drawColor(Color.GREEN);
-
Region.Op.INTERSECT
image -
Region.Op.REPLACE
image -
Region.Op.REVERSE_DEFFERENCE
image -
Region.Op.UNION
image -
Region.Op.XOR
image
canvas的保存與恢復
- 解釋:用來保存或恢復
Canvas的狀態(tài) - 作用:
save之后可以調(diào)用Canvas的平移、放縮、旋轉(zhuǎn)、錯切、裁剪等對當前畫布進行操作,再進行相應的繪制,避免影響畫布上已繪制的view,配合canvas.restore()(將當前畫布恢復到初始狀態(tài)) 使用
canvas的變幻操作
canvas.translate
-
方法:
canvas.translate(float dx, float dy)
作用:移動當前畫布水平距離 dx,豎直距離 dy
-
例子:
mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); canvas.drawColor(Color.BLUE); canvas.translate(100,100); canvas.drawCircle(0,0,50,mPaint);

canvas.scale
-
方法1:
canvas.scale(float sx, float sy)
作用:sx、sy 是 x、y 方向上縮放的倍數(shù),畫布縮放后,再畫出的圖片相應的坐標都會進行縮放
-
例子:
mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); canvas.drawColor(Color.BLUE); canvas.save(); canvas.scale(0.5f,0.5f);//x,y均縮小一半 canvas.drawCircle(100,100,50,mPaint); canvas.restore(); mPaint.setColor(Color.WHITE); canvas.drawCircle(100,100,50,mPaint);

-
方法2:
canvas.scale (float sx, float sy, float px, float py)
作用:縮放畫布并平移畫布到基準點 (px,py)
對參數(shù)的解釋:
- px,py 為縮放后畫布新的坐標原點(也叫縮放基準點)
-
例子:
mPaint.setAntiAlias(true); canvas.drawColor(Color.BLUE); mPaint.setColor(Color.WHITE); canvas.drawCircle(100,100,50,mPaint); canvas.save(); canvas.scale(0.5f,0.5f,100,100); mPaint.setColor(Color.RED); canvas.drawCircle(100,100,50,mPaint); canvas.restore();

canvas.rotate
-
方法:
canvas.rotate(float degrees)
作用:順時針旋轉(zhuǎn)當前畫布一定角度,也可加入基準點坐標
-
例子:
mPaint.setAntiAlias(true); canvas.drawColor(Color.BLUE); mPaint.setColor(Color.WHITE); canvas.drawRect(new RectF(80,80,180,180),mPaint); canvas.save(); canvas.rotate(45); //canvas.rotate(45,200,200); mPaint.setColor(Color.RED); canvas.drawRect(new RectF(80,80,180,180),mPaint); canvas.restore();
-
無基準點
image -
有基準點 (200,200)
image
canvas.skew
-
方法:
canvas.skew(float sx, float sy)
作用:畫布的錯切
- sx:將畫布在 x 方向上傾斜相應的角度,sx 為傾斜角度的 tan 值;
-
sy:將畫布在 y 軸方向上傾斜相應的角度,sy 為傾斜角度的 tan 值;
比如在 X 軸方向上傾斜45度,tan45=1;
-
例子:
mPaint.setAntiAlias(true); canvas.drawColor(Color.BLUE); mPaint.setColor(Color.WHITE); canvas.drawRect(new RectF(0,0,180,180),mPaint); canvas.save(); canvas.skew(1,0);//畫布X軸傾斜45度 mPaint.setColor(Color.RED); canvas.drawRect(new RectF(0,0,180,180),mPaint); canvas.restore();

總結(jié)
以上大致介紹了 Canvas 類中眾多繪制方法。首先,先對方法進行解析;其次,給出相應的示例代碼并結(jié)合運行效果,旨在幫助讀者更好地理解諸如上述繪制方法的基本使用;最后,對于方法的理解如有紕漏,歡迎指正。










