天天看點

閑聊自定義控件之基礎——Path

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,線性貝茲曲線隻是一條兩點之間的直線。這條線由下式給出:

閑聊自定義控件之基礎——Path

且其等同于線性插值。

二次方公式

二次方貝茲曲線的路徑由給定點P0、P1、P2的函數B(t)追蹤:

閑聊自定義控件之基礎——Path

TrueType字型就運用了以貝茲樣條組成的二次貝茲曲線。

三次方公式

P0、P1、P2、P3四個點在平面或在三維空間中定義了三次方貝茲曲線。曲線起始于P0走向P1,并從P2的方向來到P3。一般不會經過P1或P2;這兩個點隻是在那裡提供方向資訊。P0和P1之間的間距,決定了曲線在轉而趨進P3之前,走向P2方向的“長度有多長”。

曲線的參數形式為:

閑聊自定義控件之基礎——Path

現代的成象系統,如PostScript、Asymptote和Metafont,運用了以貝茲樣條組成的三次貝茲曲線,用來描繪曲線輪廓。

一般參數公式

階貝茲曲線可如下推斷。給定點P0、P1、…、Pn,其貝茲曲線即:

閑聊自定義控件之基礎——Path

如上公式可如下遞歸表達: 用表示由點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);
}
           

效果圖如下:

閑聊自定義控件之基礎——Path

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);
}
           

效果圖如下:

閑聊自定義控件之基礎——Path

PathMeasure

PathMeasure這個類的主要方法如下:

閑聊自定義控件之基礎——Path

主要方法的對應含義如下:

- setPath:設定要測量的路徑,第二個形參代表是否強制閉合

- getLength:傳回目前Path的長度

- getPosTan:獲得對應長度的點的坐标以及tan值

- getMatrix:獲得對應長度點的矩陣資訊

- getSegment:截取兩個點中間的路徑,其中前兩個參數分别為這兩個點到起始點的距離

PathMeasure看似簡單,卻是android自定義控件裡是非常重要的一部分,大部分跟路徑相關的具有動畫效果的自定義控件都需要用到PathMeasure看似簡單的相關知識。前面寫過的仿鍊家splash,就是用了getSegment這一方法來截取路徑。

為了大家更好的了解path的相關知識點,後期将做一個Path綜合練習,同時使用貝塞爾曲線、PathMeasure、邏輯運算等盡可能多的知識點。

示範demo位址