PathMeasure是什么?
PathMeasure是用来对Path进行测量的工具,一般来说PathMeasure是和Path配合着使用的。通过PathMeasure,我们可以知道Path路径上某讴歌点的坐标、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常用的API:
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()
此方法是关联一个预先创建好的Path。第二个参数forceClosed如果为true,并且关联的Path未闭合时,测量的Path长度可能比Path实际长度长一点,因为测量的是Path闭合的长度。但是关联的Path不会有任何变化。
判断关联的Path是否是闭合状态。如果forceClosed为true,那么此方法一定返回true。
返回已关联的Path总长度。如果setPath()时设置的forceClosed为true,则返回的值可能会比实际长度的长。
截取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.在Android 4.4或之前的版本在开启硬件加速时,绘制可能会不显示,请关闭硬件加速或者给dst添加一个简单的操作,如:dst.rLineTo(0,0)
演示一下:
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE); //只描边
paint.setColor(Color.BLUE);
paint.setStrokeWidth(10f);
paint.setAntiAlias(true); //设置抗锯齿
paint.setDither(true); //设置防抖动
//初始化Path并顺时针绘制一个矩形
Path sourcePath = new Path();
sourcePath.addRect(300, 300, 800, 800, Path.Direction.CW);
PathMeasure measure = new PathMeasure();
measure.setPath(sourcePath, false);
Log.e("PathMeasure", "measure.getLength():" + measure.getLength());
canvas.drawPath(sourcePath, paint);
现在要截取上图中的下半部分,总长度是2000,中间的部分就是[750,1750]。演示一下:
//初始化Path并顺时针绘制一个矩形
Path sourcePath = new Path();
sourcePath.addRect(300, 300, 800, 800, Path.Direction.CW);
PathMeasure measure = new PathMeasure();
measure.setPath(sourcePath, false);
// Log.e("PathMeasure", "measure.getLength():" + measure.getLength());
// canvas.drawPath(sourcePath, paint);
//初始化一个空的Path
Path dstPath = new Path();
//截取sourcePath的一部分添加到dstPath中
measure.getSegment(750, 1750, dstPath, true);
dstPath.rLineTo(0, 0);//真机刚刚好是4.4版本的,加个简单的操作
canvas.drawPath(dstPath, paint);
上面代码中的dstPath初始化完之后,并没有内容的,试试有内容的情况:
//初始化Path并顺时针绘制一个矩形
Path sourcePath = new Path();
sourcePath.addRect(300, 300, 800, 800, Path.Direction.CW);
PathMeasure measure = new PathMeasure();
measure.setPath(sourcePath, false);
// Log.e("PathMeasure", "measure.getLength():" + measure.getLength());
// canvas.drawPath(sourcePath, paint);
//初始化一个空的Path
Path dstPath = new Path();
dstPath.rLineTo(200, 200);
dstPath.lineTo(800, 200);
//截取sourcePath的一部分添加到dstPath中
measure.getSegment(750, 1750, dstPath, true);
canvas.drawPath(dstPath, paint);
有意思的是,将dstPath的rLineTo()和lineTo()放在getSegment()之后,后面内容的起点就成了getSegment()的终点了。演示一下:
//初始化Path并顺时针绘制一个矩形
Path sourcePath = new Path();
sourcePath.addRect(300, 300, 800, 800, Path.Direction.CW);
PathMeasure measure = new PathMeasure();
measure.setPath(sourcePath, false);
// Log.e("PathMeasure", "measure.getLength():" + measure.getLength());
// canvas.drawPath(sourcePath, paint);
//初始化一个空的Path
Path dstPath = new Path();
//截取sourcePath的一部分添加到dstPath中
measure.getSegment(750, 1750, dstPath, true);
dstPath.rLineTo(200, 200);
dstPath.lineTo(800, 200);
canvas.drawPath(dstPath, paint);
再把上面代码中的getSegment()的startWithMoveTo参数改成false会变成什么样呢?演示一下:
//初始化Path并顺时针绘制一个矩形
Path sourcePath = new Path();
sourcePath.addRect(300, 300, 800, 800, Path.Direction.CW);
PathMeasure measure = new PathMeasure();
measure.setPath(sourcePath, false);
// Log.e("PathMeasure", "measure.getLength():" + measure.getLength());
// canvas.drawPath(sourcePath, paint);
//初始化一个空的Path
Path dstPath = new Path();
//为了易于理解,把这两个方法放回getSegment()前面
dstPath.rLineTo(200, 200);
dstPath.lineTo(800, 200);
//截取sourcePath的一部分添加到dstPath中
measure.getSegment(750, 1750, dstPath, false);
canvas.drawPath(dstPath, paint);
和上上结果对比可得出:startWithMoveTo参数为true时,被截取的path片段会保持原状;startWithMoveTo参数为false时,会将截取的path片段的起始点移动到dstPath的终点,以保持dstPath的连续性。
距离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旋转。 |
距离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有多条曲线组成,且彼此不连接,那么getLength()、getSegment()、getMatrix()和getPosTan()这些方法,都只是针对当前正在操作的。举个例子,Path由多条曲线组成,且彼此不连接,那么getLength()返回的只是当前操作曲线的长度,并不是所有曲线的长度。那么怎么获取下一条曲线的长度呢?这时就得用nextContour()跳转到下一条曲线了,跳转成功返回true,失败就返回false。演示一下:
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE); //只描边
paint.setColor(Color.BLUE);
paint.setStrokeWidth(10f);
paint.setAntiAlias(true); //设置抗锯齿
paint.setDither(true); //设置防抖动
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("PathMeasure", "measure.getLength():" + measure.getLength());
measure.nextContour(); //跳转到下一条曲线
//输出第二条曲线的长度
Log.e("PathMeasure", "next measure.getLength():" + measure.getLength());
canvas.drawPath(path, paint);
扫码关注个人公众号「技术人的日常」: