其實(shí)這邊并不需要你記住太多,能有知道有這樣的作用,讓后不會(huì)使用的時(shí)候當(dāng)工具來(lái)查看就看。主要是需要能靈活運(yùn)用這些。下面我來(lái)介紹一下:
1. 繪畫(huà)基礎(chǔ)
這篇文章很基礎(chǔ),羅列一下文章中提到的繪制方法。對(duì)于一些重點(diǎn)和大家不太了解的,我會(huì)斜體加粗。
Paint 類(lèi)的幾個(gè)最常用的方法。具體是:
- 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è)置抗鋸齒開(kāi)關(guān)
- Paint.setStrokeCap(cap) 設(shè)置點(diǎn)的形狀
Canvas 類(lèi)下相關(guān)的方法:
- Canvas.drawColor(@ColorInt int color) ,Canvas.drawRGB(int r, int g, int b) ,Canvas.drawARGB(int a, int r, int g, int b) 顏色填充
- Canvas.drawCircle(float centerX, float centerY, float radius, Paint paint) 畫(huà)圓
- Canvas.drawRect(float left, float top, float right, float bottom, Paint paint),Canvas.drawRect(RectF rect, Paint paint) ,Canvas.drawRect(Rect rect, Paint paint) 畫(huà)矩形
- Canvas.drawPoint(float x, float y, Paint paint) 畫(huà)點(diǎn)
- Canvas.drawPoints(float[] pts, int offset, int count, Paint paint),Canvas.drawPoints(float[] pts, Paint paint) 畫(huà)點(diǎn)(批量)
- Canvas.drawOval(float left, float top, float right, float bottom, Paint paint),Canvas.drawOval(RectF rect, Paint paint)畫(huà)橢圓
- Canvas.drawLine(float startX, float startY, float stopX, float stopY, Paint paint) 畫(huà)線
- Canvas.drawLines(float[] pts, int offset, int count, Paint paint),Canvas.drawLines(float[] pts, Paint paint) 畫(huà)線(批量)
- Canvas.drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint) 畫(huà)圓角矩形
- Canvas.drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint) 繪制弧形或扇形
- Canvas.drawBitmap(Bitmap bitmap, float left, float top, Paint paint),Canvas.drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint),Canvas.drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint), Canvas.drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)畫(huà) Bitmap
- Canvas.drawText(String text, float x, float y, Paint paint) 繪制文字
- Canvas.drawPath(Path path, Paint paint) 畫(huà)自定義圖形,一般復(fù)雜圖形都會(huì)用到這個(gè)。
Path 方法:
- Path.addCircle(float x, float y, float radius, Direction dir) 添加圓
- Path.addOval(float left, float top, float right, float bottom, Direction dir) / addOval(RectF oval, Direction dir) 添加橢圓
- Path.addRect(float left, float top, float right, float bottom, Direction dir) / addRect(RectF rect, Direction dir) 添加矩形
- Path.addRoundRect(RectF rect, float rx, float ry, Direction dir) / addRoundRect(float left, float top, float right, float bottom, float rx, float ry, Direction dir) / addRoundRect(RectF rect, float[] radii, Direction dir) / addRoundRect(float left, float top, float right, float bottom, float[] radii, Direction dir) 添加圓角矩形
- Path.addPath(Path path) 添加另一個(gè) Path
- Path.lineTo(float x, float y) / rLineTo(float x, float y) 畫(huà)直線,第二個(gè)是相對(duì)坐標(biāo)
- Path.quadTo(float x1, float y1, float x2, float y2) / rQuadTo(float dx1, float dy1, float dx2, float dy2) 畫(huà)二次貝塞爾曲線
- Path.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) 畫(huà)三次貝塞爾曲線
- Path.moveTo(float x, float y) / rMoveTo(float x, float y) 移動(dòng)到目標(biāo)位置
- Path.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) 畫(huà)弧形,forceMoveTo=true沒(méi)有痕跡,false有痕跡
- Path.addArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle) / addArc(RectF oval, float startAngle, float sweepAngle) 添加弧形
- Path.close() 封閉當(dāng)前子圖形
-
Path.setFillType(Path.FillType ft) 設(shè)置填充方式,WINGING(不考慮圖形繪制方向)交點(diǎn)偶數(shù)為外部,交點(diǎn)奇數(shù)為內(nèi)部,EVEN_ODD(考慮繪制圖形方向)交點(diǎn)為0是外部,交點(diǎn)不為零為內(nèi)部,內(nèi)部填充顏色。
自定義View繪制基礎(chǔ)
2. 詳解Paint
設(shè)置顏色:
setShader(Shader shader) 設(shè)置 著色器Shader,Shader一共有5種
LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode tile) 線性漸變,TileMode 一共有 3 個(gè)值可選: CLAMP, MIRROR 和 REPEAT
RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, TileMode tileMode) 輻射漸變
SweepGradient(float cx, float cy, int color0, int color1) 掃描漸變
BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)
ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode) 混合著色器setColorFilter(ColorFilter colorFilter) 為繪制設(shè)置顏色過(guò)濾,使用LightingColorFilter PorterDuffColorFilter 和 ColorMatrixColorFilter
LightingColorFilter(int mul, int add) mul用來(lái)和目標(biāo)像素相乘,add用來(lái)和目標(biāo)像素相加
PorterDuffColorFilter(int color, PorterDuff.Mode mode)
ColorMatrix(new float[]{}); 4*5的矩陣,具體效果自己網(wǎng)上查找-
setXfermode(Xfermode xfermode) 以繪制的內(nèi)容作為源圖像,以 View 中已有的內(nèi)容作為目標(biāo)圖像,選取一個(gè) PorterDuff.Mode 作為繪制內(nèi)容的顏色處理方案
使用離屏緩存(View自身的底并不是透明,結(jié)合后容易出現(xiàn)周邊黑色的問(wèn)題)
控制它的透明區(qū)域不要太小,要讓它足夠覆蓋到要和它結(jié)合繪制的內(nèi)容
SRC源圖像和DST目標(biāo)圖像.png


效果:
- setAntiAlias (boolean aa) 設(shè)置抗鋸齒
- setStyle(Paint.Style style) 設(shè)置圖形是線條風(fēng)格還是填充風(fēng)格的(也可以二者并用)
- setStrokeWidth(float width) 設(shè)置線條寬度。
- setStrokeCap(Paint.Cap cap) 設(shè)置線頭的形狀
- setStrokeJoin(Paint.Join join) 設(shè)置拐角的形狀,MITER 尖角、 BEVEL 平角和 ROUND 圓角
- setStrokeMiter(float miter) setStrokeJoin() 的一個(gè)補(bǔ)充,它用于設(shè)置 MITER 型拐角的延長(zhǎng)線的最大值。
- setDither(boolean dither) 設(shè)置圖像的抖動(dòng)
- setFilterBitmap(boolean filter) 設(shè)置是否使用雙線性過(guò)濾來(lái)繪制 Bitmap
- setPathEffect(PathEffect effect) PathEffect 分為兩類(lèi),
單一效果的CornerPathEffect(float radius)(把所有拐角變成圓角) ,
DiscretePathEffect(float segmentLength, float deviation)(把線條進(jìn)行隨機(jī)的偏離) ,
DashPathEffect(float[] intervals, float phase)(使用虛線來(lái)繪制線條),
PathDashPathEffect(Path shape, float advance, float phase, PathDashPathEffect.Style style)(它是使用一個(gè) Path 來(lái)繪制「虛線」) ,
組合效果的 SumPathEffect(不同效果,畫(huà)多次)
ComposePathEffect(不同效果先組合,畫(huà)一次)。 - setShadowLayer(float radius, float dx, float dy, int shadowColor) 在之后的繪制內(nèi)容下面加一層陰影
- setMaskFilter(MaskFilter maskfilter) 設(shè)置的是在繪制層上方的附加效果
BlurMaskFilter(float radius, BlurMaskFilter.Blur style) 模糊效果的 MaskFilter。Blur style:NORMAL(內(nèi)外都模糊繪制),SOLID(內(nèi)部正常繪制,外部模糊),INNER(內(nèi)部模糊,外部不繪制),OUTER(內(nèi)部不繪制,外部模糊) - EmbossMaskFilter(float[] direction, float ambient, float specular, float blurRadius) 浮雕效果的 MaskFilter。
- getFillPath(Path src, Path dst)
- getTextPath(String text, int start, int end, float x, float y, Path path) / getTextPath(char[] text, int index, int count, float x, float y, Path path)
初始化
- reset()
- set(Paint src)
- setFlags(int flags)
自定義view Paint 詳解
3.文字的繪制
Canvas 繪制文字的方式
- drawText(String text, float x, float y, Paint paint) 參數(shù)x,y一個(gè)與左下角比較接近的位置,y是文字的基線
- drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) 沿著一條 Path 來(lái)繪制文字。hOffset 和 vOffset,分別是文字相對(duì)于 Path 的水平偏移量和豎直偏移量
- StaticLayout(CharSequence source, TextPaint paint, int width, Layout.Alignment align, float spacingmult, float spacingadd, boolean includepad)支持換行,它既可以為文字設(shè)置寬度上限來(lái)讓文字自動(dòng)換行,也會(huì)在 \n 處主動(dòng)換行。
width 是文字區(qū)域的寬度,文字到達(dá)這個(gè)寬度后就會(huì)自動(dòng)換行;
align 是文字的對(duì)齊方向;
spacingmult 是行間距的倍數(shù),通常情況下填 1 就好;
spacingadd 是行間距的額外增加值,通常情況下填 0 就好;
includeadd 是指是否在文字上下添加額外的空間,來(lái)避免某些過(guò)高的字符的繪制出現(xiàn)越界
設(shè)置顯示效果類(lèi)
- setTextSize(float textSize) 設(shè)置文字大小
- setTypeface(Typeface typeface) 設(shè)置字體
- setFakeBoldText(boolean fakeBoldText) 是否使用偽粗體
- setStrikeThruText(boolean strikeThruText) 是否加刪除線
- setUnderlineText(boolean underlineText) 是否加下劃線
- setTextSkewX(float skewX) 設(shè)置文字橫向錯(cuò)切角度。
- setTextScaleX(float scaleX) 設(shè)置文字橫向放縮
- setLetterSpacing(float letterSpacing) 設(shè)置字符間距
- setFontFeatureSettings(String settings) 用 CSS 的 font-feature-settings 的方式來(lái)設(shè)置文字。
- setTextAlign(Paint.Align align) 設(shè)置文字的對(duì)齊方式。一共有三個(gè)值:LEFT CETNER 和 RIGHT。默認(rèn)值為 LEFT。
- setTextLocale(Locale locale) / setTextLocales(LocaleList locales) 設(shè)置繪制所使用的 Locale,Locale.TAIWAN繁體中文
測(cè)量文字尺寸類(lèi)
- float getFontSpacing() 獲取推薦的行距,即推薦的兩行文字的 baseline 的距離
- getFontMetrics(FontMetrics fontMetrics) 這種用法在需要頻繁獲取 FontMetrics 的時(shí)候性能會(huì)好些,能獲得ascent, descent, top, bottom, leading。使用Paint.getFontMetrics()的方式進(jìn)行文字居中,使? (fontMetrics.ascend + fontMetrics.descend) / 2,沒(méi)有g(shù)etTextBounds方式的絕對(duì)居中,但是當(dāng)需要居中的文字變化時(shí)不會(huì)像getTextBounds方式一樣跳動(dòng)。
各種線.png
ascent : 該距離是從所繪字符的baseline之上至該字符所繪制的最高點(diǎn)。
descent: 該距離是從所繪字符的baseline之下至該字符所繪制的最低點(diǎn)。
top: 該距離是從所繪字符的baseline之上至可繪制區(qū)域的最高點(diǎn)。
bottom: 該距離是從所繪字符的baseline之下至可繪制區(qū)域的最低點(diǎn)。
leading: 為文本的線之間添加額外的空間,這是官方文檔直譯,debug時(shí)發(fā)現(xiàn)一般都為0.0,該值也是系統(tǒng)推薦的。
特別注意: ascent和top都是負(fù)值,而descent和bottom:都是正值,這些值都是基于baseline為坐標(biāo)0的相對(duì)值。
getTextBounds(String text, int start, int end, Rect bounds) 獲取文字的顯示范圍。用這種方式做文字居中的縱向測(cè)量,使? (bounds.top + bounds.bottom) / 2,會(huì)使文字非常居中,但是如果是輸入的形式,到導(dǎo)致bounds.top和bounds.bottom值發(fā)生變化。
float measureText(String text) 測(cè)量文字的寬度并返回
getTextWidths(String text, float[] widths) 獲取字符串中每個(gè)字符的寬度,并把結(jié)果填入?yún)?shù) widths。
int breakText(String text, boolean measureForwards, float maxWidth, float[] measuredWidth) breakText() 是在給出寬度上限的前提下測(cè)量文字的寬度,返回值是截取的文字個(gè)數(shù)。參數(shù)中, text 是要測(cè)量的文字;measureForwards 表示文字的測(cè)量方向,true 表示由左往右測(cè)量;maxWidth 是給出的寬度上限;measuredWidth 是用于接受數(shù)據(jù),而不是用于提供數(shù)據(jù)的:方法測(cè)量完成后會(huì)把截取的文字寬度(如果寬度沒(méi)有超限,則為文字總寬度)賦值給 measuredWidth[0]。
getRunAdvance(CharSequence text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset) 對(duì)于一段文字,計(jì)算出某個(gè)字符處光標(biāo)的 x 坐標(biāo)。start end 是文字的起始和結(jié)束坐標(biāo);contextStart contextEnd 是上下文的起始和結(jié)束坐標(biāo);isRtl 是文字的方向;offset 是字?jǐn)?shù)的偏移,即計(jì)算第幾個(gè)字符處的光標(biāo)。
getOffsetForAdvance(CharSequence text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float advance) 給出一個(gè)位置的像素值,計(jì)算出文字中最接近這個(gè)位置的字符偏移量(即第幾個(gè)字符最接近這個(gè)坐標(biāo))。
檢查指定的字符串中是否是一個(gè)單獨(dú)的字形 (glyph)
自定義 View 1-3 drawText() 文字的繪制
4.Canvas 對(duì)繪制的輔助
范圍裁切
- clipRect() 裁剪矩形,記得要加上 Canvas.save() 和 Canvas.restore() 來(lái)及時(shí)恢復(fù)繪制范圍
- clipPath() 裁剪Path范圍形狀
幾何變換
使用 Canvas 來(lái)做常見(jiàn)的二維變換;
使用 Matrix 來(lái)做常見(jiàn)和不常見(jiàn)的二維變換;
使用 Camera 來(lái)做三維變換。
- Canvas.translate(float dx, float dy) 平移,參數(shù)里的 dx 和 dy 表示橫向和縱向的位移。
- Canvas.rotate(float degrees, float px, float py) 旋轉(zhuǎn),參數(shù)里的 degrees 是旋轉(zhuǎn)角度,單位是度(也就是一周有 360° 的那個(gè)單位),方向是順時(shí)針為正向; px 和 py 是軸心的位置
- Canvas.scale(float sx, float sy, float px, float py) 放縮,參數(shù)里的 sx sy 是橫向和縱向的放縮倍數(shù); px py 是放縮的軸心
- Canvas.skew(float sx, float sy) 錯(cuò)切,參數(shù)里的 sx 和 sy 是 x 方向和 y 方向的錯(cuò)切系數(shù)。
- 使用 Matrix 來(lái)做變換
- 創(chuàng)建 Matrix 對(duì)象;
- 調(diào)用 Matrix 的pre/postTranslate/Rotate/Scale/Skew() 方法來(lái)設(shè)置幾何變換;
- 使用 Canvas.setMatrix(matrix) 或 Canvas.concat(matrix) 來(lái)把幾何變換應(yīng)用到 Canvas。
- Matrix.setPolyToPoly(float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount) 用點(diǎn)對(duì)點(diǎn)映射的方式設(shè)置變換
- Camera.rotate*() 三維旋轉(zhuǎn)
- Camera.translate(float x, float y, float z) 移動(dòng)
- Camera.setLocation(x, y, z) 設(shè)置虛擬相機(jī)的位置
Canvas 的幾何變換方法參照的是 View 的坐標(biāo)系,而繪制方法(drawXxx())參照的是Canvas 自己的坐標(biāo)系。
Canvas 的變換方法多次調(diào)用的時(shí)候,由于 Canvas 的坐標(biāo)系會(huì)整體被變換,因此當(dāng)平移、旋轉(zhuǎn)、放縮、錯(cuò)切等變換多重存在的時(shí)候, Canvas 的變換參數(shù)會(huì)非常難以計(jì)算,因此可以改用倒序的理解方式
Canvas 對(duì)繪制的輔助 clipXXX() 和 Matrix
5.繪制順序
super.onDraw() 前 or 后
根據(jù)自己的需求判斷是否將自己繪制的內(nèi)容放前或者后。
dispatchDraw():繪制子 View 的方法
Android 的繪制順序:在繪制過(guò)程中,每一個(gè) ViewGroup 會(huì)先調(diào)用自己的 onDraw() 來(lái)繪制完自己的主體之后再去繪制它的子 View。就是ViewGroup會(huì)先調(diào)用onDraw,然后調(diào)用dispatchDraw。
繪制過(guò)程簡(jiǎn)述
- 背景
- 主體(onDraw())
- 子 View(dispatchDraw())
- 滑動(dòng)邊緣漸變和滑動(dòng)條
- 前景

onDrawForeground()
在 onDrawForeground() 中,會(huì)依次繪制滑動(dòng)邊緣漸變、滑動(dòng)條和前景。如果你把繪制代碼寫(xiě)在了 super.onDrawForeground() 的下面,繪制代碼會(huì)在滑動(dòng)邊緣漸變、滑動(dòng)條和前景之后被執(zhí)行,那么繪制內(nèi)容將會(huì)蓋住滑動(dòng)邊緣漸變、滑動(dòng)條和前景。寫(xiě)在了 super.onDrawForeground() 的上面,則滑動(dòng)邊緣漸變、滑動(dòng)條和前景將會(huì)蓋住繪制內(nèi)容。
draw() 總調(diào)度方法
draw() 是繪制過(guò)程的總調(diào)度方法。一個(gè) View 的整個(gè)繪制過(guò)程都發(fā)生在 draw() 方法里。前面講到的背景、主體、子 View 、滑動(dòng)相關(guān)以及前景的繪制,它們其實(shí)都是在 draw() 方法里的。


如何成為自定義高手(一)繪制
如何成為自定義高手(二)動(dòng)畫(huà)
如何成為自定義高手(三)布局
如何成為自定義高手(四)觸摸反饋,事件分發(fā)機(jī)制
如何成為自定義高手(五)多點(diǎn)觸摸
如何成為自定義高手(六)滑動(dòng)和拖拽
如何成為自定義高手(七)滑動(dòng)沖突

