PathMeasure 动画路径追踪详解
PathMeasure 是 Android 中一个强大的工具类,用于测量 Path 路径的长度、获取路径上某一段的片段、以及判断路径是否闭合等。它可以帮助开发者更精确地控制和绘制复杂的路径动画效果,是实现流畅动画的重要工具。
1、什么是 PathMeasure
PathMeasure 的核心功能包括:
• 测量路径总长度
• 截取路径片段
• 获取路径上某点的坐标和切线方向
• 判断路径是否闭合
• 处理多路径(通过 nextContour)
2、基本使用方法
使用 PathMeasure 非常简单,只需要创建一个 PathMeasure 对象,然后通过 setPath() 方法将需要测量的 Path 传入即可。PathMeasure 有两个构造函数:
• PathMeasure():无参构造函数
• PathMeasure(Path path, boolean forceClosed):直接传入 Path 和是否强制闭合路径
val path = Path()
path.moveTo(100f, 100f)
path.lineTo(500f, 100f)
path.lineTo(500f, 500f)
val pathMeasure = PathMeasure(path, false)
// 或者使用无参构造函数
val pathMeasure2 = PathMeasure()
pathMeasure2.setPath(path, false)3、获取路径长度
通过 getLength() 方法可以获取 Path 的总长度,这对于实现按比例绘制路径动画非常有用。
val path = Path()
path.addCircle(300f, 300f, 200f, Path.Direction.CW)
val pathMeasure = PathMeasure(path, false)
val length = pathMeasure.length
Log.d("PathMeasure", "路径总长度: $length")4、截取路径片段
getSegment() 方法是 PathMeasure 的核心功能之一,它可以从原路径中截取一段片段。
4.1、方法签名
boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)4.2、参数说明
• startD:起始距离(相对于路径起点)
• stopD:结束距离
• dst:目标 Path,用于存储截取的片段
• startWithMoveTo:是否使用 moveTo 开始片段
4.3、代码示例
val path = Path()
path.moveTo(100f, 300f)
path.quadTo(300f, 100f, 500f, 300f)
val pathMeasure = PathMeasure(path, false)
val dstPath = Path()
pathMeasure.getSegment(0f, pathMeasure.length * 0.5f, dstPath, true)5、路径追踪动画实现
利用 PathMeasure 可以实现优雅的路径追踪动画。基本思路是通过 ValueAnimator 不断更新进度,每次根据进度截取路径片段并绘制。
class PathAnimationView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : View(context, attrs) {
private val path = Path()
private val dstPath = Path()
private val pathMeasure = PathMeasure()
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.STROKE
strokeWidth = 10f
color = Color.BLUE
}
private var animator: ValueAnimator? = null
private var progress = 0f
init {
path.addCircle(300f, 300f, 200f, Path.Direction.CW)
pathMeasure.setPath(path, false)
startAnimation()
}
private fun startAnimation() {
animator = ValueAnimator.ofFloat(0f, 1f).apply {
duration = 2000
repeatCount = ValueAnimator.INFINITE
repeatMode = ValueAnimator.RESTART
addUpdateListener { animation ->
progress = animation.animatedValue as Float
invalidate()
}
start()
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
dstPath.reset()
val stop = pathMeasure.length * progress
pathMeasure.getSegment(0f, stop, dstPath, true)
canvas.drawPath(dstPath, paint)
}
}6、进阶技巧与注意事项
6.1、处理多路径
PathMeasure 只能测量连续的单一路径,对于复杂的多路径需要使用 nextContour() 方法依次处理:
do {
val length = pathMeasure.length
// 处理当前路径
} while (pathMeasure.nextContour())6.2、图标沿路径移动
结合 Matrix 可以实现图标沿路径移动的效果:
val matrix = Matrix()
val pos = FloatArray(2)
val tan = FloatArray(2)
pathMeasure.getPosTan(pathMeasure.length * progress, pos, tan)
matrix.reset()
val degrees = (Math.atan2(tan[1].toDouble(), tan[0].toDouble()) * 180 / Math.PI).toFloat()
matrix.postRotate(degrees, bitmap.width / 2f, bitmap.height / 2f)
matrix.postTranslate(pos[0] - bitmap.width / 2f, pos[1] - bitmap.height / 2f)
canvas.drawBitmap(bitmap, matrix, paint)6.3、注意事项
• getSegment() 方法的目标 Path 需要先调用 reset()
• 某些 Android 版本上可能需要开启硬件加速
• 对于复杂路径,建议先调用 forceClosed 参数为 true 的构造函数
• 注意内存泄漏,及时释放动画资源
7、总结
PathMeasure 是 Android 动画开发中的重要工具,掌握它可以实现各种复杂的路径动画效果。无论是路径绘制动画还是图标沿路径移动,PathMeasure 都能提供精确的控制能力。在实际开发中,结合 ValueAnimator 和 Matrix,可以创造出更加丰富的动画效果。

