Android 绘制动画(波浪动画/轨迹动画/PathMeasure)
16lz
2021-01-23
Android 绘制动画(波浪动画/轨迹动画/PathMeasure)
本文由 Luzhuo 编写,转发请保留该信息.
原文: https://blog.csdn.net/rozol/article/details/79730582
绘制动画, 由Android的绘画功能 + 属性动画 组成的一种动画
主要方法
valueAnimator.addUpdateListener(AnimatorUpdateListener)
// 监听动画数值更新估值器
ValueAnimator.ofObject(new TypeEvaluator
() { @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(); }
- 效果
更多相关文章
- Animator记录一次属性动画实现的逐渐出现和逐渐消失的动画
- Android 动画的重复播放
- Android实现TextView字符串波浪式跳动
- Android动画设置interpolator插值器
- Android中帧动画
- Android 收缩展开动画
- Android旋转动画不停顿
- Android xml资源文件animal动画解析
- Android中启动动画源码讲解