天天看點

PathMeasure之迷徑追蹤

Path,不論是在自定義View還是動畫,都占有舉足輕重的地位。繪制Path,可以通過Android提供的API,或者是貝塞爾曲線、數學函數、圖形組合等等方式,而要擷取Path上每一個構成點的坐标,一般需要知道Path的函數方法,例如求解貝塞爾曲線上的點的De Casteljau算法,但對于一般的Path來說,是很難通過簡單的函數方法來進行計算的,那麼,如何來定位任意一個給定Path的任意一個點的坐标呢?

Android SDK提供了一個非常有用的API來幫助開發者實作這樣一個Path路徑點的坐标追蹤,這個類就是PathMeasure,它可以認為是一個Path的坐标電腦。

PathMeasure類似一個電腦,對它進行初始化隻需要new一個PathMeasure對象即可:

初始化PathMeasure後,可以通過PathMeasure.setPath()的方式來将Path和PathMeasure進行綁定,例如:

當然,你也可以直接使用PathMeasure的有參構造方法來進行初始化:

這裡最不容易了解的就是第二個boolean參數forceClosed。

這個參數——forceClosed,簡單的說,就是Path最終是否需要閉合,如果為True的話,則不管關聯的Path是否是閉合的,都會被閉合。

但是這個參數對Path和PathMeasure的影響是需要解釋下的:

forceClosed參數對綁定的Path不會産生任何影響,例如一個折線段的Path,本身是沒有閉合的,forceClosed設定為True的時候,PathMeasure計算的Path是閉合的,但Path本身繪制出來是不會閉合的。

forceClosed參數對PathMeasure的測量結果有影響,還是例如前面說的一個折線段的Path,本身沒有閉合,forceClosed設定為True,PathMeasure的計算就會包含最後一段閉合的路徑,與原來的Path不同。

PathMeasure的API非常容易了解,幾乎都是望文生義。

PathMeasure.getLength()的使用非常廣泛,其作用就是擷取計算的路徑長度。

這個API用于截取整個Path的片段,通過參數startD和stopD來控制截取的長度,并将截取的Path儲存到dst中,最後一個參數startWithMoveTo表示起始點是否使用moveTo方法,通常為True,保證每次截取的Path片段都是正常的、完整的。

如果startWithMoveTo設定為false,通常是和dst一起使用,因為dst中儲存的Path是被不斷添加的,而不是每次被覆寫,設定為false,則新增的片段會從上一次Path終點開始計算,這樣可以儲存截取的Path片段數組連續起來。

nextContour()方法用的比較少,比較大部分情況下都隻會有一個Path而不是多個,畢竟這樣會增加Path的複雜度,但是如果真有一個Path,包含了多個Path,那麼通過nextContour這個方法,就可以進行切換,同時,預設的API,例如getLength,擷取的也是目前的這段Path所對應的長度,而不是所有的Path的長度,同時,nextContour擷取Path的順序,與Path的添加順序是相同的。

這個API用于擷取路徑上某點的坐标及其切線的坐标,這個API非常強大,但是比較難了解,後面會結合例子來講解。

簡單的說,就是通過指定distance(0

由于硬體加速的問題,PathMeasure中的getSegment在講Path添加到dst數組中時會被導緻一些錯誤,需要通過mDst.lineTo(0,0)來避免這樣一個Bug。

路徑繪制是PathMeasure最常用的功能,其原理就是通過getSegment來不斷截取Path片段,進而不斷繪制完整的路徑,效果如圖所示:

PathMeasure之迷徑追蹤

代碼如下所示:

通過這種方式,隻需要做一點點小的修改,就可以完成一個比較有意思的loading圖,效果如下所示:

PathMeasure之迷徑追蹤

我們隻需要修改下起始值的數字即可,關鍵代碼如下:

關于路徑繪制,View的始祖Romain Guy曾經有一篇文章講解了一個很使用的技巧,位址如下所示:

<a href="http://www.curious-creature.com/2013/12/21/android-recipe-4-path-tracing/">http://www.curious-creature.com/2013/12/21/android-recipe-4-path-tracing/</a>

Romain Guy使用DashPathEffect來實作了路徑繪制。

DashPathEffect傳入了一個intervals數組,用來控制實線和虛線的數組的顯示,那麼當實線和虛線都是整個路徑的長度時,整個路徑就隻顯示實線或者虛線了,這時候通過第二個參數phase來控制起始偏移量,就可以完成整個路徑的繪制了,這的确是一個非常trick而且有效的方式,效果如圖所示:

PathMeasure之迷徑追蹤

其關鍵代碼就是在于設定:

後面通過屬性動畫來控制路徑繪制即可。

PathMeasure的getPosTan()方法,可以擷取路徑上的坐标點和對應點的切線坐标,其中,路徑上對應的點非常好了解,就是對應的點的坐标,而另一個參數tan[]數組,它用于傳回目前點的運動軌迹的斜率,要了解這個API,我們首先來看下Math中的atan2這個方法:

雖然atan()方法可以用于求一個反正切值,但是他傳入的是一個角度,是以我們使用atan2()方法:

Math.atan2()函數傳回點(x,y)和原點(0,0)之間直線的傾斜角

那麼如何計算任意兩點間直線的傾斜角呢?隻需要将兩點x,y坐标分别相減得到一個新的點(x2-x1,y2-y1)。然後利用它求出角度即可——Math.atan2(y2-y1,x2-x1)。

利用這個API,通常可以擷取Path上的點坐标和點的運動趨勢,對于運動趨勢,通常通過Math.atan2()來轉換為切線的角度,代碼如下所示:

根據這個API,我們可以模拟一個圓上的點和點的運動趨勢,代碼如下:

Demo效果如圖所示:

PathMeasure之迷徑追蹤

隻不過這裡在繪制的時候,使用了一些Trick,先通過canvas.translate方法将原點移動的圓心,同時,通過canvas.rotate将運動趨勢的角度轉換為畫布的旋轉,這樣每次繪制切線,就隻需要畫一條同樣的切線即可。

源碼已上傳到Github:

<a href="https://github.com/xuyisheng/PathArt">https://github.com/xuyisheng/PathArt</a>

繼續閱讀