# Path之基本操作
### 作者微博: [@GcsSloop](http://weibo.com/GcsSloop)
### [【本系列相關文章】](https://github.com/GcsSloop/AndroidNote/tree/master/CustomView/README.md)
在上一篇[Canvas之圖片文字](https://github.com/GcsSloop/AndroidNote/blob/master/CustomView/Advance/%5B04%5DCanvas_PictureText.md)中我們了解了如何使用Canvas中繪制圖片文字,結合前幾篇文章,Canvas的基本操作已經差不多完結了,然而Canvas不僅僅具有這些基本的操作,還可以更加炫酷,本次會了解到path(路徑)這個Canvas中的神器,有了這個神器,就能創造出更多**炫(zhuang)酷(B)**的東東了。
******
# 一.Path常用方法表
> 為了兼容性(_偷懶_) 本表格中去除了部分API21(即安卓版本5.0)以上才添加的方法。
| 作用 | 相關方法 | 備注 |
| ----------- | ---------------------------------------- | ---------------------------------------- |
| 移動起點 | 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中的內容<br/> **reset不保留內部數據結構,但會保留FillType.**<br/> **rewind會保留內部的數據結構,但不保留FillType** |
| 矩陣操作 | transform | 矩陣變換 |
# 二.Path詳解
**請關閉硬件加速,以免引起不必要的問題!<br/>請關閉硬件加速,以免引起不必要的問題!<br/>請關閉硬件加速,以免引起不必要的問題!**
**在AndroidMenifest文件中application節點下添上 android:hardwareAccelerated="false"以關閉整個應用的硬件加速。 <br/>更多請參考這里:[Android的硬件加速及可能導致的問題](https://github.com/GcsSloop/AndroidNote/issues/7)**
## Path作用
本次特地開了一篇詳細講解Path,為什么要單獨摘出來呢,這是因為Path在2D繪圖中是一個很重要的東西。
在前面我們講解的所有繪制都是簡單圖形(如 矩形 圓 圓弧等),而對于那些復雜一點的圖形則沒法去繪制(如繪制一個心形 正多邊形 五角星等),而**使用Path不僅能夠繪制簡單圖形,也可以繪制這些比較復雜的圖形。另外,根據路徑繪制文本和剪裁畫布都會用到Path。**
關于Path的作用先簡單地說這么多,具體的我們接下來慢慢研究。
## Path含義
**官方介紹:**
_The Path class encapsulates compound (multiple contour) geometric paths consisting of straight line segments, quadratic curves, and cubic curves. It can be drawn with canvas.drawPath(path, paint), either filled or stroked (based on the paint's Style), or it can be used for clipping or to draw text on a path._
> 嗯,沒錯依舊是拿來裝逼的,如果你看不懂的話,不用擔心,其實并沒有什么卵用。
**通俗解釋(sloop個人版):**
**Path封裝了由直線和曲線(二次,三次貝塞爾曲線)構成的幾何路徑。你能用Canvas中的drawPath來把這條路徑畫出來(同樣支持Paint的不同繪制模式),也可以用于剪裁畫布和根據路徑繪制文字。我們有時會用Path來描述一個圖像的輪廓,所以也會稱為輪廓線(輪廓線僅是Path的一種使用方法,兩者并不等價)**
另外路徑有開放和封閉的區別。
| 圖像 | 名稱 | 備注 |
| ---------------------------------------- | ---- | ------------- |
|  | 封閉路徑 | 首尾相接形成了一個封閉區域 |
|  | 開放路徑 | 沒有首位相接形成封閉區域 |
> 這個是我隨便畫的,僅為展示一下區別,請無視我靈魂畫師一般的繪圖水準。
**與Path相關的還有一些比較神奇的概念,不過暫且不說,等接下來需要用到的時候再詳細說明。**
## Path使用方法詳解
前面扯了一大堆概念性的東西。接下來就開始實戰了,請諸位看官準備好瓜子、花生、爆米花,坐下來慢慢觀看。

### 第1組: moveTo、 setLastPoint、 lineTo 和 close
由于Path的有些知識點無法單獨來講,所以本次采取了一次講一組方法。
按照慣例,先創建畫筆:
``` java
Paint mPaint = new Paint(); // 創建畫筆
mPaint.setColor(Color.BLACK); // 畫筆顏色 - 黑色
mPaint.setStyle(Paint.Style.STROKE); // 填充模式 - 描邊
mPaint.setStrokeWidth(10); // 邊框寬度 - 10
```
#### lineTo:
方法預覽:
```
public void lineTo (float x, float y)
```
首先講解的的LineTo,為啥先講解這個呢?
是因為moveTo、 setLastPoint、 close都無法直接看到效果,借助有具現化效果的lineTo才能讓這些方法現出原形。
lineTo很簡單,只有一個方法,作用也很容易理解,line嘛,顧名思義就是一條線。
俗話(數學書上)說,兩點確定一條直線,但是看參數明顯只給了一個點的坐標吧(這不按常理出牌啊)。
再仔細一看,這個lineTo除了line外還有一個to呢,to翻譯過來就是“至”,到某個地方的意思,**lineTo難道是指從某個點到參數坐標點之間連一條線?**
沒錯,你猜對了,但是這某個點又是哪里呢?
前面我們提到過Path可以用來描述一個圖像的輪廓,圖像的輪廓通常都是用一條線構成的,所以這里的某個點就是上次操作結束的點,如果沒有進行過操作則默認點為坐標原點。
那么我們就來試一下:
``` java
canvas.translate(mWidth / 2, mHeight / 2); // 移動坐標系到屏幕中心(寬高數據在onSizeChanged中獲取)
Path path = new Path(); // 創建Path
path.lineTo(200, 200); // lineTo
path.lineTo(200,0);
canvas.drawPath(path, mPaint); // 繪制Path
```
<img src="http://ww4.sinaimg.cn/large/005Xtdi2jw1f1ap1tu0w9j30u01hcjse.jpg" width = "270" height = "480"/>
在示例中我們調用了兩次lineTo,**第一次由于之前沒有過操作,所以默認點就是坐標原點O,結果就是坐標原點O到A(200,200)之間連直線(用藍色圈1標注)。**
**第二次lineTo的時候,由于上次的結束位置是A(200,200),所以就是A(200,200)到B(200,0)之間的連線(用藍色圈2標注)。**
#### moveTo 和 setLastPoint:
方法預覽:
``` java
// moveTo
public void moveTo (float x, float y)
// setLastPoint
public void setLastPoint (float dx, float dy)
```
這兩個方法雖然在作用上有相似之處,但實際上卻是完全不同的兩個東東,具體參照下表:
| 方法名 | 簡介 | 是否影響之前的操作 | 是否影響之后操作 |
| ------------ | -------------- | --------- | -------- |
| moveTo | 移動下一次操作的起點位置 | 否 | 是 |
| setLastPoint | 設置之前操作的最后一個點位置 | 是 | 是 |
廢話不多說,直接上代碼:
``` java
canvas.translate(mWidth / 2, mHeight / 2); // 移動坐標系到屏幕中心
Path path = new Path(); // 創建Path
path.lineTo(200, 200); // lineTo
path.moveTo(200,100); // moveTo
path.lineTo(200,0); // lineTo
canvas.drawPath(path, mPaint); // 繪制Path
```
<img src="http://ww3.sinaimg.cn/large/005Xtdi2gw1f1aqjptdtjj30u01hct9t.jpg" width = "270" height = "480"/>
這個和上面演示lineTo的方法類似,只不過在兩個lineTo之間添加了一個moveTo。
**moveTo只改變下次操作的起點,在執行完第一次LineTo的時候,本來的默認點位置是A(200,200),但是moveTo將其改變成為了C(200,100),所以在第二次調用lineTo的時候就是連接C(200,100) 到 B(200,0) 之間的直線(用藍色圈2標注)。**
下面是setLastPoint的示例:
``` java
canvas.translate(mWidth / 2, mHeight / 2); // 移動坐標系到屏幕中心
Path path = new Path(); // 創建Path
path.lineTo(200, 200); // lineTo
path.setLastPoint(200,100); // setLastPoint
path.lineTo(200,0); // lineTo
canvas.drawPath(path, mPaint); // 繪制Path
```
<img src="http://ww1.sinaimg.cn/large/005Xtdi2gw1f1ari1l9g8j30u01hcab5.jpg" width = "270" height = "480"/>
**setLastPoint是重置上一次操作的最后一個點,在執行完第一次的lineTo的時候,最后一個點是A(200,200),而setLastPoint更改最后一個點為C(200,100),所以在實際執行的時候,第一次的lineTo就不是從原點O到A(200,200)的連線了,而變成了從原點O到C(200,100)之間的連線了。**
**在執行完第一次lineTo和setLastPoint后,最后一個點的位置是C(200,100),所以在第二次調用lineTo的時候就是C(200,100) 到 B(200,0) 之間的連線(用藍色圈2標注)。**
#### close
方法預覽:
``` java
public void close ()
```
close方法用于連接當前最后一個點和最初的一個點(如果兩個點不重合的話),最終形成一個封閉的圖形。
``` java
canvas.translate(mWidth / 2, mHeight / 2); // 移動坐標系到屏幕中心
Path path = new Path(); // 創建Path
path.lineTo(200, 200); // lineTo
path.lineTo(200,0); // lineTo
path.close(); // close
canvas.drawPath(path, mPaint); // 繪制Path
```
<img src="http://ww4.sinaimg.cn/large/005Xtdi2jw1f1axmfeojzj30u01hcwfi.jpg" width = "270" height = "480"/>
很明顯,兩個lineTo分別代表第1和第2條線,而close在此處的作用就算連接了B(200,0)點和原點O之間的第3條線,使之形成一個封閉的圖形。
**注意:close的作用是封閉路徑,與連接當前最后一個點和第一個點并不等價。如果連接了最后一個點和第一個點仍然無法形成封閉圖形,則close什么 也不做。**
### 第2組: addXxx與arcTo
這次內容主要是在Path中添加基本圖形,重點區分addArc與arcTo。
#### 第一類(基本形狀)
方法預覽:
``` java
// 第一類(基本形狀)
// 圓形
public void addCircle (float x, float y, float radius, Path.Direction dir)
// 橢圓
public void addOval (RectF oval, Path.Direction dir)
// 矩形
public void addRect (float left, float top, float right, float bottom, Path.Direction dir)
public void addRect (RectF rect, Path.Direction dir)
// 圓角矩形
public void addRoundRect (RectF rect, float[] radii, Path.Direction dir)
public void addRoundRect (RectF rect, float rx, float ry, Path.Direction dir)
```
**這一類就是在path中添加一個基本形狀,基本形狀部分和前面所講的繪制基本形狀并無太大差別,詳情參考[Canvas(1)顏色與基本形狀](https://github.com/GcsSloop/AndroidNote/blob/master/%E9%97%AE%E9%A2%98/Canvas/Canvas(1).md), 本次只將其中不同的部分摘出來詳細講解一下。**
**仔細觀察一下第一類的方法,無一例外,在最后都有一個_Path.Direction_,這是一個什么神奇的東東?**
Direction的意思是 方向,趨勢。 點進去看一下會發現Direction是一個枚舉(Enum)類型,里面只有兩個枚舉常量,如下:
| 類型 | 解釋 | 翻譯 |
| ---- | ----------------- | ---- |
| CW | clockwise | 順時針 |
| CCW | counter-clockwise | 逆時針 |
> **瞬間懵逼,我只是想添加一個基本的形狀啊,搞什么順時針和逆時針, (╯‵□′)╯︵┻━┻**
**稍安勿躁,┬─┬ ノ( ' - 'ノ) {擺好擺好) 既然存在肯定是有用的,先偷偷劇透一下這個順時針和逆時針的作用。**
| 序號 | 作用 |
| ---- | ------------------------- |
| 1 | 在添加圖形時確定閉合順序(各個點的記錄順序) |
| 2 | 對圖形的渲染結果有影響(是判斷圖形渲染的重要條件) |
這個先劇透這么多,至于對閉合順序有啥影響,自相交圖形的渲染等問題等請慢慢看下去
咱們先研究確定閉合順序的問題,添加一個矩形試試看:
``` java
canvas.translate(mWidth / 2, mHeight / 2); // 移動坐標系到屏幕中心
Path path = new Path();
path.addRect(-200,-200,200,200, Path.Direction.CW);
canvas.drawPath(path,mPaint);
```
<img src="http://ww1.sinaimg.cn/large/005Xtdi2gw1f1cmvjtuxcj30u01hcwgm.jpg" width = "270" height = "480"/>
**將上面代碼的CW改為CCW再運行一次。接下來就是見證奇跡的時刻,兩次運行結果一模一樣,有木有很神奇!**
> **(╯°Д°)╯︵ ┻━┻(再TM掀一次)
> 坑人也不帶這樣的啊,一毛一樣要它干嘛。**
**其實啊,這個東東是自帶隱身技能的,想要讓它現出原形,就要用到咱們剛剛學到的setLastPoint(重置當前最后一個點的位置)。**
```
canvas.translate(mWidth / 2, mHeight / 2); // 移動坐標系到屏幕中心
Path path = new Path();
path.addRect(-200,-200,200,200, Path.Direction.CW);
path.setLastPoint(-300,300); // <-- 重置最后一個點的位置
canvas.drawPath(path,mPaint);
```
<img src="http://ww3.sinaimg.cn/large/005Xtdi2jw1f4bg7ly3n7j30u01hc78a.jpg" width = "270" height = "480"/>
可以明顯看到,圖形發生了奇怪的變化。為何會如此呢?
我們先分析一下,繪制一個矩形(僅繪制邊線),實際上只需要進行四次lineTo操作就行了,也就是說,只需要知道4個點的坐標,然后使用moveTo到第一個點,之后依次lineTo就行了(從上面的測試可以看出,在實際繪制中也確實是這么干的)。
可是為什么要這么做呢?確定一個矩形最少需要兩個點(對角線的兩個點),根據這兩個點的坐標直接算出四條邊然后畫出來不就行了,干嘛還要先計算出四個點坐標,之后再連直線呢?
這個就要涉及一些path的存儲問題了,前面在path中的定義中說過,Path是封裝了由直線和曲線(二次,三次貝塞爾曲線)構成的幾何路徑。其中曲線部分用的是貝塞爾曲線,稍后再講。 然而除了曲線部分就只剩下直線了,對于直線的存儲最簡單的就是記錄坐標點,然后直接連接各個點就行了。雖然記錄矩形只需要兩個點,但是如果只用兩個點來記錄一個矩形的話,就要額外增加一個標志位來記錄這是一個矩形,顯然對于存儲和解析都是很不劃算的事情,將矩形轉換為直線,為的就是存儲記錄方便。
**扯了這么多,該回歸正題了,就是我們的順時針和逆時針在這里是干啥的?**
圖形在實際記錄中就是記錄各個的點,對于一個圖形來說肯定有多個點,既然有這么多的點,肯定就需要一個先后順序,這里順時針和逆時針就是用來確定記錄這些點的順序的。
對于上面這個矩形來說,我們采用的是順時針(CW),所以記錄的點的順序就是 A -> B -> C -> D. 最后一個點就是D,我們這里使用setLastPoint改變最后一個點的位置實際上是改變了D的位置。
理解了上面的原理之后,設想如果我們將順時針改為逆時針(CCW),則記錄點的順序應該就是 A -> D -> C -> B, 再使用setLastPoint則改變的是B的位置,我們試試看結果和我們的猜想是否一致:
``` java
canvas.translate(mWidth / 2, mHeight / 2); // 移動坐標系到屏幕中心
Path path = new Path();
path.addRect(-200,-200,200,200, Path.Direction.CCW);
path.setLastPoint(-300,300); // <-- 重置最后一個點的位置
canvas.drawPath(path,mPaint);
```
<img src="http://ww2.sinaimg.cn/large/005Xtdi2jw1f4bgkdk8efj30u01hctcu.jpg" width = "270" height = "480"/>
通過驗證發現,發現結果和我們猜想的一樣,但是還有一個潛藏的問題不曉得大家可否注意到。**我們用兩個點的坐標確定了一個矩形,矩形起始點(A)就是我們指定的第一個點的坐標。**
**需要注意的是,交換坐標點的順序可能就會影響到某些繪制內容哦,例如上面的例子,你可以嘗試交換兩個坐標點,或者指定另外兩個點來作為參數,雖然指定的是同一個矩形,但實際繪制出來是不同的哦。**
**參數中點的順序很重要!<br/>參數中點的順序很重要!<br/>參數中點的順序很重要!<br/>**
重要的話說三遍,本次是用矩形作為例子的,其他的幾個圖形基本上都包含了曲線,詳情參見后續的貝塞爾曲線部分。
**關于順時針和逆時針對圖形填充結果的影響請等待后續文章,雖然只講了一個Path,但也是內容頗多,放進一篇中就太長了,請見諒。**
#### 第二類(Path)
方法預覽:
``` java
// 第二類(Path)
// path
public void addPath (Path src)
public void addPath (Path src, float dx, float dy)
public void addPath (Path src, Matrix matrix)
```
這個相對比較簡單,也很容易理解,就是將兩個Path合并成為一個。
第三個方法是將src添加到當前path之前先使用Matrix進行變換。
第二個方法比第一個方法多出來的兩個參數是將src進行了位移之后再添加進當前path中。
示例:
``` java
canvas.translate(mWidth / 2, mHeight / 2); // 移動坐標系到屏幕中心
canvas.scale(1,-1); // <-- 注意 翻轉y坐標軸
Path path = new Path();
Path src = new Path();
path.addRect(-200,-200,200,200, Path.Direction.CW);
src.addCircle(0,0,100, Path.Direction.CW);
path.addPath(src,0,200);
mPaint.setColor(Color.BLACK); // 繪制合并后的路徑
canvas.drawPath(path,mPaint);
```
<img src="http://ww4.sinaimg.cn/large/005Xtdi2gw1f4iylko7zzj30u01hc0tm.jpg" width = "270" height = "480"/>
首先我們新建的兩個Path(矩形和圓形)中心都是坐標原點,我們在將包含圓形的path添加到包含矩形的path之前將其進行移動了一段距離,最終繪制出來的效果就如上面所示。
#### 第三類(addArc與arcTo)
方法預覽:
``` java
// 第三類(addArc與arcTo)
// addArc
public void addArc (RectF oval, float startAngle, float sweepAngle)
// arcTo
public void arcTo (RectF oval, float startAngle, float sweepAngle)
public void arcTo (RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)
```
從名字就可以看出,這兩個方法都是與圓弧相關的,作用都是添加一個圓弧到path中,但既然存在兩個方法,兩者之間肯定是有區別的:
| 名稱 | 作用 | 區別 |
| ------ | ----------- | --------------------------------------- |
| addArc | 添加一個圓弧到path | 直接添加一個圓弧到path中 |
| arcTo | 添加一個圓弧到path | 添加一個圓弧到path,如果圓弧的起點和上次最后一個坐標點不相同,就連接兩個點 |
可以看到addArc有1個方法(_實際上是兩個的,但另一個重載方法是API21添加的_), 而arcTo有2個方法,其中一個最后多了一個布爾類型的變量forceMoveTo。
**forceMoveTo是什么作用呢?**
這個變量意思為“是否強制使用moveTo”,也就是說,是否使用moveTo將變量移動到圓弧的起點位移,也就意味著:
| forceMoveTo | 含義 | 等價方法 |
| ----------- | ---------------------------- | ---------------------------------------- |
| true | 將最后一個點移動到圓弧起點,即不連接最后一個點與圓弧起點 | public void addArc (RectF oval, float startAngle, float sweepAngle) |
| false | 不移動,而是連接最后一個點與圓弧起點 | public void arcTo (RectF oval, float startAngle, float sweepAngle) |
**示例(addArc):**
``` java
canvas.translate(mWidth / 2, mHeight / 2); // 移動坐標系到屏幕中心
canvas.scale(1,-1); // <-- 注意 翻轉y坐標軸
Path path = new Path();
path.lineTo(100,100);
RectF oval = new RectF(0,0,300,300);
path.addArc(oval,0,270);
// path.arcTo(oval,0,270,true); // <-- 和上面一句作用等價
canvas.drawPath(path,mPaint);
```
<img src="http://ww4.sinaimg.cn/large/005Xtdi2jw1f1ihr1s77jj30u01hcmzh.jpg" width = "270" height = "480"/>
**示例(arcTo):**
``` java
canvas.translate(mWidth / 2, mHeight / 2); // 移動坐標系到屏幕中心
canvas.scale(1,-1); // <-- 注意 翻轉y坐標軸
Path path = new Path();
path.lineTo(100,100);
RectF oval = new RectF(0,0,300,300);
path.arcTo(oval,0,270);
// path.arcTo(oval,0,270,false); // <-- 和上面一句作用等價
canvas.drawPath(path,mPaint);
```
<img src="http://ww1.sinaimg.cn/large/005Xtdi2jw1f1ihsfjexcj30u01hcjts.jpg" width = "270" height = "480"/>
從上面兩張運行效果圖可以清晰的看出來兩者的區別,我就不再廢話了。
### 第3組:isEmpty、 isRect、isConvex、 set 和 offset
這一組比較簡單,稍微說一下就可以了。
#### isEmpty
方法預覽:
``` java
public boolean isEmpty ()
```
判斷path中是否包含內容。
``` java
Path path = new Path();
Log.e("1",path.isEmpty()+"");
path.lineTo(100,100);
Log.e("2",path.isEmpty()+"");
```
log輸出結果:
```
03-02 14:22:54.770 12379-12379/com.sloop.canvas E/1: true
03-02 14:22:54.770 12379-12379/com.sloop.canvas E/2: false
```
#### isRect
方法預覽:
``` java
public boolean isRect (RectF rect)
```
判斷path是否是一個矩形,如果是一個矩形的話,會將矩形的信息存放進參數rect中。
``` java
path.lineTo(0,400);
path.lineTo(400,400);
path.lineTo(400,0);
path.lineTo(0,0);
RectF rect = new RectF();
boolean b = path.isRect(rect);
Log.e("Rect","isRect:"+b+"| left:"+rect.left+"| top:"+rect.top+"| right:"+rect.right+"| bottom:"+rect.bottom);
```
log 輸出結果:
```
03-02 16:48:39.669 24179-24179/com.sloop.canvas E/Rect: isRect:true| left:0.0| top:0.0| right:400.0| bottom:400.0
```
#### set
方法預覽:
``` java
public void set (Path src)
```
將新的path賦值到現有path。
``` java
canvas.translate(mWidth / 2, mHeight / 2); // 移動坐標系到屏幕中心
canvas.scale(1,-1); // <-- 注意 翻轉y坐標軸
Path path = new Path(); // path添加一個矩形
path.addRect(-200,-200,200,200, Path.Direction.CW);
Path src = new Path(); // src添加一個圓
src.addCircle(0,0,100, Path.Direction.CW);
path.set(src); // 大致相當于 path = src;
canvas.drawPath(path,mPaint);
```
<img src="http://ww2.sinaimg.cn/large/005Xtdi2jw1f1iv2lnnblj30u01hc3zc.jpg" width = "270" height = "480"/>
#### offset
方法預覽:
```java
public void offset (float dx, float dy)
public void offset (float dx, float dy, Path dst)
```
這個的作用也很簡單,就是對path進行一段平移,它和Canvas中的translate作用很像,但Canvas作用于整個畫布,而path的offset只作用于當前path。
**但是第二個方法最后怎么會有一個path作為參數?**
其實第二個方法中最后的參數dst是存儲平移后的path的。
| dst狀態 | 效果 |
| ----------- | ------------------------------ |
| dst不為空 | 將當前path平移后的狀態存入dst中,不會影響當前path |
| dst為空(null) | 平移將作用于當前path,相當于第一種方法 |
示例:
``` java
canvas.translate(mWidth / 2, mHeight / 2); // 移動坐標系到屏幕中心
canvas.scale(1,-1); // <-- 注意 翻轉y坐標軸
Path path = new Path(); // path中添加一個圓形(圓心在坐標原點)
path.addCircle(0,0,100, Path.Direction.CW);
Path dst = new Path(); // dst中添加一個矩形
dst.addRect(-200,-200,200,200, Path.Direction.CW);
path.offset(300,0,dst); // 平移
canvas.drawPath(path,mPaint); // 繪制path
mPaint.setColor(Color.BLUE); // 更改畫筆顏色
canvas.drawPath(dst,mPaint); // 繪制dst
```
<img src="http://ww3.sinaimg.cn/large/005Xtdi2gw1f1ix3vlwlwj30u01hcq3x.jpg" width = "270" height = "480"/>
從運行效果圖可以看出,雖然我們在dst中添加了一個矩形,但是并沒有表現出來,所以,當dst中存在內容時,dst中原有的內容會被清空,而存放平移后的path。
# 三.總結
本想一篇把path寫完,但是萬萬沒想到居然扯了這么多。本篇中講解的是直線部分和一些常用方法,下一篇將著重講解貝塞爾曲線和自相交圖形渲染等相關問題,敬請期待哦。
學完本篇之后又解鎖了新的境界,可以看看這位大神的文章[ Android雷達圖(蜘蛛網圖)繪制](http://blog.csdn.net/crazy__chen/article/details/50163693)

這個精小干練,非常適合新手練習使用,幫助大家更好的熟悉path的使用。
(,,? ? ?,,)
<b>PS: 由于本人水平有限,某些地方可能存在誤解或不準確,如果你對此有疑問可以提交Issues進行反饋。</b>
## About Me
### 作者微博: <a href="http://weibo.com/GcsSloop" target="_blank">@GcsSloop</a>
<a href="https://github.com/GcsSloop/AndroidNote/blob/magic-world/FINDME.md" target="_blank"> <img src="http://ww4.sinaimg.cn/large/005Xtdi2gw1f1qn89ihu3j315o0dwwjc.jpg" width=300/> </a>
## 參考資料
[Path](http://developer.android.com/reference/android/graphics/Path.html)<br/>
[Canvas](http://developer.android.com/reference/android/graphics/Canvas.html)<br/>
[android繪圖之Path總結](http://ghui.me/post/2015/10/android-graphics-path/)<br/>
- 0-發現
- AndroidInterview-Q-A
- Android能讓你少走彎路的干貨整理
- LearningNotes
- temp
- temp11
- 部分地址
- 0-待辦任務
- 待補充列表
- 0-未分類
- AndroidView事件分發與滑動沖突處理
- Spannable
- 事件分發機制詳解
- 1-Java
- 1-Java-01基礎
- 未歸檔
- 你應該知道的JDK知識
- 集合框架
- 1-Java-04合集
- Java之旅0
- Java之旅
- JAVA之旅01
- JAVA之旅02
- JAVA之旅03
- JAVA之旅04
- JAVA之旅05
- JAVA之旅06
- JAVA之旅07
- JAVA之旅08
- JAVA之旅09
- java之旅1
- JAVA之旅10
- JAVA之旅11
- JAVA之旅12
- JAVA之旅13
- JAVA之旅14
- JAVA之旅15
- JAVA之旅16
- JAVA之旅17
- JAVA之旅18
- JAVA之旅19
- java之旅2
- JAVA之旅20
- JAVA之旅21
- JAVA之旅22
- JAVA之旅23
- JAVA之旅24
- JAVA之旅25
- JAVA之旅26
- JAVA之旅27
- JAVA之旅28
- JAVA之旅29
- java之旅3
- JAVA之旅30
- JAVA之旅31
- JAVA之旅32
- JAVA之旅33
- JAVA之旅34
- JAVA之旅35
- 1-Java-05辨析
- HashMapArrayMap
- Java8新特性
- Java8接口默認方法
- 圖解HashMap(1)
- 圖解HashMap(2)
- 2-Android
- 2-Android-1-基礎
- View繪制流程
- 事件分發
- AndroidView的事件分發機制和滑動沖突解決
- 自定義View基礎
- 1-安卓自定義View基礎-坐標系
- 2-安卓自定義View基礎-角度弧度
- 3-安卓自定義View基礎-顏色
- 自定義View進階
- 1-安卓自定義View進階-分類和流程
- 10-安卓自定義View進階-Matrix詳解
- 11-安卓自定義View進階-MatrixCamera
- 12-安卓自定義View進階-事件分發機制原理
- 13-安卓自定義View進階-事件分發機制詳解
- 14-安卓自定義View進階-MotionEvent詳解
- 15-安卓自定義View進階-特殊形狀控件事件處理方案
- 16-安卓自定義View進階-多點觸控詳解
- 17-安卓自定義View進階-手勢檢測GestureDetector
- 2-安卓自定義View進階-繪制基本圖形
- 3-安卓自定義View進階-畫布操作
- 4-安卓自定義View進階-圖片文字
- 5-安卓自定義View進階-Path基本操作
- 6-安卓自定義View進階-貝塞爾曲線
- 7-安卓自定義View進階-Path完結篇偽
- 8-安卓自定義View進階-Path玩出花樣PathMeasure
- 9-安卓自定義View進階-Matrix原理
- 通用類介紹
- Application
- 2-Android-2-使用
- 2-Android-02控件
- ViewGroup
- ConstraintLayout
- CoordinatorLayout
- 2-Android-03三方使用
- Dagger2
- Dagger2圖文完全教程
- Dagger2最清晰的使用教程
- Dagger2讓你愛不釋手-終結篇
- Dagger2讓你愛不釋手-重點概念講解、融合篇
- dagger2讓你愛不釋手:基礎依賴注入框架篇
- 閱讀筆記
- Glide
- Google推薦的圖片加載庫Glide:最新版使用指南(含新特性)
- rxjava
- 這可能是最好的RxJava2.x入門教程完結版
- 這可能是最好的RxJava2.x入門教程(一)
- 這可能是最好的RxJava2.x入門教程(三)
- 這可能是最好的RxJava2.x入門教程(二)
- 這可能是最好的RxJava2.x入門教程(五)
- 這可能是最好的RxJava2.x入門教程(四)
- 2-Android-3-優化
- 優化概況
- 各種優化
- Android端秒開優化
- apk大小優化
- 內存分析
- 混淆
- 2-Android-4-工具
- adb命令
- 一鍵分析Android的BugReport
- 版本控制
- git
- git章節簡述
- 2-Android-5-源碼
- HandlerThread 源碼分析
- IntentService的使用和源碼分析
- 2-Android-9-辨析
- LRU算法
- 什么是Bitmap
- 常見圖片壓縮方式
- 3-Kotlin
- Kotlin使用筆記1-草稿
- Kotlin使用筆記2
- kotlin特性草稿
- Kotlin草稿-Delegation
- Kotlin草稿-Field
- Kotlin草稿-object
- 4-JavaScript
- 5-Python
- 6-Other
- Git
- Gradle
- Android中ProGuard配置和總結
- gradle使用筆記
- Nexus私服搭建
- 編譯提速最佳實踐
- 7-設計模式與架構
- 組件化
- 組件化探索(OKR)
- 1-參考列表
- 2-1-組件化概述
- 2-2-gradle配置
- 2-3-代碼編寫
- 2-4-常見問題
- 2-9-值得一讀
- 8-數據結構與算法
- 0臨時文件
- 漢諾塔
- 8-數據-1數據結構
- HashMap
- HashMap、Hashtable、HashSet 和 ConcurrentHashMap 的比較
- 遲到一年HashMap解讀
- 8-數據-2算法
- 1個就夠了
- Java常用排序算法(必須掌握的8大排序算法)
- 常用排序算法總結(性能+代碼)
- 必須知道的八大種排序算法(java實現)
- 9-職業
- 閱讀
- 書單
- 面試
- 面試-01-java
- Java面試題全集駱昊(上)
- Java面試題全集駱昊(下)
- Java面試題全集駱昊(中)
- 面試-02-android
- 40道Android面試題
- 面試-03-開源源碼
- Android圖片加載框架最全解析(二),從源碼的角度理解Glide的執行流程
- 面試-07-設計模式
- 面試-08-算法
- 面試-09-其他
- SUMMARY
- 版權說明
- temp111