版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載
前言
Canvas
本意是畫(huà)布的意思,然而將它理解為繪制工具一點(diǎn)也不為過(guò)。通過(guò) Canvas
提供的 API,你可以在畫(huà)布上繪制出絕大部分圖形,再配合上一些操作畫(huà)布的 API,比如旋轉(zhuǎn)剪裁等變換畫(huà)布的操作,就能夠巧妙地畫(huà)出更加復(fù)雜的圖形。本文將通過(guò)結(jié)合實(shí)例帶你深入學(xué)習(xí) Android 中的 Canvas。如有需要進(jìn)一步學(xué)習(xí)畫(huà)筆 Paint ,請(qǐng)移步另一篇文章—— 一起看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)
畫(huà)的方向?yàn)轫槙r(shí)針
-
對(duì)參數(shù)的解釋:
-
userCenter 若為true表示此弧會(huì)和
RectF
中心相連形成扇形,否則,弧的兩頭直接相連形成圖形。 - startAngle,負(fù)數(shù)或大于360則對(duì)360模除。
- sweepAngle,大于360,則畫(huà)出一圈。
- 角度:以
RectF
中心為坐標(biāo)中心,中心所在直線為水平線,負(fù)角度弧斜上走,正角度弧斜下走,或者說(shuō)以時(shí)鐘三點(diǎn)鐘為0度,順時(shí)針為正,逆時(shí)針為負(fù)。
-
userCenter 若為true表示此弧會(huì)和
-
例子:
mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); RectF mRecF=new RectF(20,20,200,200); canvas.drawArc(mRecF,-45,135,true,mPaint);//以斜上45度為起點(diǎn),順時(shí)針掃過(guò)135度
-
useCenter=true
image -
useCenter=false
image
canvas.drawCircle
-
方法:
drawCircle(float cx, float cy, float radius, Paint paint)
-
對(duì)參數(shù)的解釋:
- cx,cy 為所畫(huà)圓的中心坐標(biāo),radius 為圓的半徑
-
例子
mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); canvas.drawCircle(100,100,80,mPaint);
- 圓心為(100,100),半徑為80
image
-
注意: 當(dāng)畫(huà)筆設(shè)置了
StrokeWidth
時(shí),圓的半徑=內(nèi)圓的半徑+StrokeWidth
/2
canvas.drawBitmap
-
方法1:
drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)
對(duì)參數(shù)的解釋:
- bitmap:要畫(huà)在畫(huà)布上的位圖
- matrix:構(gòu)建的矩陣作用于將要畫(huà)出的位圖
-
方法2:
drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint)
對(duì)參數(shù)的解釋:
- src:可為 null
,表示畫(huà)整個(gè)位圖,否則只花出位圖的一塊矩形區(qū)域圖.subset of bitmap
- dst:定義的一個(gè)矩形范圍,位圖會(huì)平移或縮放來(lái)將自身放入矩形內(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)格扭曲,水波等的繪制
知識(shí):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);//順時(shí)針旋轉(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ū)域,一定填滿(mǎn) 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)
畫(huà)整個(gè)畫(huà)布的背景,但若區(qū)域受到剪裁,則只繪制剪裁區(qū)域的背景. 關(guān)鍵類(lèi) PorterDuff.Mode
- 方法:
drawRGB(int r, int g, int b)
同上
canvas.drawLine(s)
-
方法1:
drawLine(float startX, float startY, float stopX, float stopY, Paint paint)
對(duì)參數(shù)的解釋:
- 前四個(gè)參數(shù)為直線的起點(diǎn)和終點(diǎn)的 XY 軸坐標(biāo)
-
方法2:
drawLines(float[] pts,Paint paint)
-
方法3:
drawLines(float[] pts, int offset, int count, Paint paint)
對(duì)參數(shù)的解釋?zhuān)?br>
- pts:待畫(huà)的坐標(biāo)點(diǎn)數(shù)組,格式為(x1,y1,x2,y2,...),至少4個(gè)值
- offset:要跳過(guò)坐標(biāo)點(diǎn)數(shù)組中幾個(gè)值才開(kāi)始畫(huà)(必須是4的倍數(shù))
- count:至少為2,offset 之后數(shù)組的大小。
- 例子
-
方法1:
mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); mPaint.setTextSize(16); canvas.drawText("起點(diǎn)(20,100)", 22, 100, mPaint); canvas.drawText("終點(diǎn)(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個(gè)值,即能夠繪制一條直線 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個(gè)點(diǎn) canvas.drawLines(points,4,8,mPaint);
canvas.drawRect
-
方法1:
void drawRect(float left, float top, float right, float bottom, Paint paint)
確定矩形四個(gè)頂點(diǎn)的位置配上畫(huà)筆即可
-
方法2:
void drawRect(Rect r, Paint paint)
矩形的四個(gè)位置為整型時(shí)使用
-
方法3:
void drawRect(RectF r, Paint paint)
方法1的另一簡(jiǎn)版,矩形的四個(gè)位置為浮點(diǎn)型時(shí)使用
-
例子:
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
繪制橢圓
類(lèi)似 drawRect
canvas.drawPaint
方法:
drawPaint(Paint paint)
自定義的 paint
畫(huà)在整個(gè)畫(huà)布上,等于用 paint
在畫(huà)布上畫(huà)一個(gè)無(wú)限大的矩形,但當(dāng)前畫(huà)布受到剪裁,則染色區(qū)域僅限于剪裁部分。
canvas.drawPoint(s)
繪制點(diǎn),方法基本類(lèi)似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)
對(duì)參數(shù)的解釋?zhuān)?br> - rx,ry 表示 left 到 left+rx 與 left 到 left+ry 所圍區(qū)域做弧,其余三個(gè)角類(lèi)似,當(dāng) rx=ry>=(right-left)/2 時(shí)表示畫(huà)一個(gè)半徑為 rx(ry) 的圓(剛好外接一個(gè)矩形)
- 例子
-
方法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 位置開(kāi)始畫(huà) text
注意:其中 y 表示文字的基線(baseline )所在的坐標(biāo),說(shuō)白了就是我們小學(xué)寫(xiě)字用的那種帶有橫線的本子(一般都是按照一條基線來(lái)寫(xiě)字是吧?),用于規(guī)范你寫(xiě)的字是否成一條直線,否則很多人寫(xiě)著寫(xiě)著就往上飄了。而 x 坐標(biāo)就是文字繪制的起始水平坐標(biāo),但是每個(gè)文字本身兩側(cè)都有一定的間隙,故實(shí)際文字的位置會(huì)比 x 的位置再偏右側(cè)一些。
-
圖:
基線類(lèi)似下圖深綠色的橫線
image -
方法2:
drawText(CharSequence text, int start, int end, float x, float y, Paint paint)
在 x,y 位置上畫(huà)出 start 到 end(不含 end) 之間的字符 CharSequence charSequence = "charSequence";
-
方法3:
drawText(char[] text, int index, int count, float x, float y, Paint paint)
對(duì)參數(shù)的解釋:
- index:表示從第幾個(gè)字符開(kāi)始,
- count:表示截取的數(shù)組長(zhǎng)度
- 字符數(shù)組的定義: char[] a="abc".toCharArray()
-
方法4:
drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint)
對(duì)參數(shù)的解釋:
- path:文本繪制的路徑(關(guān)鍵)
- hOffset:相對(duì)于路徑的水平偏移量
- vOffset:相對(duì)于路徑的垂直偏移量
-
方法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)
對(duì)參數(shù)的解釋?zhuān)?br>
- contextStart:可選,直接=start
- contextEnd:可選,直接=end
- x,y:文字繪制起點(diǎn)
- 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學(xué)習(xí)",50,100,mPaint);
-
方法2:
mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); mPaint.setTextSize(20); CharSequence charSequence="Canvas學(xué)習(xí)"; canvas.drawText(charSequence,1,charSequence.length(),30,50,mPaint);
-
方法3:
mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); mPaint.setTextSize(20); char[] chars="Canvas學(xué)習(xí)".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學(xué)習(xí)"; path.addCircle(100,100,50, Path.Direction.CCW); canvas.drawTextOnPath(text,path,0f,0f,mPaint);
canvas.drawPath
方法:drawPath(Path path, Paint paint)
根據(jù)定義的路徑畫(huà)出圖
例子:
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
Path path=new Path();
path.addCircle(100,100,50, Path.Direction.CCW);
canvas.drawPath(path,mPaint);
效果等于畫(huà)一個(gè)圓
canvas.clipxxx系列
canvas.clipPath
-
方法1:
clipPath(Path path)
按所定義的路線剪裁,默認(rèn)Region.Op.INTERSECT表示剪裁出相交的部分
-
方法2:
clipPath(Path path, Region.Op op)
解釋?zhuān)河弥付ǖ穆窂?path 修改當(dāng)前的剪裁
對(duì)op參數(shù)的理解:以剪裁兩次的區(qū)域分別為A,B來(lái)區(qū)別
- Region.Op.DIFFERENCE:剪裁出差異的部分,類(lèi)似 A-B 部分
- Region.Op.REPLACE:后剪裁B的覆蓋剪裁的A
- Region.Op.REVERSE_DEFFERENCE:剪裁出差異的部分,類(lèi)似 B-A 部分
- Region.Op.INTERSECT:剪裁出相交的部分,類(lèi)似 A交B 部分
- Region.Op.UNION:剪裁出AB合并的部分,類(lèi)似** AUB**
- Region.Op.XOR:是** (AUB)-(A交B)** 剛好與** A交B** 相對(duì)
-
方法3:
clipRect(Rect[F] rect, Region.Op op)[]表示可選
解釋?zhuān)河弥付ǖ木匦蝸?lái)修改當(dāng)前的剪裁
-
方法4:
clipRect(Rect rect)
剪裁一個(gè)矩形區(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 方法剪裁模式默認(rèn)為Region.Op.INTERSECT,故當(dāng)前剪裁部分和整個(gè)畫(huà)布相交即為本身。
-
剪裁模式(綠色區(qū)域?yàn)樽罱K得到的剪裁部分)
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的保存與恢復(fù)
- 解釋?zhuān)河脕?lái)保存或恢復(fù)
Canvas
的狀態(tài) - 作用:
save
之后可以調(diào)用Canvas
的平移、放縮、旋轉(zhuǎn)、錯(cuò)切、裁剪等對(duì)當(dāng)前畫(huà)布進(jìn)行操作,再進(jìn)行相應(yīng)的繪制,避免影響畫(huà)布上已繪制的view
,配合canvas.restore()
(將當(dāng)前畫(huà)布恢復(fù)到初始狀態(tài)) 使用
canvas的變幻操作
canvas.translate
-
方法:
canvas.translate(float dx, float dy)
作用:移動(dòng)當(dāng)前畫(huà)布水平距離 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ù),畫(huà)布縮放后,再畫(huà)出的圖片相應(yīng)的坐標(biāo)都會(huì)進(jìn)行縮放
-
例子:
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)
作用:縮放畫(huà)布并平移畫(huà)布到基準(zhǔn)點(diǎn) (px,py)
對(duì)參數(shù)的解釋:
- px,py 為縮放后畫(huà)布新的坐標(biāo)原點(diǎn)(也叫縮放基準(zhǔn)點(diǎn))
-
例子:
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)
作用:順時(shí)針旋轉(zhuǎn)當(dāng)前畫(huà)布一定角度,也可加入基準(zhǔn)點(diǎn)坐標(biāo)
-
例子:
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();
-
無(wú)基準(zhǔn)點(diǎn)
image -
有基準(zhǔn)點(diǎn) (200,200)
image
canvas.skew
-
方法:
canvas.skew(float sx, float sy)
作用:畫(huà)布的錯(cuò)切
- sx:將畫(huà)布在 x 方向上傾斜相應(yīng)的角度,sx 為傾斜角度的 tan 值;
-
sy:將畫(huà)布在 y 軸方向上傾斜相應(yīng)的角度,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);//畫(huà)布X軸傾斜45度 mPaint.setColor(Color.RED); canvas.drawRect(new RectF(0,0,180,180),mPaint); canvas.restore();
總結(jié)
以上大致介紹了 Canvas 類(lèi)中眾多繪制方法。首先,先對(duì)方法進(jìn)行解析;其次,給出相應(yīng)的示例代碼并結(jié)合運(yùn)行效果,旨在幫助讀者更好地理解諸如上述繪制方法的基本使用;最后,對(duì)于方法的理解如有紕漏,歡迎指正。