Android 绘制动画(波浪动画/轨迹动画/PathMeasure)

本文由 Luzhuo 编写,转发请保留该信息.
原文: https://blog.csdn.net/rozol/article/details/79730582

绘制动画, 由Android的绘画功能 + 属性动画 组成的一种动画

主要方法

* valueAnimator.addUpdateListener(AnimatorUpdateListener) // 监听动画数值更新
*
估值器
ValueAnimator.ofObject(new TypeEvaluator<PointF>() { @Override public PointF
evaluate(float fraction, PointF startValue, PointF endValue) { //
fraction(时间因子[0,1]), startValue(开始值), endValue(结束值) return null; // 返回计算结果值 }
});
波浪动画

*
原理: 通过动画计算的数值, 不断将波浪右移.


*
代码
protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPath.reset();
mPath.moveTo(-waveLength + offset, centerY);// 绘制2个贝塞尔曲线 for (int i = 0; i <
waveCount; i++){ mPath.quadTo( - waveLength *3 / 4 + i * waveLength + offset,
centerY +60, - waveLength / 2 + i * waveLength + offset, centerY);
mPath.quadTo( - waveLength /4 + i * waveLength + offset, centerY - 60, i *
waveLength + offset, centerY); }// 封闭波浪 mPath.lineTo(screenWidth,
screenHeight); mPath.lineTo(0, screenHeight); mPath.close();
canvas.drawPath(mPath, mPaintBezier); }public void onClick(View v) { // 设置属性动画
valueAnimator = valueAnimator.ofInt(0, waveLength); valueAnimator.setDuration(
1000); valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override public void onAnimationUpdate(ValueAnimator animation) { offset = (int
) animation.getAnimatedValue(); invalidate(); } }); valueAnimator.start(); }
* 效果

轨迹动画

*
原理: 使用估值器计算出动画执行时间点的坐标结果, 然后将黑色小球按该坐标结果绘制

*
代码
protected void onDraw(Canvas canvas) { super.onDraw(canvas);
canvas.drawCircle(movePointX, movePointY,20, paintCircle); } public void onClick
(View v) {// 运动轨迹 BezierEvaluator evaluator = new BezierEvaluator(new
PointF(flagPointX, flagPointY)); ValueAnimator animator =
ValueAnimator.ofObject(evaluator,new PointF(startPointX, startPointY), new
PointF(endPointX, endPointY)); animator.setDuration(600);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override public void onAnimationUpdate(ValueAnimator animation) { PointF
pointF = (PointF) animation.getAnimatedValue(); movePointX = (int) pointF.x;
movePointY = (int) pointF.y; invalidate(); } }); animator.setInterpolator(new
AccelerateDecelerateInterpolator()); animator.start(); }public class
BezierEvaluator implements TypeEvaluator<PointF> { private PointF flagPoint;
public BezierEvaluator(PointF flagPoint){ this.flagPoint = flagPoint; }
@Override public PointF evaluate(float fraction, PointF startValue, PointF
endValue) {// fraction(时间因子[0,1]), startValue(开始值), endValue(结束值) return
BezierUtil.getCalculateBezierPointForQuadratic(fraction, startValue, flagPoint,
endValue); } }/** * 获取二阶贝塞尔曲线点的坐标 * B(t) = (1 - t)^2 * P0 + 2t * (1 - t) * P1 +
t^2 * P2, t ∈ [0,1] * * @param t 曲线长度比例 * @param p0 起始点 * @param p1 控制点 * @param
p2 终止点 * @return t对应的点 */ public static PointF
getCalculateBezierPointForQuadratic(float t, PointF p0, PointF p1, PointF p2) {
PointF point =new PointF(); float temp = 1 - t; point.x = temp * temp * p0.x + 2
* t * temp * p1.x + t * t * p2.x; point.y = temp * temp * p0.y +2 * t * temp *
p1.y + t * t * p2.y;return point; }
* 效果

PathMeasure (Path测量类)

基本使用
public void onclick(View view){ Bitmap copybitmap = Bitmap.createBitmap(1000,
1000, Bitmap.Config.ARGB_8888); // 白纸 Canvas canvas = new Canvas(copybitmap);
// 画布 Paint paint = new Paint(); // 画笔 paint.setStyle(Paint.Style.STROKE); Path
path =new Path(); Path dst = new Path(); path.addCircle(300, 300, 200,
Path.Direction.CW); PathMeasure pm;// 测量Path的类 pm= new PathMeasure(); // pm =
new PathMeasure(path, true); pm.setPath(path, true); // 设置path float length =
pm.getLength();// 获取Path长度 // 截取片段 (参数:起始截取位置, 结束截取位置, 截取的path添加到dst, 是否从起点开始截)
返回:true存入dst中 boolean segment = pm.getSegment(0 + 100, length - 100, dst, true);
// 选择下个path boolean next = pm.nextContour(); // 获取指定位置的 坐标 和 该坐标的正切值 (参数:位置,
坐标, 正切值) 返回:true将数据存入 pos 和 tan 中, float[] pos = new float[2], tan = new float[2
];boolean postan = pm.getPosTan(300, pos, tan); // 获取指定位置的 坐标 和 该坐标的矩阵 (参数:位置,
矩阵, 将什么存入矩阵[POSITION_MATRIX_FLAG(坐标) / TANGENT_MATRIX_FLAG(正切)]) 返回:true将数据存入
matrix 中 Matrix matrix = new Matrix(); boolean m = pm.getMatrix(300, matrix,
PathMeasure.TANGENT_MATRIX_FLAG);// 如果清除dst里的内容, 修改后加上该代码, 用以解决 Android 4.4-
开启硬件加速的bug // dst.reset(); // dst.lineTo(0, 0); //
记算路径上某点的切线的角度(Math.atan2(纵坐标, 横坐标)) float degree = (float) (Math.atan2(tan[1],
tan[0]) * 180 / Math.PI); canvas.drawPath(dst, paint);
iv.setImageBitmap(copybitmap); }
案例: 缓冲圆圈

*
实现
public PathView(Context context, AttributeSet attrs) { super(context, attrs);
paint =new Paint(Paint.ANTI_ALIAS_FLAG); paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5); path = new Path(); dst = new Path(); // 画一个圆,
关联pathMeasure path.addCircle(400, 400, 100, Path.Direction.CW); pathMeasure =
new PathMeasure(); // 测量Path的类 pathMeasure.setPath(path, true); length =
pathMeasure.getLength();// 路径长度 // 创建属性动画 ValueAnimator animator =
ValueAnimator.ofFloat(0, 1); animator.setDuration(1000);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatCount(ValueAnimator.INFINITE); animator.addUpdateListener(new
ValueAnimator.AnimatorUpdateListener() {@Override public void onAnimationUpdate
(ValueAnimator valueAnimator) { animValue = (float)
valueAnimator.getAnimatedValue(); invalidate(); } }); animator.start(); }
protected void onDraw(Canvas canvas) { super.onDraw(canvas); dst.reset();
dst.lineTo(0, 0); // 解决硬件加速的bug float stop = length * animValue; float start = (
float) (stop - ((0.5 - Math.abs(animValue - 0.5)) * length)); // 从起点开始截取,
路径将越来越长 pathMeasure.getSegment(start, stop, dst, true); // 截取整个path的任何片段(开始长度 /
结束长度 / 保存截取的路径 / 是否从起点开始截取) canvas.drawPath(dst, paint); }
*
上述主要使用pathMeasure.getSegment(start, stop, dst, true);对path进行截取, 然后进行重绘;
除此之外还可以使用Path虚线类, 改变起始偏移量实现类似截取的效果, 由于是通过起始偏移量实现的, 所以只能从头部开始.
public void onAnimationUpdate(ValueAnimator valueAnimator) { animValue = (float
) valueAnimator.getAnimatedValue();// 初始化路径风格(float intervals[]:实现的长度, float
phase:起始偏移量) effect = new DashPathEffect(new float[]{length, length}, length *
animValue); paint.setPathEffect(effect); invalidate(); }
* 效果

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信