二、Android繪制知識總結(路徑篇)

Path即路徑,可以將其想象成一條或多條線段,它們可能是直的,也可能是彎曲的。一般來說只有一條,即連貫的,除非以下兩種情況:

1、調用addXXX方法,直接添加固定形狀的路徑。
2、調用moveTo函數改變繪制起始位置。

1、Path的方向

Path.Direction,用于將封閉形狀(例如矩形,橢圓形)添加到Path時,計算其內部像素點是被填充,還是被鏤空
Path.Direction.CW表示順時針。
Path.Direction.CCW表示逆時針。

注意:計算規則根據FillType不同而不同


2、Path填充模式

setFillType(FillType fillType)

FileType有以下取值:WINDING(默認值)EVEN_ODDINVERSE_WINDINGINVERSE_EVEN_ODD

1、WINDING(默認值)

這里畫了6個圖形,1和2是關于y=300對稱,方向相反,其余的都是以(300,300)為中心,他們的邊界已用黑線在圖中標記。

image.png

WINDING判斷一個像素點是被填充,還是被鏤空的規則如下:

1、從該點引出一條假想線到外層區域,如圖中紅線。
2、計算從該點出發所穿過的邊界,如果遇到順時針,則+1,逆時針,則-1。
3、計算的結果,如果為0,則表示該點被鏤空,否則該點被填充顏色。

2、EVEN_ODD
在原先代碼中,設置FileType為EVEN_ODD

image.png

EVEN_ODD判斷一個像素點是被填充,還是被鏤空的規則如下:

1、從該點引出一條假想線到外層區域。
2、計算從該點出發所穿過的邊界數量。
3、如果穿過數量為奇數,表示被填充,偶數則被鏤空。

3、INVERSE_WINDING
在原先代碼中,設置FileType為INVERSE_WINDING

image.png

INVERSE_WINDING判斷一個像素點是被填充,還是被鏤空的規則如下:

1、從該點引出一條假想線到外層區域。
2、計算從該點出發所穿過的邊界,如果遇到順時針,則+1,逆時針,則-1。
3、計算的結果,如果為0,則表示該點被填充顏色,否則該點被鏤空。

4、INVERSE_EVEN_ODD
在原先代碼中,設置FileType為INVERSE_EVEN_ODD

image.png

INVERSE_EVEN_ODD判斷一個像素點是被填充,還是被鏤空的規則如下:

1、從該點引出一條假想線到外層區域。
2、計算從該點出發所穿過的邊界數量。
3、如果穿過數量為偶數,表示被填充,奇數則被鏤空。


3、重置路徑

rewind()
清除FillType及所有的直線、曲線、點的數據,但會保留數據結構,這樣可以實現快速重用,提高一定的性能。
reset()
新建一個路徑對象,它的所有數據空間都會被回收并重新分配,但不會清除FillType。
對比:rewind()函數不會清除內存,但會清除FillType;而reset()函數會清除內存,但不會清除FillType


4、PathMeasure

PathMeasure是Path的一個輔助類,雖然它跟繪制無關,但它有以下2個主要作用:
1、獲取Path中某個子線段。
2、獲取Path中某個位置的坐標和切線角度。
這意味著它配合屬性動畫,能夠實現各種Path特效。

PathMeasure需要關聯一個創建好的path,以及指定forceClose,表示Path是否需要閉合,它會影響path的測量結果。

1、獲取路徑的長度
float getLength()

2、分割路徑
boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)
startWithMoveTo為true時,表示將dst中的終點,設置為本次分割結果的起始點。

  • 截取的結果跟Path的方向有關。
  • 會將截取的Path片段添加到dst中,而不是替換dst中的內容。

3、獲取路徑上某點的坐標和切線
3.1、方式1:
boolean getPosTan(float distance, float pos[], float tan[])
pos[0]、pos[1]分別表示當前distance位置的x,y坐標
tan表示該點的切線與單位圓相交的坐標點,可通過以下公式求出角度。
double degress = Math.atan2(tan[0], tan[1]) * 180.0 / Math.PI;
3.2、方式2:
boolean getMatrix(float distance, Matrix matrix, int flags)
這個函數用于得到路徑上某一長度的位置以及該位置的正切值的矩陣。
flags可指定以下參數,并且可通過|來同時傳入:
POSITION_MATRIX_FLAG:表示獲取位置信息
TANGENT_MATRIX_FLAG:表示獲取切線信息

4、跳轉到下一條線段
boolean nextContour()
由于Path可以包含多條線段,但getLength()、getSegment()還是其他函數,都只針對第一條線段進行計算,而nextContour()就是用于跳轉到下一條曲線的函數。


5、貝塞爾曲線

二階貝塞爾曲線
B(t) = (1 - t)^2 * P_0 + 2t * (1 - t) * P_1 + t^2 * P_2, t ∈ [0,1]
void quadTo(float x1, float y1, float x2, float y2)
void rQuadTo(float dx1, float dy1, float dx2, float dy2)

三階貝塞爾曲線
B(t) = P_0 * (1-t)^3 + 3 * P_1 * t * (1-t)^2 + 3 * P_2 * t^2 * (1-t) + P_3 * t^3, t ∈ [0,1]
void cubicTo(float x1, float y1, float x2, float y2, float x3, float y3)
void rCubicTo(float x1, float y1, float x2, float y2, float x3, float y3)

注意:函數前帶r的貝塞爾函數,表示坐標位置都是相對位置,參照于Path中前一個線段的終點。

下面解釋幾種貝塞爾曲線在真實場景下的實際應用。

5.1、平滑線條

利用貝塞爾曲線,可以改善書寫時的邊緣鋸齒。

public class DrawBezierView extends View {
    private Path mPath;
    private Paint mPaint;
    private float mPreX;
    private float mPreY;

    public DrawBezierView(Context context) {
        super(context);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(5);
        mPath = new Path();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mPreX = event.getX();
                mPreY = event.getY();
                mPath.moveTo(mPreX, mPreY);
                break;
            case MotionEvent.ACTION_MOVE:
                float endX = (event.getX() + mPreX) / 2;
                float endY = (event.getY() + mPreY) / 2;
                mPath.quadTo(mPreX, mPreY, endX, endY);
                mPreX = event.getX();
                mPreY = event.getY();
                invalidate();
                break;
        }
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawPath(mPath, mPaint);
    }
}

5.2、波浪

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容