Android之玩轉(zhuǎn)View(八):Path

請尊重原創(chuàng),轉(zhuǎn)載請注明出處【tianyl】的博客

關(guān)于的Android之玩轉(zhuǎn)View目錄

前言

在了解了Paint和Canvas之后,接下來就來說是Path,我是比較喜歡稱Paint、Canvas和Path為自定義繪制View時的三劍客,主要是它們在繪制我們想要View時,能起到非常重要的作用。

接下來,就說說Path的一些用法

1 Path的基礎(chǔ)用法

對于Path的基礎(chǔ)操作,我比較喜歡歸納為點操作、線操作和圖形操作

1.1 點操作

點操作的api主要有moveTo、rMoveTo,其實這兩個方法的作用比較類似,只有細微的不同

  • moveTo (x,y):移動下一個操作的起始點到坐標點(x,y)

例如:對于一個Path,它的默認起始點是(0,0),即屏幕左上角,當使用了moveTo(100,100),后,它的起始點就變成了坐標點(100,100)的位置

  • rMoveTo(x,y):移動下一個操作的起始點到當前點的相對位置的坐標(x,y),這個r就是relative的首字母,它表示相對位置

例如:對于一個Path,它的默認起始點是(0,0),即屏幕左上角,當使用了moveTo(100,100),后,它的起始點就變成了坐標點(100,100)的位置,這個時候,如果我們再使用moveTo(100,100),就沒有任何效果,因為我們的起始點已經(jīng)在坐標點(100,100)上了,如果用rMoveTo(100,100),那么起始點就會變成(200,200),這就是rMoveTo的用途了

1.2 線操作

說完了點操作,那么線操作也就比較簡單了,線操作的api也是兩個lineTo、rLineTo

  • lineTo (x,y):從起始點(默認是坐標原點(0,0))到坐標點(x,y)繪制一條線

  • rLineTo(x,y):從起始點(默認是坐標原點(0,0))到相對坐標點(x,y)繪制一條線,這個相對坐標也是當前的起始點,比如當前起始點是坐標點(100,100),那么實際的結(jié)束點就是坐標點(100+x,100+y)

除此之外,還有setLastPoint和close兩個api

  • setLastPoint(dx, dy):改變之前操作的終點的位置

例如:


//初始化Path

Path path = new Path();

//畫從(0,0)到(400,400)之間的直線

path.lineTo(400, 400);

//新加的setLastPoint

path.setLastPoint(100, 800);

path.lineTo(400, 800);

canvas.drawPath(path, mPaint);

這段代碼等價于


//初始化Path

Path path = new Path();

//畫從(0,0)到(100,400)之間的直線

path.lineTo(100, 800);

path.lineTo(400, 800);

canvas.drawPath(path, mPaint);

關(guān)于對api setLastPoint的解釋,我查了一下資料,網(wǎng)上說moveTo影響的是后面操作的起點位置。不會影響之前的操作;而 setLastPoint改變前一步操作最后一個點的位置,不僅影響前一步操作,同一時候也會影響后一步操作。其實我感覺因為它可以直接被之前的lineTo取代,即直接改lineTo的傳遞的參數(shù)即可,所以這個方法我用得并不多

還有一個方法

  • clost():封閉當前的繪制路徑,這個api很簡單,也算比較常用

1.3 圖形操作

接下來就是Path的圖形操作了,也是我們經(jīng)常用來進行圖形繪制的api,它們分別是

  • 繪制圓:addCircle

  • 繪制橢圓:addOval

  • 繪制矩形:addRect

  • 繪制圓角矩形:addRoundRect

對于這些api,除了它們各自特定的參數(shù)之外,還會有一個相同的參數(shù)Direction

對于參數(shù)Direction,它有兩個選項

  • Direction.CW:按照逆時針方向繪制

  • Direction.CCW:按照順時針方向繪制

對于這個參數(shù),在大多數(shù)情況下,我們使用順時針繪制和逆時針繪制都不會影響最終結(jié)果,只是在少數(shù)情況下,比如如果使用了setLastPoint這個方法,那么就會因為繪制方向的不同得到不同的結(jié)果,或者我們需要根據(jù)繪制的方向來自定義一些動畫,或者使用繪制的路徑,這時也需要考慮我們繪制的方向造成的影像。

2 貝塞爾曲線

說完了Path的基礎(chǔ)api,再說說Path稍微難一點的用法,這也是Android中實現(xiàn)一些比較炫酷特效的方法——貝塞爾曲線。

說到貝塞爾曲線,就先簡單的介紹一下什么是貝塞爾曲線

2.1 介紹

關(guān)于貝塞爾曲線的介紹,這里參考維基百科

線性曲線

線性貝塞爾曲線函數(shù)中的t會經(jīng)過由P0至P1的B(t)所描述的曲線。例如當t=0.25時,B(t)即一條由點P0至P1路徑的四分之一處。就像由0至1的連續(xù)t,B(t)描述一條由P0至P1的直線

一階貝塞爾曲線

二次曲線

為建構(gòu)二次貝塞爾曲線,可以中介點Q0和Q1作為由0至1的t:

由P0至P1的連續(xù)點Q0,描述一條線性貝塞爾曲線。

由P1至P2的連續(xù)點Q1,描述一條線性貝塞爾曲線。

由Q0至Q1的連續(xù)點B(t),描述一條二次貝塞爾曲線。

二階貝塞爾曲線

高階曲線

為建構(gòu)高階曲線,便需要相應(yīng)更多的中介點。對于三次曲線,可由線性貝塞爾曲線描述的中介點Q0、Q1、Q2,和由二次曲線描述的點R0、R1所建構(gòu)


三階貝塞爾曲線

2.2 Android 中的貝塞爾曲線

介紹了貝塞爾曲線的基礎(chǔ)知識,接下來說說Android中的貝塞爾曲線,Android系統(tǒng)已經(jīng)封裝好了二次貝塞爾曲線和三次貝塞爾曲線的api,我們可以直接使用

  • 二次貝塞爾曲線:quadTo()

  • 三次貝塞爾曲線:cubicTo()

這兩個api就是Android系統(tǒng)提供給我們使用的api,當然,如果想要使用更高階的貝塞爾曲線,那么就需要自己去實現(xiàn)了(一般情況下,這兩個api就已經(jīng)夠用了),接下來我就通過一個簡單水波紋的例子,說明貝塞爾曲線在Android中的應(yīng)用

2.2.1 通過貝塞爾曲線實現(xiàn)水波紋效果

首先,在寫代碼之前,我們要先確定如何通過一個貝塞爾曲線繪制出一個水波紋,首先,我們需要繪制出一個波,然后我們對這個波進行移動,那么就是我們想要的動畫效果了

如圖所示

設(shè)計思路

這是我們實現(xiàn)水波紋的基本思路

  1. 首先我們移動到波的起始點(x,y)

  2. 假設(shè)一個波的完整長度是waveLength,波的高度是waveHeight

  3. 首先從波的起始點(x,y),控制點為1,繪制貝塞爾曲線到中間點(x1,y1),然后在使用控制點2,從中間點繪制貝塞爾曲線到結(jié)束點(x2,y2)

  4. 假設(shè)這個波的一個規(guī)則的正弦波,那么起始點的坐標為(0,height/2),控制點1的坐標為(waveLength/4,0),中間點的坐標為(waveHeight/2,height/2),控制點2的坐標為(3*waveLength/4,height),結(jié)束點的坐標為(waveLength,height/2)

  5. 封閉繪制路徑

具體代碼如下


Path path = new Path();

//步驟1

path.moveTo(0, height/2);

//步驟3 注意這里使用的是rQuadTo,也就是相對當前的位置

path.rQuadTo(waveLength / 4, -height/2, waveLength / 2,0);

path.rQuadTo(waveLength / 4, height/2, waveLength / 2, 0);

//步驟4

path.lineTo(mWidth, mHeight);

path.lineTo(0, mHeight);

path.close();

canvas.drawPath(path, mPaint);

具體效果如下(為了效果突出,所以設(shè)置的波高較大))

效果圖
  1. 完成了靜態(tài)的波,接下來就只需要將這個波進行平移即可,之前我們波的起始點是(x,y),我們通過不斷的平移這個起始點,就可以實現(xiàn)動畫的效果,當然為了避免邊界的空白,我們一般可以從view外的開始繪制,然后超出view,這樣就通過一個貝塞爾曲線實現(xiàn)了水波紋效果

//這里移動到-waveLength是為了超出屏幕外,避免動畫的時候出現(xiàn)空白,offsetX就是波的偏移量,只要不斷的修改這個偏移量,就能實現(xiàn)動畫效果
path.moveTo(-waveLength + offsetX, mHeight / 2);
//繪制到超出屏幕即可
for (int i = -waveLength; i < getWidth() + waveLength; i += waveLength) {
   path.rQuadTo(waveLength / 4, -height/2, waveLength / 2,0);
   path.rQuadTo(waveLength / 4, height/2, waveLength / 2, 0);
}

下面是添加了動畫效果后的完整draw代碼

private void drawWave(Canvas canvas) {
    Path path = new Path();
    path.moveTo(-mWaveWidth + offsetX, mHeight / 2);
    //注意這里使用的是rQuadTo,也就是相對當前的位置
    for (int i = -mWaveWidth; i < getWidth() + mWaveWidth; i += mWaveWidth) {
        path.rQuadTo(mWaveWidth / 4, -mHeight / 2, mWaveWidth / 2, 0);
        path.rQuadTo(mWaveWidth / 4, mHeight / 2, mWaveWidth / 2, 0);
    }
    path.lineTo(mWaveWidth, mHeight);
    path.lineTo(0, mHeight);
    path.close();
    canvas.drawPath(path, mPaint);
}

效果如下


水波紋
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容