Android動(dòng)畫 - PathMeasure打造不一樣的動(dòng)畫
PathMeasures是什么
顧名思義,PathMeasure是一個(gè)用來測(cè)量Path的類
構(gòu)造函數(shù)
| 構(gòu)造函數(shù) | 方法描述 |
|---|---|
| PathMeasure() | 創(chuàng)建一個(gè)空的PathMeasure對(duì)象。 |
| PathMeasure(Path path, boolean forceClosed) | 創(chuàng)建與指定路徑對(duì)象(已經(jīng)創(chuàng)建并指定)關(guān)聯(lián)的PathMeasure對(duì)象。 |
公共方法
| 返回值 | 方法名稱 |
|---|---|
| float | getLength() |
| boolean | getMatrix(float distance, Matrix matrix, int flags) |
| boolean | getPosTan(float distance, float[] pos, float[] tan) |
| boolean | getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo) |
| boolean | isClosed() |
| boolean | nextContour() |
| void | setPath(Path path, boolean forceClosed) |
接下來分別介紹下以上的方法
1.構(gòu)造函數(shù)
無參構(gòu)造函數(shù):
PathMeasure()
創(chuàng)建一個(gè)空的PathMeasure,要使用它來測(cè)量路徑長(zhǎng)度,或查找路徑的位置和切線,需要調(diào)用setPath。一旦路徑與測(cè)量對(duì)象相關(guān)聯(lián),Path進(jìn)行了更改,需要重新調(diào)用setPath方法。
有參構(gòu)造函數(shù)
PathMeasure(Path path, boolean forceClosed)
創(chuàng)建與指定路徑對(duì)象關(guān)聯(lián)的PathMeasure對(duì)象。現(xiàn)在,測(cè)量對(duì)象可以返回路徑的長(zhǎng)度以及路徑上任何位置的位置和切線。同上,一旦路徑與測(cè)量對(duì)象相關(guān)聯(lián),Path進(jìn)行了更改,需要重新調(diào)用setPath方法。
forceClosed:如果為true,該路徑也將被視為“閉合”。
forceClosed不會(huì)影響Path本身的狀態(tài)。但是會(huì)影響測(cè)量結(jié)果。
下面我們舉個(gè)例子:
canvas.translate(mWidth/2,mHeight/2);
Path path = new Path();
path.lineTo(0,200);
path.lineTo(200,200);
path.lineTo(200,0);
PathMeasure measure1 = new PathMeasure(path,false);
PathMeasure measure2 = new PathMeasure(path,true);
Log.e("TAG", "forceClosed=false---->"+measure1.getLength());
Log.e("TAG", "forceClosed=true----->"+measure2.getLength());
canvas.drawPath(path,mDeafultPaint);
log如下:
E/TAG: forceClosed=false---->600.0
E/TAG: forceClosed=true----->800.0
繪制的效果如下:

2.公共方法
getLength
返回當(dāng)前Path的總長(zhǎng)度;如果沒有路徑與此度量對(duì)象關(guān)聯(lián),則返回0。
isClosed
如果當(dāng)前Path為close(),則返回true。
setPath
Path與PathMeasure進(jìn)行關(guān)聯(lián)。
主要講一下以下幾個(gè)方法:
nextContour
public boolean nextContour()
獲取在路徑中下一個(gè)輪廓,如果有下一個(gè)輪廓,則返回true,且PathMeasure切至下一個(gè)輪廓的數(shù)據(jù);如果沒有下一個(gè)輪廓?jiǎng)t返回false。
我的理解一次moveTo增加一個(gè)輪廓。
getSegment
public boolean getSegment(float startD,float stopD,Path dst,boolean startWithMoveTo)
給定起點(diǎn)和終點(diǎn)的距離,請(qǐng)返回中間的路段。如果段的長(zhǎng)度為零,則返回false,否則返回true。startD和stopD固定取值范圍(0,getLength())。如果startD> = stopD,則返回false(并保持dst不變)。如果startWithMoveTo為true,則以moveTo開頭。
| 參數(shù) | 作用 |
|---|---|
| startD | 開始截取的位置距離Path起點(diǎn)的距離 |
| stopD | 結(jié)束截取的位置距離Path起點(diǎn)的距離 |
| dst | 截取的Path會(huì)添加到dst中 |
| startWithMoveTo | 起點(diǎn)是否啟用moveTo |
startD 和 stopD 的取值范圍 0 <= startD < stopD <= getLength
startWithMoveTo: 截取的片段的第一個(gè)點(diǎn)是否保持不變;
設(shè)置為true:保持截取的片段不變,添加至dst路徑中;
設(shè)置為false:會(huì)將截取的片段的起始點(diǎn)移至dst路徑中的最后一個(gè)點(diǎn),讓dst路徑保持連續(xù)
舉個(gè)例子:startWithMoveTo 為flase
canvas.translate(width/2,height/2);
Path mPath = new Path();
Path mDst = new Path();
PathMeasure mPathMeasure = new PathMeasure();
// 順時(shí)針畫 半徑為400px的圓
mPath.addCircle(0,0, 400, Path.Direction.CW);
mPathMeasure.setPath(mPath, false);
// 畫直線
mDst.moveTo(110, 0);
mDst.lineTo(200, 300);
// 截取 0.25 到 0.5 距離的圓弧放置dst中
mPathMeasure.getSegment(mPathMeasure.getLength() * 0.25f,
mPathMeasure.getLength() * 0.5f,
mDst,
false);
canvas.drawPath(mDst, paint);
效果圖如下:

startWithMoveTo 為true
效果圖如下:

getPosTan
public boolean getPosTan(float distance, float pos[], float tan[])
| 參數(shù) | 作用 |
|---|---|
| distance | 即需要的測(cè)量點(diǎn)與當(dāng)前path起始位置的距離 |
| pos | 測(cè)量點(diǎn)的坐標(biāo),pos[0]為x坐標(biāo),pos[1]為y坐標(biāo) |
| tan | 測(cè)量點(diǎn)的正余弦值,tan[0]為cos,即余弦值或稱為單位圓的x坐標(biāo);tan[1]為sin,即正弦值或稱為單位圓的y坐標(biāo); |
distance 取值范圍:0<=distance<=getLength()
A(x,y)原點(diǎn)為O,cos = OA/OB,sin = OA/AB
getMatrix
public boolean getMatrix(float distance, Matrix matrix, int flags)
| 參數(shù) | 作用 |
|---|---|
| distance | 即需要的測(cè)量點(diǎn)與當(dāng)前path起始位置的距離 |
| matrix | 根據(jù) falgs 封裝好的matrix |
| flags | 規(guī)定哪些內(nèi)容會(huì)存入到matrix中 |
flags 有POSITION_MATRIX_FLAG(位置) 和 ANGENT_MATRIX_FLAG(正切)兩種
其實(shí)這個(gè)方法就相當(dāng)于getPosTan的封裝 matrix 的過程由 getMatrix 替我們做了,我們可以直接得到一個(gè)封裝好到 matrix。
實(shí)戰(zhàn)
加載動(dòng)畫(一)
效果圖

主要利用PathMeasure的getSegment方法來截取路徑,繪制該動(dòng)畫
思路
1.先勾勒出一個(gè)順時(shí)針的空心圓,然后生成pathMeasure對(duì)象
// 勾勒空心圓
path.addCircle(width / 2, height / 2, radius, Path.Direction.CW);
// 生成pathMeasure對(duì)象
pathMeasure.setPath(path, true);
2.截取的開始值和結(jié)束值
stop = mAnimatorValue * mLength;
start = (float) (stop - ((0.5 - Math.abs(mAnimatorValue - 0.5)) * mLength));
mAnimatorValue 的取值為(0,1) ,當(dāng)mAnimatorValue為0或1時(shí),兩個(gè)值相等。
3.截取路徑后繪制路徑
pathMeasure.getSegment(start, stop, dst, true);
canvas.drawPath(dst, paint);
完整代碼地址:
有用記得點(diǎn)顆小星星
加載動(dòng)畫化(二)
效果圖

思路
主要利用getPosTan 獲取測(cè)量點(diǎn)的坐標(biāo)和正余弦值,控制箭頭的方法和位置
1.先勾勒出一個(gè)順時(shí)針的空心圓,然后生成pathMeasure對(duì)象
// 勾勒空心圓
path.addCircle(width / 2, height / 2, radius, Path.Direction.CW);
// 生成pathMeasure對(duì)象
pathMeasure.setPath(path, true);
2.獲取繪制點(diǎn)的測(cè)量坐標(biāo)和正余弦值,根據(jù)tan[0],tan[1]來計(jì)算箭頭旋轉(zhuǎn)的角度。
measure.getPosTan(measure.getLength() * mAnimatorValue, pos, tan);
float angle = (float) (Math.atan2(tan[1], tan[0]) * 180 / Math.PI);
mAnimatorValue 的取值為(0,1) ,當(dāng)mAnimatorValue為0或1時(shí),兩個(gè)值相等。
angle計(jì)算方式自行查找,數(shù)學(xué)知識(shí)。
3.用Matrix對(duì)bitmap進(jìn)行旋轉(zhuǎn)和平移
mMatrix.postRotate(angle,mBitmap.getWidth()/2,mBitmap.getHeight()/2);
mMatrix.postTranslate(pos[0] - mBitmap.getWidth() / 2,pos[1] - mBitmap.getHeight() / 2);
4.繪制bitmap
canvas.drawBitmap(mBitmap,mMatrix,mPaint);
完整代碼地址:
有用記得點(diǎn)顆小星星
往期文章地址
Android動(dòng)畫 - 仿花束直播加載動(dòng)畫
Android動(dòng)畫 - 仿58同城加載動(dòng)畫
Android動(dòng)畫 - 仿抖音加載動(dòng)畫