自定义View之Path测量工具PathMeasure的详解

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()

setPath(Path path, boolean forceClosed)

此方法是关联一个预先创建好的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.在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);

结果:
这里写图片描述

自定义View之Path测量工具PathMeasure的详解_第1张图片

现在要截取上图中的下半部分,总长度是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);

结果:
自定义View之Path测量工具PathMeasure的详解_第2张图片

上面代码中的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);

结果:
自定义View之Path测量工具PathMeasure的详解_第3张图片

有意思的是,将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);

结果:
自定义View之Path测量工具PathMeasure的详解_第4张图片

再把上面代码中的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);

结果:
自定义View之Path测量工具PathMeasure的详解_第5张图片

和上上结果对比可得出: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旋转。

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 可以得到正切角的弧度值。

nextContour()

如果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);

输出的结果:
这里写图片描述

扫码关注个人公众号「技术人的日常」:

自定义View之Path测量工具PathMeasure的详解_第6张图片

你可能感兴趣的:(Android)