安卓自定義 View 進階:Path 完結篇(偽)
一.Path常用方法表
為了相容性(偷懶) 本表格中去除了在API21(即安卓版本5.0)以上才新增的方法。忍不住吐槽一下,為啥看起來有些順手就能寫的過載方法要等到API21才新增上啊。寶寶此刻內心也是崩潰的。
作用 | 相關方法 | 備註 |
---|---|---|
移動起點 | moveTo | 移動下一次操作的起點位置 |
設定終點 | setLastPoint | 重置當前path中最後一個點位置,如果在繪製之前呼叫,效果和moveTo相同 |
連線直線 | lineTo | 新增上一個點到當前點之間的直線到Path |
閉合路徑 | close | 連線第一個點連線到最後一個點,形成一個閉合區域 |
新增內容 | addRect, addRoundRect, addOval, addCircle, addPath, addArc, arcTo | 新增(矩形, 圓角矩形, 橢圓, 圓, 路徑, 圓弧) 到當前Path (注意addArc和arcTo的區別) |
是否為空 | isEmpty | 判斷Path是否為空 |
是否為矩形 | isRect | 判斷path是否是一個矩形 |
替換路徑 | set | 用新的路徑替換到當前路徑所有內容 |
偏移路徑 | offset | 對當前路徑之前的操作進行偏移(不會影響之後的操作) |
貝塞爾曲線 | quadTo, cubicTo | 分別為二次和三次貝塞爾曲線的方法 |
rXxx方法 | rMoveTo, rLineTo, rQuadTo, rCubicTo | 不帶r的方法是基於原點的座標系(偏移量), rXxx方法是基於當前點座標系(偏移量) |
填充模式 | setFillType, getFillType, isInverseFillType, toggleInverseFillType | 設定,獲取,判斷和切換填充模式 |
提示方法 | incReserve | 提示Path還有多少個點等待加入(這個方法貌似會讓Path優化儲存結構) |
布林操作(API19) | op | 對兩個Path進行布林運算(即取交集、並集等操作) |
計算邊界 | computeBounds | 計算Path的邊界 |
重置路徑 | reset, rewind | 清除Path中的內容 reset不保留內部資料結構,但會保留FillType. rewind會保留內部的資料結構,但不保留FillType |
矩陣操作 | transform | 矩陣變換 |
二、Path方法詳解
rXxx方法
此類方法可以看到和前面的一些方法看起來很像,只是在前面多了一個r,那麼這個rXxx和前面的一些方法有什麼區別呢?
rXxx方法的座標使用的是相對位置(基於當前點的位移),而之前方法的座標是絕對位置(基於當前座標系的座標)。
舉個例子:
123456 | Path path=newPath();path.moveTo(100,100);path.lineTo(100,200);canvas.drawPath(path,mDeafultPaint); |
在這個例子中,先移動點到座標(100,100)處,之後再連線 點(100,100) 到 (100,200) 之間點直線,非常簡單,畫出來就是一條豎直的線,那接下來看下一個例子:
123456 | Path path=newPath();path.moveTo(100,100);path.rLineTo(100,200);canvas.drawPath(path,mDeafultPaint); |
這個例子中,將 lineTo 換成了 rLineTo 可以看到在螢幕上原本是豎直的線變成了傾斜的線。這是因為最終我們連線的是 (100,100) 和 (200, 300) 之間的線段。
在使用rLineTo之前,當前點的位置在 (100,100) , 使用了 rLineTo(100,200) 之後,下一個點的位置是在當前點的基礎上加上偏移量得到的,即 (100+100, 100+200) 這個位置,故最終結果如上所示。
PS: 此處僅以 rLineTo 為例,只要理解 “絕對座標” 和 “相對座標” 的區別,其他方法類比即可。
填充模式
我們在之前的文章中瞭解到,Paint有三種樣式,“描邊” “填充” 以及 “描邊加填充”,我們這裡所瞭解到就是在Paint設定為後兩種樣式時不同的填充模式對圖形渲染效果的影響。
我們要給一個圖形內部填充顏色,首先需要分清哪一部分是外部,哪一部分是內部,機器不像我們人那麼聰明,機器是如何判斷內外呢?
機器判斷圖形內外,一般有以下兩種方法:
PS:此處所有的圖形均為封閉圖形,不包括圖形不封閉這種情況。
方法 | 判定條件 | 解釋 |
---|---|---|
奇偶規則 | 奇數表示在圖形內,偶數表示在圖形外 | 從任意位置p作一條射線, 若與該射線相交的圖形邊的數目為奇數,則p是圖形內部點,否則是外部點。 |
非零環繞數規則 | 若環繞數為0表示在圖形外,非零表示在圖形內 | 首先使圖形的邊變為向量。將環繞數初始化為零。再從任意位置p作一條射線。當從p點沿射線方向移動時,對在每個方向上穿過射線的邊計數,每當圖形的邊從右到左穿過射線時,環繞數加1,從左到右時,環繞數減1。處理完圖形的所有相關邊之後,若環繞數為非零,則p為內部點,否則,p是外部點。 |
接下來我們先了解一下兩種判斷方法是如何工作的。
奇偶規則(Even-Odd Rule)
這一個比較簡單,也容易理解,直接用一個簡單示例來說明。
在上圖中有一個四邊形,我們選取了三個點來判斷這些點是否在圖形內部。
P1: 從P1發出一條射線,發現圖形與該射線相交邊數為0,偶數,故P1點在圖形外部。
P2: 從P2發出一條射線,發現圖形與該射線相交邊數為1,奇數,故P2點在圖形內部。
P3: 從P3發出一條射線,發現圖形與該射線相交邊數為2,偶數,故P3點在圖形外部。
非零環繞數規則(Non-Zero Winding Number Rule)
非零環繞數規則相對來說比較難以理解一點。
我們在之前的文章 Path之基本操作 中我們瞭解到,在給Path中新增圖形時需要指定圖形的新增方式,是用順時針還是逆時針,另外我們不論是使用lineTo,quadTo,cubicTo還是其他連線線的方法,都是從一個點連線到另一個點,換言之,Path中任何線段都是有方向性的,這也是使用非零環繞數規則的基礎。
我們依舊用一個簡單的例子來說明非零環繞數規則的用法:
PS: 注意圖形中線段的方向性!
P1: 從P1點發出一條射線,沿射線防線移動,並沒有與邊相交點部分,環繞數為0,故P1在圖形外邊。
P2: 從P2點發出一條射線,沿射線方向移動,與圖形點左側邊相交,該邊從左到右穿過穿過射線,環繞數-1,最終環繞數為-1,故P2在圖形內部。
P3: 從P3點發出一條射線,沿射線方向移動,在第一個交點處,底邊從右到左穿過射線,環繞數+1,在第二個交點處,右側邊從左到右穿過射線,環繞數-1,最終環繞數為0,故P3在圖形外部。
通常,這兩種方法的判斷結果是相同的,但也存在兩種方法判斷結果不同的情況,如下面這種情況:
注意圖形線段的方向,就不詳細解釋了,用上面的方法進行判斷即可。
自相交圖形
自相交圖形定義:多邊形在平面內除頂點外還有其他公共點。
簡單的提一下自相交圖形,瞭解概念即可,下圖就是一個簡單的自相交圖形:
Android中的填充模式
Android中的填充模式有四種,是封裝在Path中的一個列舉。
模式 | 簡介 |
---|---|
EVEN_ODD | 奇偶規則 |
INVERSE_EVEN_ODD | 反奇偶規則 |
WINDING | 非零環繞數規則 |
INVERSE_WINDING | 反非零環繞數規則 |
我們可以看到上面有四種模式,分成兩對,例如 “奇偶規則” 與 “反奇偶規則” 是一對,它們之間有什麼關係呢?
Inverse 和含義是“相反,對立”,說明反奇偶規則剛好與奇偶規則相反,例如對於一個矩形而言,使用奇偶規則會填充矩形內部,而使用反奇偶規則會填充矩形外部,這個會在後面示例中程式碼展示兩者對區別。
Android與填充模式相關的方法
這些都是Path中的方法。
方法 | 作用 |
---|---|
setFillType | 設定填充規則 |
getFillType | 獲取當前填充規則 |
isInverseFillType | 判斷是否是反向(INVERSE)規則 |
toggleInverseFillType | 切換填充規則(即原有規則與反向規則之間相互切換) |
示例演示:
本演示著重於幫助理解填充模式中的一些難點和易混淆的問題,對於一些比較簡單的問題,讀者可自行驗證,本文中不會過多贅述。
奇偶規則與反奇偶規則
12345678910 | mDeafultPaint.setStyle(Paint.Style.FILL);// 設定畫布模式為填充canvas.translate(mViewWidth/2,mViewHeight/2);// 移動畫布(座標系)Path path=newPath();// 建立Path//path.setFillType(Path.FillType.EVEN_ODD); // 設定Path填充模式為 奇偶規則path.setFillType(Path.FillType.INVERSE_WINDING);// 反奇偶規則path.addRect(-200,-200,200,200,Path.Direction.CW);// 給Path中新增一個矩形 |
下面兩張圖片分別是在奇偶規則於反奇偶規則的情況下繪製的結果,可以看出其填充的區域剛好相反:
PS: 白色為背景色,黑色為填充色。
圖形邊的方向對非零奇偶環繞數規則填充結果的影響
我們之前討論過給Path新增圖形時順時針與逆時針的作用,除了上次講述的方便記錄外,就是本文所涉及的另外一個重要作用了: “作為非零環繞數規則的判斷依據。”
通過前面我們已經大致瞭解了在圖形邊的方向會如何影響到填充效果,我們這裡驗證一下:
12345678910111213141516 | mDeafultPaint.setStyle(Paint.Style.FILL);// 設定畫筆模式為填充canvas.translate(mViewWidth/2,mViewHeight/2);// 移動畫布(坐系)Path path=newPath();// 建立Path// 新增小正方形 (通過這兩行程式碼來控制小正方形邊的方向,從而演示不同的效果)// path.addRect(-200, -200, 200, 200, Path.Direction.CW);path.addRect(-200,-200,200,200,Path.Direction.CCW);// 新增大正方形path.addRect(-400,-400,400,400,Path.Direction.CCW);path.setFillType(Path.FillType.WINDING);// 設定Path填充模式為非零環繞規則canvas.drawPath(path,mDeafultPaint);// 繪製Path |
布林操作(API19)
布林操作與我們中學所學的集合操作非常像,只要知道集合操作中等交集,並集,差集等操作,那麼理解布林操作也是很容易的。
布林操作是兩個Path之間的運算,主要作用是用一些簡單的圖形通過一些規則合成一些相對比較複雜,或難以直接得到的圖形。
如太極中的陰陽魚,如果用貝塞爾曲線製作的話,可能需要六段貝塞爾曲線才行,而在這裡我們可以用四個Path通過布林運算得到,而且會相對來說更容易理解一點。
123456789101112131415161718 | canvas.translate(mViewWidth/2,mViewHeight/2);Path path1=newPath();Path path2=newPath();Path path3=newPath();Path path4=newPath();path1.addCircle(0,0,200,Path.Direction.CW);path2.addRect(0,-200,200,200,Path.Direction.CW);path3.addCircle(0,-100,100,Path.Direction.CW);path4.addCircle(0,100,100,Path.Direction.CCW);path1.op(path2,Path.Op.DIFFERENCE);path1.op(path3,Path.Op.UNION);path1.op(path4,Path.Op.DIFFERENCE);canvas.drawPath(path1,mDeafultPaint); |
前面演示了布林運算的作用,接下來我們瞭解一下布林運算的核心:布林邏輯。
Path的布林運算有五種邏輯,如下:
邏輯名稱 | 類比 | 說明 | 示意圖 |
---|---|---|---|
DIFFERENCE | 差集 | Path1中減去Path2後剩下的部分 | |
REVERSE_DIFFERENCE | 差集 | Path2中減去Path1後剩下的部分 | |
INTERSECT | 交集 | Path1與Path2相交的部分 | |
UNION | 並集 | 包含全部Path1和Path2 | |
XOR | 異或 | 包含Path1與Path2但不包括兩者相交的部分 |
布林運算方法
通過前面到理論知識鋪墊,相信大家對布林運算已經有了基本的認識和理解,下面我們用程式碼演示一下布林運算:
在Path中的布林運算有兩個方法
12 | booleanop(Path path,Path.Op op)booleanop(Path path1,Path path2,Path.Op op) |
兩個方法中的返回值用於判斷布林運算是否成功,它們使用方法如下:
12345 | // 對 path1 和 path2 執行布林運算,運算方式由第二個引數指定,運算結果存入到path1中。path1.op(path2,Path.Op.DIFFERENCE);// 對 path1 和 path2 執行布林運算,運算方式由第三個引數指定,運算結果存入到path3中。path3.op(path1,path2,Path.Op.DIFFERENCE) |
布林運算示例
程式碼:
123456789101112131415161718192021222324252627282930313233343536 | intx=80;intr=100;canvas.translate(250,0);Path path1=newPath();Path path2=newPath();Path pathOpResult=newPath();path1.addCircle(-x,0,r,Path.Direction.CW);path2.addCircle(x,0,r,Path.Direction.CW);pathOpResult.op(path1,path2,Path.Op.DIFFERENCE);canvas.translate(0,200);canvas.drawText("DIFFERENCE",240,0,mDeafultPaint);canvas.drawPath(pathOpResult,mDeafultPaint);pathOpResult.op(path1,path2,Path.Op.
相關推薦安卓自定義 View 進階:Path 完結篇(偽)經歷過前兩篇 Path之基本操作 和 Path之貝塞爾曲線 的講解,本篇終於進入Path的收尾篇,本篇結束後Path的大部分相關方法都已經講解完了,但Path還有一些更有意思的玩法,應該會在後續的文章中出現吧,嗯,應該會的ˊ_>ˋ 一.Path常用方法表 為了相容性 安卓自定義View進階:Path基本操作在上一篇Canvas之圖片文字中我們瞭解瞭如何使用Canvas中繪製圖片文字,結合前幾篇文章,Canvas的基本操作已經差不多完結了,然而Canvas不僅僅具有這些基本的操作,還可以更加炫酷,本次會了解到path(路徑)這個Canvas中的神器,有了這個神器,就能創造出更多炫( 安卓自定義View進階:Path之玩出花樣(PathMeasure)PS:不要問我為什麼不講 PathEffect,因為這個方法在後面的Paint系列中。 先放一個圖鎮樓,省的下面無聊的內容把你們都嚇跑了Σ( ̄。 ̄ノ)ノ Path & PathMeasure 顧名思義,PathMeasure是一個用來測量Path的類,主要有以下方法: 構造方法 方法名 釋 安卓自定義 View 進階:貝塞爾曲線在上一篇文章Path之基本圖形中我們瞭解了Path的基本使用方法,本次瞭解Path中非常非常非常重要的內容-貝塞爾曲線。 一.Path常用方法表 為了相容性(偷懶) 本表格中去除了在API21(即安卓版本5.0)以上才新增的方法。忍不住吐槽一下,為啥看起來有些順手就能寫的 安卓自定義View進階-手勢檢測(GestureDecetor)Android 手勢檢測,主要是 GestureDetector 相關內容的用法和注意事項,本文依舊屬於事件處理這一體系,部分內容會涉及到之前文章提及過的知識點,如果你沒看過之前的文章,可以到 自定義 View 系列 來檢視這些內容。 在開發 Android 手機應用過程中,可 安卓自定義View進階-多點觸控詳解Android 多點觸控詳解,在前面的幾篇文章中我們大致瞭解了 Android 中的事件處理流程和一些簡單的處理方案,本次帶大家瞭解 Android 多點觸控相關的一些知識。 多點觸控 ( Multitouch,也稱 Multi-touch ),即同時接受螢幕上多個點的人機互動 安卓自定義View進階-特殊控制元件的事件處理方案本文帶大家瞭解 Android 特殊形狀控制元件的事件處理方式,主要是利用了 Region 和 Matrix 的一些方法,超級實用的事件處理方案,相信看完本篇之後,任何奇葩控制元件的事件處理都會變得十分簡單。 不得不說,Android 對事件體系封裝的非常棒,即便對事件體系不太 安卓自定義View進階-MotionEvent詳解Android MotionEvent 詳解,之前用了兩篇文章 事件分發機制原理 和 事件分發機制詳解 來講解事件分發,而作為事件分發主角之一的 MotionEvent 並沒有過多的說明,本文就帶大家瞭解 MotionEvent 的相關內容,簡要介紹觸控事件,主要包括 單點觸控、多點 安卓自定義View進階-事件分發機制詳解Android 事件分發機制詳解,在上一篇文章 事件分發機制原理 中簡要分析了一下事件分發機制的原理,原理是十分簡單的,一句話就能總結:責任鏈模式,事件層層傳遞,直到被消費。 雖然原理簡單,但是隨著 Android 不斷的發展,實際運用場景也越來越複雜,所以想要徹底玩轉事件分發機制還 安卓自定義View進階-Matrix Camera本篇依舊屬於Matrix,主要講解Camera,Android下有很多相機應用,其中的美顏相機更是不少,不過今天這個Camera可不是我們平時拍照的那個相機,而是graphic包下的Camera,專業給View拍照的相機,不過既然是相機,作用都是類似的,主要是將3D的內容拍扁變成2D 安卓自定義View進階-Matrix詳解這應該是目前最詳細的一篇講解Matrix的中文文章了,在上一篇文章Matrix原理中,我們對Matrix做了一個簡單的瞭解,偏向理論,在本文中則會詳細的講解Matrix的具體用法,以及與Matrix相關的一些實用技巧。 ⚠️ 警告:測試本文章示例之前請關閉硬體加速。 安卓自定義View進階-Matrix原理本文內容偏向理論,和 畫布操作 有重疊的部分,本文會讓你更加深入的瞭解其中的原理。 本篇的主角Matrix,是一個一直在後臺默默工作的勞動模範,雖然我們所有看到View背後都有著Matrix的功勞,但我們卻很少見到它,本篇我們就看看它是何方神聖吧。 由於Goog 安卓自定義View進階-PathMeasure可以看到,在經過 Path之基本操作 Path之貝塞爾曲線 和 Path之完結篇 後, Path中各類方法基本上都講完了,表格中還沒有講解到到方法就是矩陣變換了,難道本篇終於要講矩陣了? 非也,矩陣這一部分仍在後面單獨講解,本篇主要講解 PathMeasure 這個類與 Path 的 安卓自定義View進階-Path之貝塞爾曲線在上一篇文章Path之基本操作中我們瞭解了Path的基本使用方法,本次瞭解Path中非常非常非常重要的內容-貝塞爾曲線。 一.Path常用方法表 為了相容性(偷懶) 本表格中去除了在API21(即安卓版本5.0)以上 安卓自定義View進階-Path之基本操作在上一篇Canvas之圖片文字中我們瞭解瞭如何使用Canvas中繪製圖片文字,結合前幾篇文章,Canvas的基本操作已經差不多完結了,然而Canvas不僅僅具有這些基本的操作,還可以更加炫酷,本次會了解到path(路徑)這個Canvas中的神器,有了這個神器,就能創造出更多炫(zhu 安卓自定義View進階-Canvas之圖片文字在上一篇文章Canvas之畫布操作中我們瞭解了畫布的一些基本操作方法,本次瞭解一些繪製圖片文字相關的內容。如果你對前幾篇文章講述的內容熟練掌握的話,那麼恭喜你,本篇結束之後,大部分的自定義View已經難不倒你了,當然了,這並不是終點,接下來還會有更加炫酷的技能。 一.Canva 安卓自定義View進階-分類與流程本章節為什麼要叫進階篇?(雖然講的是基礎內容),因為從本篇開始,將會逐漸揭開自定義View的神祕面紗,每一篇都將比上一篇內容更加深入,利用所學的知識能夠製作更加炫酷自定義View,就像在臺階上一樣,每一篇都更上一層,幫助大家一步步走向人生巔峰,出任CEO,迎娶白富美。 誤 安卓自定義View進階-Path之完結篇經歷過前兩篇 Path之基本操作 和 Path之貝塞爾曲線 的講解,本篇終於進入Path的收尾篇,本篇結束後Path的大部分相關方法都已經講解完了,但Path還有一些更有意思的玩法,應該會在後續的文章中出現。 一.Path常用方法表 為了相容性(偷懶) 本表格中去除 安卓自定義View進階-縮放手勢檢測(ScaleGestureDecetor)0. 前言 Android 縮放手勢檢測,ScaleGestureDetector 相關內容的用法和注意事項,本文依舊屬於事件處理這一體系,在大多數的情況下,縮放手勢都不是單獨存在的,需要配合其它的手勢來使用,所以推薦配合 手勢檢測(GestureDetector) 一 安卓自定義View進階-Canvas之畫布操作Canvas之畫布操作 上一篇Canvas之繪製基本形狀中我們瞭解瞭如何使用Canvas繪製基本圖形,本次瞭解一些基本的畫布操作。 本來想把畫布操作放到後面部分的,但是發現很多圖形繪製都離不開畫布操作,於是先講解一下畫布的基本操作方法。 |