- PathMeasure是什麼? 顧名思義,PathMeasure是用來對Path進行測量的,一般PathMeasure是和Path配合使用的,通過PathMeasure,我們可以知道Path路徑上某個點的座標、Path的長度等等,如果對Path不瞭解,可以先看下這篇文章:Android Canvas之Path操作
PathMeasure有兩個建構函式:
//構建一個空的PathMeasure
PathMeasure()
//構建一個PathMeasure並關聯一個指定的建立好的Path
PathMeasure(Path path, boolean forceClosed)
複製程式碼
說明:無參建構函式PathMeasure()在使用前必須呼叫 setPath(Path path, boolean forceClosed)來關聯一個Path,等同於有引數的建構函式PathMeasure(Path path, boolean forceClosed),如果關聯之後的Path有改變,需要呼叫 setPath(Path path, boolean forceClosed)重新關聯。
PathMeasure常用方法:
setPath(Path path, boolean forceClosed)
isClosed()
getLength()
getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)
getMatrix(float distance, Matrix matrix, int flags)
getPosTan(float distance, float pos[], float tan[])
nextContour()
複製程式碼
-
setPath(Path path, boolean forceClosed) PathMeasure關聯一個建立好的Path,如果第二個引數forceClosed為true,並且關聯的Path未閉合時,測量的Path長度可能會比Path的實際長度長一點,因為測量的是Path閉合的長度,但關聯的Path不會有任何變化。
-
isClosed() 判斷關聯的Path是否是閉合狀態,若forceClosed為true,則此方法一定返回true。
-
getLength() 返回已關聯Path的總長度,若setPath()時設定的forceClosed為true,則返回值可能會比實際長度長。
-
getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo) 擷取Path的一段,如果擷取成功,返回true;反之返回false。
引數 | 備註 |
---|---|
startD | 起點在Path的位置,取值範圍0<=startD<stopD<=getLength() |
stopD | 終點在Path的位置,取值範圍0<=startD<stopD<=getLength() |
dst | 將擷取的path的片段新增到dst中 |
startWithMoveTo | 起點是否使用MoveTo,如果為true,則擷取的path的第一個點不會變化,擷取的path也不會改變,如果為false,則擷取的path可能會發生形變。 |
注: 1、如果擷取的Path的長度為0,則返回false,大於0則返回true; 2、startD、stopD必須為合法值(0,getLength()),如果startD>=stopD,則返回false; 3、在4.4或之前的版本,在開啟硬體加速時,繪製可能會不顯示,請關閉硬體加速或者給 dst 新增一個簡單操作,如: dst.rLineTo(0, 0)
示例:
//初始化Paint
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(10f);
//初始化Path並順時針繪製一個矩形
Path sourcePath = new Path();
sourcePath .addRect(300, 300, 600, 600, Path.Direction.CW);
PathMeasure measure = new PathMeasure();
measure.setPath(sourcePath , false);
//列印Path的長度
Log.e("TTT", "measure.getLength() is " + measure.getLength());
canvas.drawPath(sourcePath , paint);
複製程式碼
結果:
org.ninetripods.mq.pathmeasure E/TTT: measure.getLength() is 1200.0
複製程式碼
![C912D0F205BEF3C3BB3022AD90EA91D5_副本.jpg](https://i.iter01.com/images/a1ba9ca779a1a5be50c4cff2247d2b16ed4a80fcef855dbcf3ae06101d80b6d5.jpg)
現在想擷取下圖中Path的黑色部分並放到一個新的Path中:
![getSeg.jpg](https://i.iter01.com/images/a66fab280980e878077a8613187d547648c83de50e434acd0e5fc773d5da1150.jpg)
//初始化Paint
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(10f);
//初始化Path並順時針繪製一個矩形
Path sourcePath = new Path();
sourcePath.addRect(300, 300, 600, 600, Path.Direction.CW);
PathMeasure measure = new PathMeasure();
measure.setPath(sourcePath, false);
//初始化一個空的Path
Path dstPath = new Path();
//擷取sourcePath的一部分新增到dstPath中
measure.getSegment(450, 900, dstPath, true);
canvas.drawPath(dstPath, paint);
複製程式碼
效果圖:
![seg.png](https://i.iter01.com/images/8155efbd2eea3146d70d28ff5752ef32571aaeeb5de66c439ed70fa1b5cf5c43.png)
上面程式碼中dstPath初始化時是沒有內容的,如果dstPath本身有內容呢
//初始化Paint
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(10f);
//初始化Path並順時針繪製一個矩形
Path sourcePath = new Path();
sourcePath.addRect(300, 300, 600, 600, Path.Direction.CW);
PathMeasure measure = new PathMeasure();
measure.setPath(sourcePath, false);
//初始化一個空的Path
Path dstPath = new Path();
//給dstPath新增一條直線
dstPath.lineTo(800, 200);
//擷取sourcePath的一部分新增到dstPath中
measure.getSegment(450, 900, dstPath, true);
canvas.drawPath(dstPath, paint);
複製程式碼
效果圖:
![seg1.png](https://i.iter01.com/images/b71ddce4e7c7c72a44a586ffa0960bb321e1ebff348583311f5285c325e93abf.png)
上面呼叫getSegment()時引數startWithMoveTo設成false會成什麼樣呢,上面程式碼中改變一行程式碼
measure.getSegment(450, 900, dstPath, false);
複製程式碼
效果圖:
![seg2.png](https://i.iter01.com/images/c274c5c9f1e6435d302acfa803a7b51160be164d40cc04ebeaef5a19de80d815.png)
可見,startWithMoveTo為true時,被擷取的path片段會保持原狀;startWithMoveTo為false時,會將擷取的path片段的起始點移動到dstPath的終點以保持dstPath的連續性。
- getMatrix(float distance, Matrix matrix, int flags) 距離Path起始點的一段長度distance,通過計算得到該位置座標並返回一個處理好的矩陣,該矩陣以左上角為旋轉點,如果Path不存在或者長度為0,該方法返回false。
引數 | 備註 |
---|---|
distance | 距離Path起始點的距離,取值範圍0 <= distance <= getLength() |
matrix | 根據 flags封裝matrix,flags不同,存入matrix的就不同 |
flags | PathMeasure.POSITION_MATRIX_FLAG:位置資訊 ,PathMeasure.TANGENT_MATRIX_FLAG:切邊資訊,方位角資訊,使得圖片按path旋轉。 |
//TODO 關於Matrix下一篇詳細介紹。
- getPosTan(float distance, float pos[], float tan[]) 距離Path起始點的長度distance,通過計算返回該長度在Path上的座標及該座標的正切值並分別賦值給pos[]、tan[]。
引數 | 備註 |
---|---|
distance | 距離Path起始點的距離,取值範圍0 <= distance <= getLength() |
pos[] | distance在path上的座標,即pos[]存的該點的座標x,y值 |
tan[] | distance在path上對應座標點在path上的方向,tan[0]是鄰邊邊長,tan[1]是對邊邊長。通過Math.atan2(tan[1], tan[0])*180.0/Math.PI 可以得到正切角的弧度值。 |
示例,先看下效果圖:
![path.gif](https://i.iter01.com/images/5723001b000bf6f6266faf25bf506c0d3913cd240aae2758aaeffe349d52e89b.gif)
來看下主要程式碼,完整程式碼已上傳到github:PathMeasure
@Override
protected void onDraw(Canvas canvas) {
//繪製矩形
mPath.reset();
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.STROKE);
mPath.addRect(mRectF, Path.Direction.CW);
pathMeasure.setPath(mPath, false);
canvas.drawPath(mPath, mPaint);
//繪製圓
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(pos[0], pos[1], 12, mPaint);
}
public void startMove() {
ValueAnimator animator = ValueAnimator.ofFloat(0, pathMeasure.getLength());
animator.setDuration(3 * 1000);
animator.setInterpolator(new DecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float distance = (float) animation.getAnimatedValue();
pathMeasure.getPosTan(distance, pos, null);
postInvalidate();
}
});
animator.start();
}
複製程式碼
原理是在呼叫startMove()後,通過ValueAnimator.ofFloat(0, pathMeasure.getLength())及animation.getAnimatedValue()來不斷獲得圓距離path起點的距離,通過getPosTan(distance, pos, null)不斷獲得圓在path上的座標並重新整理介面。
- nextContour() 如果Path由多條曲線組成且彼此不連線,則getLength getSegment getMatrix getPosTan方法都是針對當前正在操作的,比如:如果Path由多條曲線組成且彼此不連線,getLength()返回的只是當前操作的曲線的長度,並不是所有曲線的長度,那麼怎麼跳轉到下一條曲線上呢?答案就是用nextContour(),跳轉成功返回true;否則返回false。
Path path = new Path();
PathMeasure measure = new PathMeasure();
//繪製一條從(100,100)到(900,100)的直線,長度為800
path.moveTo(100, 100);
path.lineTo(900, 100);
//繪製一條從(100,200)到(500,100)的直線,長度為400
path.moveTo(100, 200);
path.lineTo(500, 200);
measure.setPath(path, false);
//輸出第一條路徑的長度
Log.e("TTT", "measure.getLength() is " + measure.getLength());
measure.nextContour();
//輸出第二條路徑的長度
Log.e("TTT", "measure.getLength() is " + measure.getLength());
canvas.drawPath(path, paint);
複製程式碼
Log日誌:
02-09 18:02:50.483 9010-9010/? E/TTT: measure.getLength() is 800.0
02-09 18:02:50.483 9010-9010/? E/TTT: measure.getLength() is 400.0
複製程式碼