Android View之PathMeasure

(一)PathMeasure的概念

PathMeasure是一个用来测量Path的类。主要用来测量Path曲线的轨迹长度、截图片段、计算轨迹的坐标及角度,也可以通过不断截取重绘来模拟曲线动画的轨迹。

(二)PathMeasure的用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 构造。其中参数forceClosed的值不会影响path之前的绘制,但会影响Path的测量长度。
// 即forceClosed为true时,pathMeasure.getLength()的值始终是Path的闭合长度。
pathMeasure = PathMeasure(Path path, boolean forceClosed);


// 获取Path当前的一条曲线长度
pathMeasure.getLength();

// 跳转到下一条轮廓(路径)
// 注意:一条Path可能由很多条曲线(轮廓)组成
pathMeasure.nextContour();


// 关联Path是否闭合。若关联的path有调用close()或者path本身就是闭合的,则结果始终为true。
// 反之以PathMeasure构造时或者setPath方法传入参数forceClosed的值为准。
pathMeasure.isClosed();


// 【重要方法 - 可模拟图形(动画)绘制轨迹】
// 截取Path的一个片段。(取值范围:0 <= startD < stopD <= Path总长度)
// startD:截取的初始位置距离Path起点的路径(轨迹)长度(注意不是位移,而是轨迹长度)
// stopD:截取的结束位置距离Path起点的路径(轨迹)长度
// dstPath:将截取的Path将会添加到dstPath中。
// startWithMoveTo:true表示pathMeasure从path起始点截取,
// false 表示从截取出来的Path片段的起点移动到dstPath的最后一个点(dstPath可能由多条曲线组成)。
pathMeasure.getSegment(float startD, float stopD, Path dstPath, boolean startWithMoveTo);


// 【重要方法 - 可计算轨迹点的坐标及角度】
// 若相应的位置和正切获取成功,则存放至pos、tan中。
// distance:距离Path起点的轨迹长度(0 <= distance <= getLength)
// pos:当前轨迹点的坐标位置(x,y)
// tan:当前点在曲线上的方向(x,y),使用Math.atan2(tan[1], tan[0])可得到正切角的弧度值。
pathMeasure.getPosTan(distance, pos, tan);


// 获取当前位置的坐标以及趋势的矩阵。(相当于getPosTan的一次封装,效果是一样的)
// distance:距离Path起点的轨迹长度(0 <= distance <= getLength)
// matrix:根据flags的设置而存入不同的内容
// flags:只有2个取值 -- POSITION_MATRIX_FLAG(位置) TANGENT_MATRIX_FLAG(正切)
pathMeasure.getMatrix(float distance, Matrix matrix, PathMeasure.POSITION_MATRIX_FLAG | PathMeasure.TANGENT_MATRIX_FLAG);

(三)PathMeasure的实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/**
* 自定义View -- 带箭头模拟圆的轨迹
*
* @author [*昨日重现*] lhy_ycu@163.com
* @since version 1.0
*/
public class PathMeasureArrow extends View {
private Paint mPaint;
private Path mPath;

// View的宽高
private int mWidth;
private int mHeight;

// 圆的半径
private float mRadius;
// 增长因子
private float currFactor;
// 当前轨迹点的坐标位置(x,y)
private float pos[];
// 当前点的正切值(用于计算旋转角度)(x,y)
private float tan[];
// 箭头Icon
private Bitmap mBitmap;
// 矩阵(对图片进行一些操作)
private Matrix mMatrix;

public PathMeasureArrow(Context context) {
this(context, null);
}

public PathMeasureArrow(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}

private void init(Context context) {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPath = new Path();
mMatrix = new Matrix();

pos = new float[2];
tan = new float[2];
mRadius = DensityUtils.dip2px(context, 100);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 6;// 缩小6倍
mBitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_arrow, options);
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
this.mWidth = w;
this.mHeight = h;
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(mWidth / 2, mHeight / 2);

mPath.reset();
mPath.addCircle(0, 0, mRadius, Path.Direction.CW);

PathMeasure pathMeasure = new PathMeasure(mPath, true);

float distance = pathMeasure.getLength() * Math.min(currFactor, 1);
// 若相应的位置和正切获取成功,则存放至pos、tan中。
// distance:距离 Path 起点的长度(0 <= distance <= getLength)
// pos:当前轨迹点的坐标位置(x,y)
// tan:当前点在曲线上的方向,使用 Math.atan2(tan[1], tan[0]) 获取到正切角的弧度。
if (pathMeasure.getPosTan(distance, pos, tan)) {
float degrees = (float) (Math.atan2(tan[1], tan[0]) * 180 / Math.PI);// 获取曲线方向上的角度

mMatrix.reset();
mMatrix.postRotate(degrees, mBitmap.getWidth() / 2, mBitmap.getHeight() / 2);// 图标旋转的角度
mMatrix.postTranslate(pos[0] - mBitmap.getWidth() / 2, pos[1] - mBitmap.getHeight() / 2);// 图标中心移至轨迹点

mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5F);
canvas.drawPath(mPath, mPaint);
canvas.drawBitmap(mBitmap, mMatrix, mPaint);
}

if (currFactor < 1) {
currFactor += 0.002;
} else {
currFactor = 0;
}
invalidate();
}

}
Hawky wechat
欢迎订阅我的微信公众号
坚持原创技术分享,您的支持将鼓励我继续创作!

分享