利用PathMeasure来实现自定义的动画
源码地址
https://github.com/SHPDZY/AndroidUiDemo
效果展示
代码实现
主要使用PathMeasure绘制View动画的执行路径,成功/失败的动画使用贝塞尔实现。
设置view路径
//边框路径
val path = Path()
path.moveTo(lineStartX, lineTopY)
path.lineTo(lineStopX, lineTopY)
arcRectF.left = mW - mH + padding
arcRectF.top = padding
arcRectF.right = mW - padding
arcRectF.bottom = mH - padding
path.arcTo(arcRectF, arcRtStartAng, arcSweepAng)
pathMeasureStrokeTop = PathMeasure(path, false)
pathMeasureStrokeLen = pathMeasureStrokeTop.length
path.reset()
path.moveTo(lineStopX, lineBtmY)
path.lineTo(lineStartX, lineBtmY)
arcRectF.left = padding
arcRectF.top = padding
arcRectF.right = mH + padding
arcRectF.bottom = mH - padding
path.arcTo(arcRectF, arcLtStartAng, arcSweepAng)
pathMeasureStrokeBtm = PathMeasure(path, false)
//对钩的路径
path.reset()
path.moveTo(tickLtStartX, tickLtStartY)
path.quadTo(tickLtControlX, tickLtControlY, tickLtEndX, tickLtEndY)
path.quadTo(tickCenX, tickCenY, tickCenEndX, tickCenEndY)
path.quadTo(tickRtControlX, tickRtControlY, tickRtEdX, tickRtEdY)
mPathMeasureTick = PathMeasure(path, false)
pathMeasureTickLen = mPathMeasureTick.length
// X的路径
path.reset()
path.moveTo(xLfStartX, xLfStartY)
path.lineTo(xLfEdX, xLfEdY)
pathMeasureXL = PathMeasure(path, false)
pathMeasureXLen = pathMeasureXL.length
path.reset()
path.moveTo(xRfStartX, xRfStartY)
path.lineTo(xRfEdX, xRfEdY)
pathMeasureXR = PathMeasure(path, false)
根据路径和动画执行进度绘制
/**
* 绘制边框
*/
private fun drawStroke(canvas: Canvas) {
getStrokePaint()
pathMeasurePath.reset()
pathMeasureStrokeLenRatio = pathMeasureStrokeLen * strokeRatio
if (strokeMode == MODE_STROKE_AROUND) {
pathMeasureStrokeLenRatio *= 2f
}
pathMeasureStrokeTop.getSegment(pathMeasureStrokeLenRatio, pathMeasureStrokeLen, pathMeasurePath, true)
canvas.drawPath(pathMeasurePath, paint)
if (strokeMode == MODE_STROKE_AROUND) {
pathMeasureStrokeLenRatio = pathMeasureStrokeLen * max((strokeRatio - 0.5f), 0f) * 2f
}
pathMeasurePath.reset()
pathMeasureStrokeBtm.getSegment(pathMeasureStrokeLenRatio, pathMeasureStrokeLen, pathMeasurePath, true)
canvas.drawPath(pathMeasurePath, paint)
}
/**
* 绘制成功或失败的图案
*/
private fun drawComplete(canvas: Canvas) {
getTickAndXPaint()
pathMeasurePath.reset()
if (isSuccess) {
mPathMeasureTick.getSegment(0f, pathMeasureTickLen * tickAndXRatio, pathMeasurePath, true)
canvas.drawPath(pathMeasurePath, paint)
return
}
pathMeasureXLenRatio = pathMeasureXLen * tickAndXRatio
pathMeasureXL.getSegment(0f, pathMeasureXLenRatio, pathMeasurePath, true)
canvas.drawPath(pathMeasurePath, paint)
pathMeasurePath.reset()
pathMeasureXLenRatio = pathMeasureXLen * max(tickAndXRatio - 0.5f, 0f) * 2f
pathMeasureXR.getSegment(0f, pathMeasureXLenRatio, pathMeasurePath, true)
canvas.drawPath(pathMeasurePath, paint)
}
/**
* 绘制背景
*/
private fun drawBackground(canvas: Canvas) {
getBacPaint()
if (isSuccess) {
paint.color = getColor(bacColorStart, bacColorSuccess, (strokeRatio + tickAndXRatio) / 2f)
} else {
paint.color = getColor(bacColorStart, bacColorError, (strokeRatio + tickAndXRatio) / 2f)
}
canvas.drawRoundRect(0f, 0f, mW, mH, 200f, 200f, paint)
}
/**
* 绘制文字
*/
private fun drawText(canvas: Canvas) {
getTextPaint()
val fontMetrics: Paint.FontMetrics = paint.fontMetrics
textY = mH / 2f - (fontMetrics.ascent + fontMetrics.descent) / 2f
paint.color = getColor(textColor, 1f - strokeRatio)
canvas.drawText(if (strokeRatio == 0f) text else textLoading, textX, textY, paint)
}