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


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


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

主要方法

  1. valueAnimator.addUpdateListener(AnimatorUpdateListener) // 监听动画数值更新
  2. 估值器

    ValueAnimator.ofObject(new TypeEvaluator() {    @Override    public PointF evaluate(float fraction, PointF startValue, PointF endValue) {        // fraction(时间因子[0,1]), startValue(开始值), endValue(结束值)        return null; // 返回计算结果值    }});

波浪动画

  • 原理: 通过动画计算的数值, 不断将波浪右移.
    Android 绘制动画(波浪动画/轨迹动画/PathMeasure)_第1张图片

  • 代码

    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();}
  • 效果
    Android 绘制动画(波浪动画/轨迹动画/PathMeasure)_第2张图片

轨迹动画

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

  • 代码

    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;}
  • 效果
    Android 绘制动画(波浪动画/轨迹动画/PathMeasure)_第3张图片

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(); }
  • 效果
    Android 绘制动画(波浪动画/轨迹动画/PathMeasure)_第4张图片

更多相关文章

  1. Animator记录一次属性动画实现的逐渐出现和逐渐消失的动画
  2. Android 动画的重复播放
  3. Android实现TextView字符串波浪式跳动
  4. Android动画设置interpolator插值器
  5. Android中帧动画
  6. Android 收缩展开动画
  7. Android旋转动画不停顿
  8. Android xml资源文件animal动画解析
  9. Android中启动动画源码讲解

随机推荐

  1. HTML5用户身份认证源代码:注册、登录、会
  2. Eclipse中以html格式高亮显示velocity文
  3. ExtJS的使用方法汇总(4)——拖放以及弹出
  4. HTML5晃动DeviceMotionEvent事件
  5. CSS3列 - 添加第二个元素后的额外宽度
  6. html里的px是什么
  7. 在京东上6-24日购买6台笔记本7天无理由
  8. HTML语言基础之——掌握HTML的写法(2)
  9. html5 的 canvas 想写个小项目 画板
  10. 翻译:HTML5与HTML4的区别