Path在自定義控件裡的使用頻率也相當的高,使用Path不僅能繪制矩形、圓形等正常圖形,還能繪制比較複雜的組合路徑。
基本方法
點的相關操作
- moveTo:移動到哪一點
- lineTo:連接配接到哪一點
- rMoveTo:移動到哪一點(參考點為目前點)
- rlineTo:連接配接到哪一點(參考點為目前點)
- setLastPoint:設定終點位置
- close:連接配接開始的點和末尾的點,使路徑閉合
圖形相關操作
- addRect:在目前Path中添加一個矩形
- addRoundRect:在目前Path中添加一個圓角矩形
- addOval:在目前Path中添加一個橢圓
- addCircle:在目前Path中添加一個圓
- addArc:在目前Path添加一個圓弧
- arcTo:在目前Path添加一個圓弧(會連接配接上一個操作點)
其它
- offset:對目前Path進行偏移
- reset:清除内容
- rewind:清除内容(保留資料結構)
貝塞爾曲線
這個是個比較燒腦的知識點,因為它需要有一定的數學知識,不過我們大部分情況下隻需要了解下其含義,調用Android提供的API即可,三次以上的貝塞爾曲線也很少用到。貝塞爾曲線使得線段或者曲線能夠使用數學的方式進行描述,這樣就可以友善的在計算機上繪圖了。
公式及含義
從百科上給大家摘錄了一段關于貝塞爾曲線的講解
給定點P0、P1,線性貝茲曲線隻是一條兩點之間的直線。這條線由下式給出:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CX51keNhXUE10MNpHW3BjMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2LcRHelR3LcJzLctmch1mclRXY39jNzIjM0ATM1EDNxUDM4EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
且其等同于線性插值。
二次方公式
二次方貝茲曲線的路徑由給定點P0、P1、P2的函數B(t)追蹤:
TrueType字型就運用了以貝茲樣條組成的二次貝茲曲線。
三次方公式
P0、P1、P2、P3四個點在平面或在三維空間中定義了三次方貝茲曲線。曲線起始于P0走向P1,并從P2的方向來到P3。一般不會經過P1或P2;這兩個點隻是在那裡提供方向資訊。P0和P1之間的間距,決定了曲線在轉而趨進P3之前,走向P2方向的“長度有多長”。
曲線的參數形式為:
現代的成象系統,如PostScript、Asymptote和Metafont,運用了以貝茲樣條組成的三次貝茲曲線,用來描繪曲線輪廓。
一般參數公式
階貝茲曲線可如下推斷。給定點P0、P1、…、Pn,其貝茲曲線即:
如上公式可如下遞歸表達: 用表示由點P0、P1、…、Pn所決定的貝茲曲線。
API表現形式
Android中提供了一級到三級貝塞爾曲線API分别如下:
- lineTo:畫直線,一階貝塞爾曲線形式
- quadTo:畫曲線,二階貝塞爾曲線形式
- cubicTo:畫曲線,三階貝塞爾曲線形式
- rLineTo:畫直線,一階貝塞爾曲線形式,參考點為目前點
- rQuadTo:畫曲線,二階貝塞爾曲線形式,參考點為目前點
- rCubicTo:畫曲線,三階貝塞爾曲線形式,參考點為目前點
使用場景
貝塞爾曲線的使用場景非常的豐富,比如我們常見的粘連體開發(如QQ删除泡泡)、彈性體、波浪曲線等,複雜的曲線變化效果的場景裡都少不了貝塞爾曲線的身影。貝塞爾曲線後期有專門的demo示範
Path邏輯運算
Path邏輯運算通過其op方法來實作的,它有5種類型:
public enum Op {
/**
* Subtract the second path from the first path.
*/
DIFFERENCE,
/**
* Intersect the two paths.
*/
INTERSECT,
/**
* Union (inclusive-or) the two paths.
*/
UNION,
/**
* Exclusive-or the two paths.
*/
XOR,
/**
* Subtract the first path from the second path.
*/
REVERSE_DIFFERENCE
}
主要方法如下:
public boolean op(Path path, Op op) {
return op(this, path, op);
}
public boolean op(Path path1, Path path2, Op op) {
if (native_op(path1.mNativePath, path2.mNativePath, op.ordinal(), this.mNativePath)) {
isSimplePath = false;
rects = null;
return true;
}
return false;
}
從上面可以看出op(Path path, Op op)實際調用的是op(Path path1, Path path2, Op op) ,第一個Path參數為調用者本身,假如設為A,傳入的Path參數為需要參與運算的Path,設為B,那麼A.op(B,op)在不同的op參數下結果如下:
- Op.DIFFERENCE:A與A交B的內插補點,
- Op.INTERSECT:A與B的交集
- Op.UNION:A與B的并集
- Op.XOR:A與B的異或
- Op.REVERSE_DIFFERENCE:B與A交B的內插補點,通過名字也可以看出來與Op.DIFFERENCE進行了反轉。
邏輯運算demo的核心代碼如下:
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
mPathA.reset();
mPathB.reset();
mPathA.addRect(, , (int) (mWidth * ), (int) (mHeight * ), Path.Direction.CW);
mPathB.addCircle((int) (mWidth * ), (int) (mHeight * ), (int) (mHeight * ), Path.Direction.CW);
mPathA.op(mPathB, mOp);
canvas.drawPath(mPathA, mPaint);
}
效果圖如下:
FillType
FillType跟上面的邏輯運算有一定的類似,都可用于圖形相交的情況,隻不過op用于Path之間的,而FillType用于的Path内部,FillType有以下4種類型:
public enum FillType {
// these must match the values in SkPath.h
/**
* Specifies that "inside" is computed by a non-zero sum of signed
* edge crossings.
*/
WINDING (),
/**
* Specifies that "inside" is computed by an odd number of edge
* crossings.
*/
EVEN_ODD (),
/**
* Same as {@link #WINDING}, but draws outside of the path, rather than inside.
*/
INVERSE_WINDING (),
/**
* Same as {@link #EVEN_ODD}, but draws outside of the path, rather than inside.
*/
INVERSE_EVEN_ODD();
FillType(int ni) {
nativeInt = ni;
}
final int nativeInt;
}
不同的FillType下的結果如下:
FillType.WINDING:取圖形的相交部分,這個是FillType的預設值。
FillType.EVEN_ODD:取圖形的不相交部分
FillType.INVERSE_WINDING :取圖形的相交之外的部分
FillType.INVERSE_EVEN_ODD:取圖形的不相交之外的部分
FillType示範demo的核心代碼如下:
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
mPath.reset();
mPath.addRect(, , (int) (mWidth * ), (int) (mHeight * ), Path.Direction.CW);
mPath.addCircle((int) (mWidth * ), (int) (mHeight * ), (int) (mHeight * ), Path.Direction.CW);
mPath.setFillType(mFillType);
canvas.drawPath(mPath, mPaint);
}
效果圖如下:
PathMeasure
PathMeasure這個類的主要方法如下:
主要方法的對應含義如下:
- setPath:設定要測量的路徑,第二個形參代表是否強制閉合
- getLength:傳回目前Path的長度
- getPosTan:獲得對應長度的點的坐标以及tan值
- getMatrix:獲得對應長度點的矩陣資訊
- getSegment:截取兩個點中間的路徑,其中前兩個參數分别為這兩個點到起始點的距離
PathMeasure看似簡單,卻是android自定義控件裡是非常重要的一部分,大部分跟路徑相關的具有動畫效果的自定義控件都需要用到PathMeasure看似簡單的相關知識。前面寫過的仿鍊家splash,就是用了getSegment這一方法來截取路徑。
為了大家更好的了解path的相關知識點,後期将做一個Path綜合練習,同時使用貝塞爾曲線、PathMeasure、邏輯運算等盡可能多的知識點。
示範demo位址