首圖
前言1. 文章內容這篇文章分為下面 5 個部分。
繪圖基礎
這一節會介紹 Android 中的畫筆 Paint 和畫布 Canvas 的用法。
路徑繪製
這一節會介紹 Android 中路徑 Path 的用法,包括直線、弧線和雷達圖等圖形的繪製方法。
文字繪製
這一節會介紹 Android 中繪製文字的方法,包括粗體、斜體、加載字體等。
區域操作
這一節會介紹 Android 中區域 Region 的用法,包括區域裁剪、區域合併等操作。
畫布進階
這一節會對畫布的用法進行更詳細的介紹,包括保存、指向恢復和圓形頭像。
2. 注意事項對象創建
下面的示例代碼會在 onDraw() 方法中創建 Paint、Path、Rect 和 Region等對象,這在實際開發中是禁止的。
因為當 View 需要重繪時會調用 onDraw() 函數,每一次調用 onDraw() 函數都會重新創建這些對象。
這樣會引起頻繁的 GC,嚴重時會導致 App 卡頓。
圖片清晰度
點擊文章中的圖片可以查看原圖。
示例代碼
文中的示例代碼源碼在文章最下方可以找到。
1. 繪圖基礎本節包含內容如下。
1.1 畫筆用法我們這一節來看一下畫筆的用法。
本節包含內容如下。
1.1.1 設置畫筆顏色畫筆顏色.png
Paint 的 setColor(int color) 方法可用於設置畫筆顏色,下面是 color 參數的取值。
一種顏色是由紅綠藍三色合成的,所以 color 只能取 8 位 0xAARRGGBB 樣式顏色值。
透明度
A 表示透明度 Alpha,取值範圍是 0~255,值越小,圖像越透明
紅色
R 表示紅色 Red,取值範圍是 0~255,取值越小紅色越少
綠色
G 表示綠色 Green,取值範圍是 0~255,取值越小綠色越少
藍色
B 表示藍色 Blue,取值範圍是 0~255,取值越小藍色越少
除了手動組合顏色,系統還提供了一個用於解析顏色的類 Color,關於 Color 在後面會有更詳細的介紹。
1.1.2 設置畫筆樣式畫筆樣式.png
Paint 的 setStyle(Style style) 方法可用於設置畫筆樣式,下面是 style 參數的取值。
1.1.3 設置畫筆寬度Paint 的 setStrokeWidth(width) 方法用於設置描邊寬度,單位是 px。
1.1.4 設置畫筆鋸齒畫筆 Paint 繪製圖形時默認不是抗鋸齒的,也就是邊邊會有鋸齒。
Paint 提供了 setAntiAlias() 方法,這個方法可以開啟畫筆的抗鋸齒功能。
下面是兩個放大後的圓,右邊的圓用的是抗鋸齒的畫筆繪製的。
抗鋸齒.png
1.2 畫布用法上一節演示畫筆的同時也演示了畫布繪製圓的方法,這一節我們來看一下畫布的其他方法。
本節內容如下。
1.2.1 設置背景Canvas 提供了三個設置背景的方法。
drawColor(int color)
drawARGB(int a, int r, int g, int b)
drawRGB(int r, int g, int b)
需要注意的是,設置畫布背景要在其他圖形繪製前設置,否則設置好的背景色會覆蓋原有的圖形。
畫布背景.png
1.2.2 繪製直線繪製直線.png
Canvas 的 drawLine() 方法可以繪製直線,直線的粗細取決於 Paint 的 setStrokeWith(width) 中傳入的寬度。
繪製直線需要注意的是,只有當 Style 是 STROKE、FILL_AND_STROKE 時繪製才有效。
drawLine(float startX, float starY, float stopX, float stopY, Paint paint)
1.2.3 繪製點繪製點.png
Canvas 的 drawPoint() 方法可以用於繪製點,點的大小取決於 setStrokeWith(width) 中傳入的寬度。
1.2.4 矩形結構矩形結構在繪製矩形區域的時候需要用到。
Android 提供了 Rect 和 RectF 類用於存儲矩形數據結構,下面是 Rect 和 RectF 的構造函數。
Rect 構造函數.png
Rect 和 RectF 在於存儲的數據類型不同。
Rect
用於保存 int 類型數值的矩形結構
RectF
用於保存 float 類型數值的矩形結構
1.2.5 繪製矩形繪製矩形.png
Canvas 的 drawPoint() 和 drawRect() 方法都可用於繪製矩形。
下面是這兩個方法的區別。
形狀
樣式
drawPoint()
只能是填充樣式。
drawRect()
可以自己選擇樣式,可以是描邊也可以是填充。
下面是 Canvas 中提供用於繪製矩形的三個方法。
drawRect(float left, float top, float right, float bottom, Paint paint)
drawRect(RectF rect, Paint paint)
drawRect(Rect r, Paint paint)
1.2.6 圓角矩形Canvas 中提供了一個 drawRoundRect 方法用於繪製圓角矩形,圓角矩形的四個角是橢圓的一角,下面是它的基本用法。
圓角矩形.png
1.3 顏色構造Color 類是 Android 中與顏色有關的類。
本節內容如下。
1.3.1 顏色常量Color 中定義了下面的顏色常量值。
Color 常量.png
1.3.2 構造顏色除了顏色常量外,Color 還提供了一些構造顏色的方法。
構造顏色
alpha << 就是進 24 位,當我們調用 Color.argb(255, 0, 0,0 0) 時,轉換後的 16 進位顏色值就是 0xFF000000。
進位.png
2. 路徑繪製路徑 Path 指的是畫筆畫出來的一條不間斷的曲線,本節會講解直線和弧線路徑,除了這兩種方式外還有很多方法可以實現很多效果。
drawPath 是 Canvas 繪製路徑的方法。
本節內容如下。
直線路徑
弧線路徑
添加路徑
填充路徑
重置路徑
雷達路徑
2.1 直線路徑繪製直線路徑.png
上面是一段繪製直線路徑的代碼,這裡需要注意的是,上面代碼中的畫筆樣式設為描邊,默認是填充的。
也就是在默認情況下,多條線相連形成閉環後,中間的區域會被填充。
繪製直線路徑涉及:起點、終點和閉環。
起點
Path 的 moveTo() 用於指定直線路徑的起點,參數 x1 和 y1 是起點坐標值。
終點
Path 的 lineTo() 用於指定直線路徑的起點,參數 x2 和 y2 是終點坐標值,也是下一次繪製直線的起點。
閉環
Path 的 close() 方法用於形成閉環。
如果連續畫了幾條直線,但沒有形成閉環,調用 close() 函數會把路徑首尾連接起來形成閉環,相當於是幫我們畫多一條直線。
如果只畫了一條直線,那 close() 方法是不會起作用的。
2.2 弧線路徑Path 的 arcTo() 方法可用於繪製弧線,弧線在這裡指的是橢圓上截取的一部分。
本節內容如下。
2.2.1 弧線樣式這裡的橢圓底色默認是不存在的,在這裡畫出來主要是為了突出弧線。
在這裡我們要注意的是,弧線默認是填充的,更準確的來說 drawArc() 方法是切出橢圓中的一塊。
如果我們只想要一條線的話,就要自己設置描邊樣式和描邊寬度。
弧線樣式.png
2.2.2 弧線起點除了樣式以外,繪製弧線要注意的另外一點就是起點。
弧線起點.png
2.2.3 弧線角度arcTo 中有兩個跟角度有關的參數 startAngle 和 sweepAngle,這兩個參數分別代表起始角和掃描角。
起始角
起始角指定弧線從哪裡開始畫起。
掃描角
掃描角可以看成是弧線的長度。
弧線角度.png
2.2.4 弧線函數arcTo.png
Path 中定義了的三個 arcTo() 函數,下面是 arcTo() 函數中主要四個參數的含義。
2.3 添加路徑本節內容如下。
2.3.1 路徑添加方法Path 提供了 addXXX 函數用於添加路徑,添加的路徑可以是不連續的,還可以是曲線。
下面是 Path 中提供的一些添加路徑的方法。
添加路徑函數.png
下面是添加路徑的示例。
添加路徑.png
2.3.2 路徑繪製方向在添加路徑的函數中有一些函數有 Direction 參數,這個 Direction 就是繪製的方向。
方向分為順時針逆時針 CCW(Counter-Clockwise)。
Direction.png
下面是不同方向的繪製示例,這個示例中用到了 drawTextOnPath 方法,這個方法在講繪製文字的時候會有更詳細的介紹,現在我們先看繪製方向對繪製效果的影響。
繪製方向.png
2.4 填充路徑路徑 Path 的填充模式與畫筆 Paint 的填充模式不同,Path 的填充模式是指填充 Path 的哪部分。
在 Path 中有一個 FillType 枚舉類,其中定義了 4 種填充類型。
FillType.png
下面是這四種填充類型的示例。
路徑填充.png
2.5 重置路徑Android 提供了兩個重置路徑的方法,讓我們可以重複使用 Path 對象。
路徑 Path 一旦被重置,其中保存的所有路徑都會被清空,這樣就不需要重新創建一個 Path 對象了。
reset()
reset() 函數類似於新建一個路徑對象,除了填充類型 FillType 以外,Path 的所有數據都會被回收並重新分配。
rewind()
rewind() 函數會清除 FillType 和所有的路徑,保留內部數據結構,以便更快地重用。
比如我們需要重複繪製一類線段,它們的點和數量相等,使用 rewind() 函數可以保留裝載點數據的數據結構,效率更高。
要注意的是,只有重複繪製相同路徑時,這些數據結構才是可復用的。
2.6 雷達路徑本節包含如下內容。
創建畫筆
計算圓心
畫多邊形
畫網格線
畫數據圖
2.6.1 創建畫筆雷達初始化.png
2.6.2 計算圓心在控制項大小發生變化時,onSizeChanged() 會被回調並得到最新的控制項大小,所以我們需要重寫這個方法。
網狀路徑的大小佔當前控制項的 90%,所以半徑為:寬高最小值 ÷ 2 × 0.9。
然後根據中心點,分別繪製雷達網格、網格中線、數據圖。
雷達onDraw.png
2.6.3 畫多邊形drawPolygon 涉及到三角函數,下面是公式計算示意圖。
公式示意圖.png
下面是公式的具體實現。
畫多邊形.png
2.6.4 畫網格線畫網格線.png
2.6.5 畫數據圖畫數據.png
3. 文字繪製本文包含如下內容。
3.1 畫筆文字參數本節包含內容如下。
3.1.1 文字參數下面是幾個 Paint 中設置文字繪製效果的方法。
文字畫筆函數.png
3.1.2 文字樣式文字樣式.png
3.1.2 文字對齊我們可以通過 Paint 的 setTextAlign(align) 方法來設置繪製文字時的對齊方向。
Paint 中有一個枚舉類 Align,在 Align 中定義了三種對齊模式:LEFT、CENTER、RIGHT,這三種模式分別代表左對齊,居中對齊和右對齊。
文字對齊.png
3.1.4 文字變形文本變形.png
3.1.6 其他設置其他效果.png
3.2 畫布繪製文字上一節我們看到了Canvas 的 drawText() 方法,下面我們來看一下 Canvas 提供的其他繪製文本的方法。
本節包含內容如下。
3.2.1 逐字繪製文字逐字繪製.png
3.2.2 路徑繪製文字在講解路徑繪製方向時我們就已經用到過 Canvas 提供了 drawTextOnPath() 方法,在這裡我們看一下這個方法中的偏移參數。
路勁文字.png
3.3 設置字體樣式Paint 中提供了 setTypeFace(typeFace) 方法同於設置字體樣式。
TypeFace 是專門用於設置字體樣式的,我們可以指定系統提供的字體也可以指定自定義字體。
當創建 TypeFace 類時,可以指定是正常字體、斜體或者粗體,當指定樣式中沒有相關文字的樣式時,就會用系統默認的樣式顯示,一般默認是宋體。
本節包含如下內容。
3.3.1 系統字體TypeFace 中提供了下面三種字體,這三種字體對中文的支持不是很好,當遇到不支持的文字時,會用系統默認字體來寫。
TypeFace.png
下面是這三種字體的顯示效果。
系統字體.png
3.3.2 字體樣式除了可以選擇特定字體以外,我們還可以通過 defaultFromStyle() 方法選擇特定具體的某種樣式。
TypeFace 中還定義了下面四種字體樣式。
TypeFaceStyle.png
下面是這四種樣式的顯示效果。
字體樣式.png
3.3.3 加載字體Android 提供了下面三種加載自定義字體的方式。
1. 根據字體名加載
TypeFace 中提供了一個用於根據字體名加載字體的 create() 方法,比如下面這行代碼這樣。
由於模擬器上沒有別的字體,在這裡就不演示了,大家有興趣的可以自己嘗試下。
createFace.png
2. 根據資源名加載
假如我們從網上下了個 ttf 字體文件,想放在包中直接使用,我們可以在 app/src/main 目錄下建一個 assets 目錄,再建一個 fonts 目錄,然後把 ttf 文件放到裡面。
而根據資源名加載字體的方式就是 TypeFace 的 createFromAsset() 方法。
加載字體資源.png
3. 根據文件名加載字體
從資源中加載字體的弊端就是會讓 APK 包變大,如果我們把字體下載到本地的話就可以避免這個問題。
加載字體文件.png
3.4 獲取文字寬高Paint 提供了三種獲取文字寬高的方法,下面我們來看一下這幾個方法的用法。
本節內容如下。
3.4.1 獲取字體寬度文字寬度.png
3.4.2 獲取字體寬高文字寬高.png
4. 區域操作區域指的是 Region 類,Region 是一塊任意形狀的封閉圖形,這一節我們來看一下 Region 提供的一些操作區域的方法。
本節包含如下內容。
創建區域
合併區域
裁剪區域
其他操作
4.1 創建區域直接構造區域指的是用 Region 的構造函數創建區域,下面是 Region 的四個構造函數。
Region 構造函數.png
下面是一個繪製 Region 的簡單示例,由於 Canvas 中沒有繪製 Region 的方法,所以我們在這裡自己定義一個 drawRegion() 方法來繪製 Region。
Region 可用於繪製各種各樣的形狀,我們先看一下怎麼繪製一個簡單的正方形 Region。
直接構造區域.png
4.2 合併區域Region 提供了一個 union(rect) 函數,傳入目標矩陣到 union 函數中就能實現合併區域。
合併區域.png
4.3 裁剪區域Region 中聲明了下面的 5 個設置區域的方法。
設置區域方法.png
裁剪區域需要先創建一個空 Region,然後調用 setPath 方法,下面是裁剪區域的操作,橢圓路徑和裁剪區域相交的區域就是裁剪結果。
裁剪區域.png
4.4 其他操作本節如下內容。
1. 集合運算方法
除了 union 以外,Region 還提供了下面幾個區域操作方法,而且 union 本身也是調用了 op 方法。
op.png
2. 集合運算類型
在上面這些方法中比較重要的是 Op 參數,Op 是 Region 中定義的枚舉類,是集合運算操作。
在這裡,集合中的元素就是 Region 矩陣範圍中的子矩陣。
opEnum.png
**3. 集合運算示例 **
下面是這些集合運算的示例。
集合運算.png
5. 畫布上一節我們講了怎麼用畫布 Canvas 繪製各種圖形,畫布除了能用來繪製各種圖形以外,我們還能對畫筆進行變換和裁剪。
本節內容如下。
畫布平移
畫布合成
畫布裁剪
畫布保存
4.1 畫布平移畫布 Canvas 提供了一個可用於平移的方法 translate(),畫布的原始狀態是以左上角為圓點,向右是 X 軸正方向,向下是 Y 軸正方向。
由於畫布的左上角是坐標軸的原點(0, 0),所以平移畫布後,坐標系也會被平移。
平移後的畫筆的左上角是新的坐標原點。
畫布平移.png
4.2 畫布合成合成畫布與屏幕的操作是由系統進行的,這一節我們來看一下這個操作的簡單介紹。
本節內容如下。
4.2.1 合成我們每次在畫布上畫圖時,系統會先產生一個透明圖層,在這個圖層上畫圖,花完後再覆蓋在屏幕上。
合成
4.2.2 平移當我們繪製紅色矩形時,會產生另一個新的 Canvas 透明圖層,此時畫布坐標改變了,所以繪圖方式如下圖所示。
由於 Canvas 已經平移了 350 像素,所以畫圖時是以新原點來產生視圖的,然後再合成到屏幕上。
當屏幕移動後,有一部分超出屏幕的範圍,超出範圍的圖像是不顯示的。
再繪製.png
4.2.3 小結使用 Canvas 的繪製方法有下面三個需要注意的點。
生成新圖層
每次調用繪製方法 drawXXX 時,都會產生一個新的 Canvas 透明圖層。
操作不可逆
調用了繪製方法前,平移和旋轉等函數對 Canvas 進行了操作,那麼這個操作是不可逆的,每次產生的畫布的最新位置都是這些操作後的位置。
超出不顯示
在 Canvas 圖層與屏幕合成時,超出屏幕範圍的圖像是不會顯示出來的
4.3 畫布裁剪畫布裁剪是指用 Canvas 的 clipXXX 函數與矩形、路徑和區域取交、並、差等集合運算來獲得最新的畫布形狀。
除非使用了保存和恢復函數,否則裁剪操作是不可逆的,也就是一旦裁剪就無法恢復。
在 Canvas 中定義了下面幾個裁剪函數。
裁剪函數.png
下面是一個裁剪示例。
畫布裁剪.png
4.4 畫布保存在前面我們說到了畫布的操作是不可逆的,這會造成很多麻煩。
比如為了實現一些效果需要對畫布進行操作,但畫布狀態改變後又影響了後面的繪製效果。
因為這個原因,畫布提供了保存和恢復功能,這兩個功能對應的方法是 save() 和 restore(),每調用一次 save() 就會把當前畫布狀態保存到棧中。
本節內容如下。
4.4.1 多次保存畫布單次保存恢復的操作比較簡單,我們來看一下畫布多次保存和多次恢復的效果。
畫布多次保存.png
4.4.2 指向恢復指向恢復指的是恢復到特定的狀態,比如我們上面這個例子,假如我們要恢復到藍色400,那我們要調用三次 resoter()。
為了解決這個問題,Canvas 提供了另一個恢復畫布狀態的函數 restoreToCount(count),下面我們來看一下這個函數的用法。
指向恢復.png
4.4.3 圓形頭像下面我們來看一下怎麼用畫布的保存和恢復功能實現繪製圓形頭像後,再把畫布恢復回來。
本節包含如下內容。
1. 初始化
這裡之所以要禁用硬體加速,是因為硬體加速是使用的 OpenGL 函數完成實際繪製,而 OpenGL 並不能完全支持原始繪製函數,比如 clipPath() 在開啟硬體加速的情況下只有在 API 18 以上的系統才會生效。
圓形頭像初始化.png
2. 繪製頭像
圓形頭像.png
3. 去除鋸齒
clipPath 實現的圓形圖像是有鋸齒的,我們可以用 PorterDuffXfermode 來實現無鋸齒的圓形圖片。
混合模式.png
作者:燈不利多
連結:https://www.jianshu.com/p/9fb81d77f782
關注我獲取更多知識或者投稿