Android Path测量工具:PathMeasure

简介: Android Path测量工具:PathMeasure
  • 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

现在想截取下图中Path的黑色部分并放到一个新的Path中:
getSeg.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

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

上面调用getSegment()时参数startWithMoveTo设成false会成什么样呢,上面代码中改变一行代码

measure.getSegment(450, 900, dstPath, false);

效果图:
seg2.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

来看下主要代码,完整代码已上传到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
相关文章
|
6月前
|
网络协议 Android开发 Python
Android 抓包工具r0capture使用
Android 抓包工具r0capture使用
535 1
|
6月前
|
Android开发 芯片 开发者
Android MediaTek bootloader 的序列号长度 & 移除非字母和数字限制 SN-Writer工具支持写入
Android MediaTek bootloader 的序列号长度 & 移除非字母和数字限制 SN-Writer工具支持写入
94 0
|
3月前
|
编解码 Android开发
【Android Studio】使用UI工具绘制,ConstraintLayout 限制性布局,快速上手
本文介绍了Android Studio中使用ConstraintLayout布局的方法,通过创建布局文件、设置控件约束等步骤,快速上手UI设计,并提供了一个TV Launcher界面布局的绘制示例。
56 1
|
4月前
|
Java Android开发 iOS开发
探索安卓与iOS开发的差异性:平台、工具和用户体验的对比分析
【7月更文挑战第30天】在移动应用开发的广阔天地中,安卓和iOS两大平台各自占据着不可忽视的地位。本文将深入探讨这两大平台在开发环境、工具选择以及最终用户体验上的根本差异,并分析这些差异如何影响开发者的策略和用户的偏好。通过比较安卓的开放性与iOS的封闭性,我们将揭示不同平台下的开发哲学及其对生态系统的影响。
44 4
|
4月前
|
开发工具 Android开发 数据安全/隐私保护
探索iOS与安卓应用开发的异同:技术、工具和市场趋势
在移动操作系统的广阔舞台上,iOS和安卓两大主角各自演绎着怎样的精彩?本文将深入剖析这两大平台在应用开发过程中的技术差异、开发工具的选择以及面对的市场环境。通过数据支撑和案例分析,我们将一窥这两个系统如何影响开发者的决策,并探讨它们未来的发展方向。
|
3月前
|
API 调度 Android开发
Android经典实战之处理后台任务的2个工具
本文介绍Android后台任务管理,涵盖WorkManager与JobScheduler的使用方法及区别。WorkManager属Jetpack库,确保任务可靠执行,支持延迟与条件依赖。JobScheduler则针对特定条件下的任务调度,如网络类型。两者各有优势,WorkManager适用于多数场景。
47 0
|
4月前
|
监控 开发工具 Android开发
探索安卓与iOS开发的差异:平台特性、工具和市场趋势
在移动应用开发的广阔舞台上,安卓与iOS两大操作系统扮演着主角。它们各自拥有独特的平台特性、开发工具和市场定位,这些差异深刻影响着开发者的决策和产品的最终形态。本文将深入分析这两大平台的关键技术差异,探讨各自的开发环境和工具集,以及它们在市场上的表现和未来的趋势,为开发者提供一个全面的视角,帮助他们在这两个平台上做出更明智的开发选择。
|
5月前
|
监控 Android开发 iOS开发
探索Android与iOS开发的差异:平台、工具和用户体验的比较
【6月更文挑战第25天】在移动应用开发的广阔天地中,Android和iOS两大平台各领风骚,它们在开发环境、工具选择及用户体验设计上展现出独特的风貌。本文将深入探讨这两个操作系统在技术实现、市场定位和用户交互方面的关键差异,旨在为开发者提供一个全景式的视图,帮助他们在面对项目决策时能够更加明智地选择适合自己项目需求的平台。
|
5月前
|
开发工具 Android开发 iOS开发
探索安卓与iOS开发的差异:从工具到用户体验
【6月更文挑战第20天】在移动应用开发的广阔天地中,安卓和iOS两大平台各自占据半壁江山。本文将深入探讨这两个操作系统在开发环境、编程语言、用户界面设计以及性能优化等方面的关键差异。我们将通过比较分析,揭示各自平台的独特优势和面临的挑战,为开发者提供决策参考,并为最终用户提供更深层次的用户体验洞察。
|
5月前
|
Android开发
Android 自定义View 测量控件宽高、自定义viewgroup测量
Android 自定义View 测量控件宽高、自定义viewgroup测量
88 0