rXxx方法
rXxx方法的坐标使用的是相對位置(基于目前點的位移),而之前方法的坐标是絕對位置(基于目前坐标系的坐标)。
Path path = new Path();
path.moveTo(100,100);
path.lineTo(100,200);
canvas.drawPath(path,mDeafultPaint);
複制

在這個例子中,先移動點到坐标(100,100)處,之後再連接配接 點(100,100) 到 (100,200) 之間點直線,非常簡單,畫出來就是一條豎直的線,那接下來看下一個例子:
Path path = new Path();
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) 這個位置。
此處僅以 rLineTo 為例,隻要了解 “絕對坐标” 和 “相對坐标” 的差別,其他方法類比即可。
填充模式
Paint有三種樣式,“描邊” “填充” 以及 “描邊加填充”,我們這裡所了解到就是在Paint設定為後兩種樣式時不同的填充模式對圖形渲染效果的影響。
我們要給一個圖形内部填充顔色,首先需要厘清哪一部分是外部,哪一部分是内部,機器判斷圖形内外,一般有以下兩種方法:(此處所有的圖形均為封閉圖形,不包括圖形不封閉這種情況。)
方法 | 判定條件 | 解釋 |
---|---|---|
奇偶規則 | 奇數表示在圖形内,偶數表示在圖形外 | 從任意位置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中添加圖形時需要指定圖形的添加方式,是用順時針還是逆時針,另外我們不論是使用lineTo,quadTo,cubicTo還是其他連接配接線的方法,都是從一個點連接配接到另一個點,換言之,Path中任何線段都是有方向性的,這也是使用非零環繞數規則的基礎。
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 | 切換填充規則(即原有規則與反向規則之間互相切換) |
奇偶規則與反奇偶規則
mDeafultPaint.setStyle(Paint.Style.FILL); // 設定畫布模式為填充
canvas.translate(mViewWidth / 2, mViewHeight / 2); // 移動畫布(坐标系)
Path path = new Path(); // 建立Path
//path.setFillType(Path.FillType.EVEN_ODD); // 設定Path填充模式為 奇偶規則
path.setFillType(Path.FillType.INVERSE_EVEN_ODD); // 反奇偶規則
path.addRect(-200,-200,200,200, Path.Direction.CW); // 給Path中添加一個矩形
複制
下面兩張圖檔分别是在奇偶規則于反奇偶規則的情況下繪制的結果,可以看出其填充的區域剛好相反:(白色為背景色,黑色為填充色)
圖形邊的方向對非零奇偶環繞數規則填充結果的影響
我們之前讨論過給Path添加圖形時順時針與逆時針的作用,除了上次講述的友善記錄外,就是本文所涉及的另外一個重要作用了: “作為非零環繞數規則的判斷依據。”
mDeafultPaint.setStyle(Paint.Style.FILL); // 設定畫筆模式為填充
canvas.translate(mViewWidth / 2, mViewHeight / 2); // 移動畫布(坐系)
Path path = new Path(); // 建立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通過布爾運算得到,而且會相對來說更容易了解一點。
canvas.translate(mViewWidth / 2, mViewHeight / 2);
Path path1 = new Path();
Path path2 = new Path();
Path path3 = new Path();
Path path4 = new Path();
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的布爾運算有五種邏輯,如下:
布爾運算方法
在Path中的布爾運算有兩個方法
boolean op (Path path, Path.Op op)
boolean op (Path path1, Path path2, Path.Op op)
複制
兩個方法中的傳回值用于判斷布爾運算是否成功,它們使用方法如下:
// 對 path1 和 path2 執行布爾運算,運算方式由第二個參數指定,運算結果存入到path1中。
path1.op(path2, Path.Op.DIFFERENCE);
// 對 path1 和 path2 執行布爾運算,運算方式由第三個參數指定,運算結果存入到path3中。
path3.op(path1, path2, Path.Op.DIFFERENCE)
複制
int x = 80;
int r = 100;
canvas.translate(250,0);
Path path1 = new Path();
Path path2 = new Path();
Path pathOpResult = new Path();
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.REVERSE_DIFFERENCE);
canvas.translate(0, 300);
canvas.drawText("REVERSE_DIFFERENCE", 240,0,mDeafultPaint);
canvas.drawPath(pathOpResult,mDeafultPaint);
pathOpResult.op(path1,path2, Path.Op.INTERSECT);
canvas.translate(0, 300);
canvas.drawText("INTERSECT", 240,0,mDeafultPaint);
canvas.drawPath(pathOpResult,mDeafultPaint);
pathOpResult.op(path1,path2, Path.Op.UNION);
canvas.translate(0, 300);
canvas.drawText("UNION", 240,0,mDeafultPaint);
canvas.drawPath(pathOpResult,mDeafultPaint);
pathOpResult.op(path1,path2, Path.Op.XOR);
canvas.translate(0, 300);
canvas.drawText("XOR", 240,0,mDeafultPaint);
canvas.drawPath(pathOpResult,mDeafultPaint);
複制
計算邊界
這個方法主要作用是計算Path所占用的空間以及所在位置,方法如下:
void computeBounds (RectF bounds, boolean exact)
複制
它有兩個參數:
參數 | 作用 |
---|---|
bounds | 測量結果會放入這個矩形 |
exact | 是否精确測量,目前這一個參數作用已經廢棄,一般寫true即可。 |
計算邊界示例
// 移動canvas,mViewWidth與mViewHeight在 onSizeChanged 方法中獲得
canvas.translate(mViewWidth/2,mViewHeight/2);
RectF rect1 = new RectF(); // 存放測量結果的矩形
Path path = new Path(); // 建立Path并添加一些内容
path.lineTo(100,-50);
path.lineTo(100,50);
path.close();
path.addCircle(-100,0,100, Path.Direction.CW);
path.computeBounds(rect1,true); // 測量Path
canvas.drawPath(path,mDeafultPaint); // 繪制Path
mDeafultPaint.setStyle(Paint.Style.STROKE);
mDeafultPaint.setColor(Color.RED);
canvas.drawRect(rect1,mDeafultPaint); // 繪制邊界
複制
重置路徑
重置Path有兩個方法,分别是reset和rewind,兩者差別主要有一下兩點:
方法 | 是否保留FillType設定 | 是否保留原有資料結構 |
---|---|---|
reset | 是 | 否 |
rewind | 否 | 是 |
這個兩個方法應該何時選擇呢?
選擇權重: FillType > 資料結構
因為“FillType”影響的是顯示效果,而“資料結構”影響的是重建速度。