系列文章之 Android中自定義View(一)
系列文章之 Android中自定義View(二)
系列文章之 Android中自定義View(三)
系列文章之 Android中自定義View(四)
系列文章之 Android中自定義View(xml繪圖)
本文出自:
http://www.lxweimin.com/u/a1251e598483
我們在使用各種App時都會看到好多漂亮的效果,說實話有的效果真的很好看,所以覺得能寫出這些效果的人都好厲害的說,自定義View 在Android 進(jìn)階相關(guān)的圖書中都是必會內(nèi)容,我也一直看過大概的自定義View 的內(nèi)容,看過之后還是覺得不夠詳細(xì),上手還是抓瞎. 剛好網(wǎng)上 扔物線 大神 寫了一個自定義view 的詳細(xì)教程. http://hencoder.com/ui-1-1/ .如果想學(xué)習(xí)自定義View的同學(xué)請去 大神那里圍觀,本文是記錄自己學(xué)習(xí) 自定義View 的理解和收獲,也是一個記錄吧,等到用的時候比較容易找到.
我是分割線,下面開始本文內(nèi)容--------------------------
自定義View分為以下幾個部分
- Canvas 的 drawXXX() 系列方法及 Paint 最常見的使用
- Paint 的完全攻略
- Canvas 對繪制的輔助——范圍裁切和幾何變換。
- 使用不同的繪制方法來控制繪制順序
今天這篇就是第一部分:Canvas 的 drawXXX() 系列方法及 Paint 最常見的使用
Paint paint = new Paint();
protected void onDraw(Canvas canvas) {
super.onDraw(canvas); // 繪制一個圓
canvas.drawCircle(300, 300, 200, paint);
}
- Canvas 類下的所有 draw- 打頭的方法,例如 drawCircle() drawBitmap()。
- Paint 類的幾個最常用的方法。具體是:
- Paint.setStyle(Style style) 設(shè)置繪制模式
- Paint.setColor(int color) 設(shè)置顏色
- Paint.setStrokeWidth(float width) 設(shè)置線條寬度
- Paint.setTextSize(float textSize) 設(shè)置文字大小
- Paint.setAntiAlias(boolean aa) 設(shè)置抗鋸齒開關(guān)
上代碼
canvas 可以畫出好多好多東西,大家可以在google的官方文檔上查看所有方法 上鏈接
https://developer.android.google.cn/reference/android/graphics/Canvas.html
有時候吧也不太樂意看文檔,因為文檔上寫的真他媽的多,但是該看的時候也一定要看,上圖的各個方法都是可以點擊進(jìn)去的,方法的詳細(xì)介紹及使用都有.
如果你真的看的下去你就去看文檔吧,我絕不攔你,但是沒人一個一個方法點進(jìn)去看,因為常用的就那幾個,接下來我把常用的多個方法列出來給大家看看.
Canvas.drawColor(@ColorInt int color) 顏色填充
在整個繪制區(qū)域統(tǒng)一涂上指定的顏色。
例如 drawColor(Color.BLACK) 會把整個區(qū)域染成純黑色,覆蓋掉原有內(nèi)容; drawColor(Color.parse("#88880000") 會在原有的繪制效果上加一層半透明的紅色遮罩。
類似的方法還有 drawRGB(int r, int g, int b) 和 drawARGB(int a, int r, int g, int b) ,它們和 drawColor(color) 只是使用方式不同,作用都是一樣的。
drawCircle(float centerX, float centerY, float radius, Paint paint) 畫圓
前兩個參數(shù) centerX centerY 是圓心的坐標(biāo),第三個參數(shù) radius 是圓的半徑,單位都是像素,它們共同構(gòu)成了這個圓的基本信息(即用這幾個信息可以構(gòu)建出一個確定的圓);第四個參數(shù) paint,它提供基本信息之外的所有風(fēng)格信息,例如顏色、線條粗細(xì)、陰影等。
在 Android 里,每個 View 都有一個自己的坐標(biāo)系,彼此之間是不影響的。這個坐標(biāo)系的原點是 View 左上角的那個點;水平方向是 x 軸,右正左負(fù);豎直方向是 y 軸,下正上負(fù)(注意,是下正上負(fù),不是上正下負(fù),和上學(xué)時候?qū)W的坐標(biāo)系方向不一樣)。也就是下面這個樣子。
所以一個 View 的坐標(biāo) (x, y) 處,指的就是相對它的左上角那個點的水平方向 x 像素、豎直方向 y 像素的點。例如,(300, 300) 指的就是左上角的點向右 300 、向下 300 的位置; (100, -50) 指的就是左上角的點向右 100 、向上 50 的位置。
好了坐標(biāo)系插播結(jié)束,接著插播 paint 方法
插播一: Paint.setColor(int color)
例如,你要畫一個紅色的圓,并不是寫成 canvas.drawCircle(300, 300, 200, RED, paint)
這樣,而是像下面這樣:
paint.setColor(Color.RED); // 設(shè)置為紅色canvas.drawCircle(300, 300, 200, paint);
Paint.setColor(int color)是 Paint最常用的方法之一,用來設(shè)置繪制內(nèi)容的顏色。你不止可以用它畫紅色的圓,也可以用它來畫紅色的矩形、紅色的五角星、紅色的文字。
插播二: Paint.setStyle(Paint.Style style)
而如果你想畫的不是實心圓,而是空心圓(或者叫環(huán)形),也可以使用 paint.setStyle(Paint.Style.STROKE)
來把繪制模式改為畫線模式。
paint.setStyle(Paint.Style.STROKE); // Style 修改為畫線模式canvas.drawCircle(300, 300, 200, paint);
setStyle(Style style) 這個方法設(shè)置的是繪制的 Style 。Style 具體來說有三種: FILL, STROKE和 FILL_AND_STROKE。FILL是填充模式,STROKE是畫線模式(即勾邊模式),F(xiàn)ILL_AND_STROKE是兩種模式一并使用:既畫線又填充。它的默認(rèn)值是 FILL,填充模式。
插播三: Paint.setStrokeWidth(float width)
在 STROKE和 FILL_AND_STROKE下,還可以使用 paint.setStrokeWidth(float width)來設(shè)置線條的寬度:paint.setStyle(Paint.Style.STROKE);paint.setStrokeWidth(20); // 線條寬度為 20 像素canvas.drawCircle(300, 300, 200, paint);
插播四: 抗鋸齒
在繪制的時候,往往需要開啟抗鋸齒來讓圖形和文字的邊緣更加平滑。開啟抗鋸齒很簡單,只要在 new Paint()的時候加上一個 ANTI_ALIAS_FLAG參數(shù)就行:Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
另外,你也可以使用 Paint.setAntiAlias(boolean aa)來動態(tài)開關(guān)抗鋸齒。抗鋸齒的效果如下:
可以看出,沒有開啟抗鋸齒的時候,圖形會有毛片現(xiàn)象,啊不,毛邊現(xiàn)象。所以一定記得要打開抗鋸齒喲!
除了圓,Canvas 還可以繪制一些別的簡單圖形。它們的使用方法和 drawCircle() 大同小異,我就只對它們的 API 做簡單的介紹.
drawRect(float left, float top, float right, float bottom, Paint paint) 畫矩形
left, top, right, bottom 是矩形四條邊的坐標(biāo)。
另外,它還有兩個重載方法 drawRect(RectF rect, Paint paint) 和 drawRect(Rect rect, Paint paint) ,讓你可以直接填寫 RectF 或 Rect 對象來繪制矩形。
drawPoint(float x, float y, Paint paint) 畫點
x 和 y 是點的坐標(biāo)。點的大小可以通過 paint.setStrokeWidth(width) 來設(shè)置;點的形狀可以通過 paint.setStrokeCap(cap) 來設(shè)置:ROUND 畫出來是圓形的點,SQUARE 或 BUTT 畫出來是方形的點。
drawPoints(float[] pts, int offset, int count, Paint paint) / drawPoints(float[] pts, Paint paint) 畫點(批量)
同樣是畫點,它和 drawPoint() 的區(qū)別是可以畫多個點。pts 這個數(shù)組是點的坐標(biāo),每兩個成一對;offset 表示跳過數(shù)組的前幾個數(shù)再開始記坐標(biāo);count 表示一共要繪制幾個點
float[] points = {0, 0, 50, 50, 50, 100, 100, 50, 100, 100, 150, 50, 150, 100};
// 繪制四個點:(50, 50) (50, 100) (100, 50) (100, 100)
canvas.drawPoints(points, 2 /* 跳過兩個數(shù),即前兩個 0 /, 4 / 一共繪制四個點*/, paint);
drawOval(float left, float top, float right, float bottom, Paint paint) 畫橢圓
只能繪制橫著的或者豎著的橢圓,不能繪制斜的 left, top, right, bottom 是這個橢圓的左、上、右、下四個邊界點的坐標(biāo)。
另外,它還有一個重載方法 drawOval(RectF rect, Paint paint),讓你可以直接填寫 RectF 來繪制橢圓。
drawLine(float startX, float startY, float stopX, float stopY, Paint paint) 畫線
startX, startY, stopX, stopY 分別是線的起點和終點坐標(biāo)。
drawLines(float[] pts, int offset, int count, Paint paint) / drawLines(float[] pts, Paint paint) 畫線(批量)
drawLines() 是 drawLine() 的復(fù)數(shù)版。
float[] points2 = {100,500,300,500,100,800,500,800,100,500,100,800,300,500,500,800};
canvas.drawLines(points2,paint);
drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint) 畫圓角矩形
left, top, right, bottom 是四條邊的坐標(biāo),rx 和 ry 是圓角的橫向半徑和縱向半徑。
drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint) 繪制弧形或扇形
drawArc() 是使用一個橢圓來描述弧形的。left, top, right, bottom 描述的是這個弧形所在的橢圓;startAngle 是弧形的起始角度(x 軸的正向,即正右的方向,是 0 度的位置;順時針為正角度,逆時針為負(fù)角度),sweepAngle 是弧形劃過的角度;useCenter 表示是否連接到圓心,如果不連接到圓心,就是弧形,如果連接到圓心,就是扇形。
paint.setStyle(Paint.Style.FILL); // 填充模式
canvas.drawArc(200, 100, 800, 500, -110, 100, true, paint); // 繪制扇形
canvas.drawArc(200, 100, 800, 500, 20, 140, false, paint); // 繪制弧形
paint.setStyle(Paint.Style.STROKE); // 畫線模式
canvas.drawArc(200, 100, 800, 500, 180, 60, false, paint); // 繪制不封口的弧形
到此為止,以上就是 Canvas 所有的簡單圖形的繪制。除了簡單圖形的繪制, Canvas 還可以使用 drawPath(Path path) 來繪制自定義圖形。
drawPath(Path path, Paint paint) 畫自定義圖形
path.addArc(200, 1200, 400, 1400, 135, 225);
path.arcTo(400, 1200, 600, 1400, 180, 225, false);
path.lineTo(400, 1500);
canvas.drawPath(path, paint); //記得改一下畫筆的顏色和填充;
Path 可以描述直線、二次曲線、三次曲線、圓、橢圓、弧形、矩形、圓角矩形。把這些圖形結(jié)合起來,就可以描述出很多復(fù)雜的圖形。下面我就說一下具體的怎么把這些圖形描述出來。
Path 有兩類方法,一類是直接描述路徑的,另一類是輔助的設(shè)置或計算。
Path 方法第一類:直接描述路徑。
第一組: addXxx() ——添加子圖形
addCircle(float x, float y, float radius, Direction dir) 添加圓
x, y, radius 這三個參數(shù)是圓的基本信息,最后一個參數(shù) dir 是畫圓的路徑的方向。
順時針 (CW clockwise) 和逆時針 (CCW counter-clockwise)
第二組:xxxTo() ——畫線(直線或曲線)
這一組和第一組 addXxx() 方法的區(qū)別在于,第一組是添加的完整封閉圖形(除了 addPath() ),而這一組添加的只是一條線。
lineTo(float x, float y) / rLineTo(float x, float y) 畫直線
從當(dāng)前位置向目標(biāo)位置畫一條直線, x 和 y 是目標(biāo)位置的坐標(biāo)。這兩個方法的區(qū)別是,lineTo(x, y) 的參數(shù)是絕對坐標(biāo),而 rLineTo(x, y) 的參數(shù)是相對當(dāng)前位置的相對坐標(biāo) (前綴 r 指的就是 relatively 「相對地」)。
paint.setStyle(Style.STROKE);
path.lineTo(100, 100); // 由當(dāng)前位置 (0, 0) 向 (100, 100) 畫一條直線
path.rLineTo(100, 0); // 由當(dāng)前位置 (100, 100) 向正右方 100 像素的位置畫一條直線
quadTo(float x1, float y1, float x2, float y2) / rQuadTo(float dx1, float dy1, float dx2, float dy2) 畫二次貝塞爾曲線
這條二次貝塞爾曲線的起點就是當(dāng)前位置,而參數(shù)中的 x1, y1 和 x2, y2 則分別是控制點和終點的坐標(biāo)。和 rLineTo(x, y) 同理,rQuadTo(dx1, dy1, dx2, dy2) 的參數(shù)也是相對坐標(biāo)
具體什么是 貝塞爾曲線 請看這篇掃盲貼 http://www.html-js.com/article/1628
cubicTo(float x1, float y1, float x2, float y2, float x3, float y3) / rCubicTo(float x1, float y1, float x2, float y2, float x3, float y3) 畫三次貝塞爾曲線
和上面這個 quadTo() rQuadTo() 的二次貝塞爾曲線同理,cubicTo() 和 rCubicTo() 是三次別塞爾曲線。
moveTo(float x, float y) / rMoveTo(float x, float y) 移動到目標(biāo)位置
不論是直線還是貝塞爾曲線,都是以當(dāng)前位置作為起點,而不能指定起點。但你可以通過 moveTo(x, y) 或 rMoveTo() 來改變當(dāng)前位置,從而間接地設(shè)置這些方法的起點。
path.lineTo(100, 100); // 畫斜線
path.moveTo(200, 100); // 我移~~
path.lineTo(200, 0); // 畫豎線
moveTo(x, y) 雖然不添加圖形,但它會設(shè)置圖形的起點,所以它是非常重要的一個輔助方法。
另外,第二組還有兩個特殊的方法: arcTo() 和 addArc()。它們也是用來畫線的,但并不使用當(dāng)前位置作為弧線的起點。
arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo) / arcTo(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean forceMoveTo) / arcTo(RectF oval, float startAngle, float sweepAngle) 畫弧形
這個方法和 Canvas.drawArc() 比起來,少了一個參數(shù) useCenter,而多了一個參數(shù) forceMoveTo 。
少了 useCenter ,是因為 arcTo() 只用來畫弧形而不畫扇形,所以不再需要 useCenter 參數(shù);而多出來的這個 forceMoveTo 參數(shù)的意思是,繪制是要「抬一下筆移動過去」,還是「直接拖著筆過去」,區(qū)別在于是否留下移動的痕跡。
addArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle) / addArc(RectF oval, float startAngle, float sweepAngle)
又是一個弧形的方法。一個叫 arcTo ,一個叫 addArc(),都是弧形,區(qū)別在哪里?其實很簡單: addArc() 只是一個直接使用了 forceMoveTo = true 的簡化版 arcTo() 。
close() 封閉當(dāng)前子圖形
它的作用是把當(dāng)前的子圖形封閉,即由當(dāng)前位置向當(dāng)前子圖形的起點繪制一條直線。
paint.setStyle(Style.STROKE); path.moveTo(100, 100); path.lineTo(200, 100); path.lineTo(150, 150); path.close(); // 使用 close() 封閉子圖形。等價于 path.lineTo(100, 100)
close()和 lineTo(起點坐標(biāo))是完全等價的。
Path 方法第二類:輔助的設(shè)置或計算
Path.setFillType(Path.FillType ft) 設(shè)置填充方式
前面在說 dir 參數(shù)的時候提到, Path.setFillType(fillType) 是用來設(shè)置圖形自相交時的填充算法的:
方法中填入不同的 FillType 值,就會有不同的填充效果。FillType 的取值有四個:
- EVEN_ODD
- WINDING (默認(rèn)值)
- INVERSE_EVEN_ODD
- INVERSE_WINDING
EVEN_ODD 和 WINDING 的原理
即 even-odd rule (奇偶原則):對于平面中的任意一點,向任意方向射出一條射線,這條射線和圖形相交的次數(shù)(相交才算,相切不算哦)如果是奇數(shù),則這個點被認(rèn)為在圖形內(nèi)部,是要被涂色的區(qū)域;如果是偶數(shù),則這個點被認(rèn)為在圖形外部,是不被涂色的區(qū)域。還以左右相交的雙圓為例:
WINDING
即 non-zero winding rule (非零環(huán)繞數(shù)原則)
首先,它需要你圖形中的所有線條都是有繪制方向的:
然后,同樣是從平面中的點向任意方向射出一條射線,但計算規(guī)則不一樣:以 0 為初始值,對于射線和圖形的所有交點,遇到每個順時針的交點(圖形從射線的左邊向右穿過)把結(jié)果加 1,遇到每個逆時針的交點(圖形從射線的右邊向左穿過)把結(jié)果減 1,最終把所有的交點都算上,得到的結(jié)果如果不是 0,則認(rèn)為這個點在圖形內(nèi)部,是要被涂色的區(qū)域;如果是 0,則認(rèn)為這個點在圖形外部,是不被涂色的區(qū)域。
所以,完整版的 EVEN_ODD
和 WINDING
的效果應(yīng)該是這樣的:
而 INVERSE_EVEN_ODD和 INVERSE_WINDING,只是把這兩種效果進(jìn)行反轉(zhuǎn)而已。
drawBitmap(Bitmap bitmap, float left, float top, Paint paint) 畫 Bitmap
它的重載方法:
drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) / drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) / drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)
drawText(String text, float x, float y, Paint paint) 繪制文字
界面里所有的顯示內(nèi)容,都是繪制出來的,包括文字。 drawText() 這個方法就是用來繪制文字的。參數(shù) text 是用來繪制的字符串,x 和 y 是繪制的起點坐標(biāo)。
通過 Paint.setTextSize(textSize),可以設(shè)置文字的大小。
基本上 canvas的常用方法說完了,接下來就是Paint了.