Android 动画进阶 PathMeasure

1,329 阅读3分钟
原文链接: www.jianshu.com

PathMeasure

作用:测量并获取Path的信息,用于绘制Path路径实现动画效果

ValueAnimator在这里不会讲解,不过至关重要,是动画的发动机!

基本路径绘制

预览:


普通路径绘制.mp4_1491231595.gif
  1. 生成完整的目标路径
  2. 初始化PathMeasure对象
  3. PathMeasure.setPath(targetPath,true) 测量路径
  4. mPathMeasure.getLength(); 获取路径的总长度
  5. 初始化动态的路径 mDstPath
  6. mPathMeasure.getSegment(startD, stopD, mDstPath, true); : 根据传入的起始值终止值(相当于要截取路径的部分),将路径赋值给mDstPath
  7. 使用ValueAnimator产生一个增长值来控制 终止值 如:float stopD = mAnimatedValue * mLength;
  8. 最后将 mDstPath 绘制出来,就可以实现路径的逐步绘制效果了
//完整的圆的路径
        mCirclePath = new Path();
        //路径绘制每段截取出来的路径
        mDstPath = new Path();

        mCirclePath.addCircle(0, 0, 200, Path.Direction.CW);

        //路径测量类
        mPathMeasure = new PathMeasure();
        //测量路径
        mPathMeasure.setPath(mCirclePath, true);

        //获取被测量路径的总长度
        mLength = mPathMeasure.getLength();

        mValueAnimator = ValueAnimator.ofFloat(0, 1);
        mValueAnimator.setDuration(2000);
        mValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
        mValueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                //获取从0-1的变化值
                mAnimatedValue = (float) animation.getAnimatedValue();
                //不断刷新绘图,实现路径绘制
                invalidate();
            }
        });


@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //必须要有lineTo(0,0)才可以实现路径的完整绘制
        mDstPath.reset();
        mDstPath.lineTo(0, 0);
        float stopD = mAnimatedValue * mLength;
        float startD = 0;

        //获取当前进度的路径,同时赋值给传入的mDstPath
        mPathMeasure.getSegment(startD, stopD, mDstPath, true);

        canvas.save();
        canvas.translate(300, 300);
        canvas.drawPath(mDstPath, mPaint);
        canvas.restore();
    }

Windows风格加载动画绘制

预览:


windows路径绘制.mp4_1491231732.gif
  • 实现这种效果只要在上面的基础上动态改变我们所截取的startD就可以了
    只要加上这一句代码
 //通过设置其实位置的变化来实现Window加载风格
            startD = (float) (stopD - ((0.5 - Math.abs(mAnimatedValue - 0.5)) * mLength));

获取路径点的坐标和角度

预览:


箭头路径绘制.mp4_1491231817.gif

主要涉及mPathMeasure.getPosTan(float distance, float pos[], float tan[]) 这个API

  • distance : 这个参数就是确定要获取路径上那个位置的点
  • pos[] : 根据distance返回 点的坐标信息并保存在传入的pos[]内, X保存在pos[0], Y则在pos[1]
  • tan[] : 根据distance返回 点的角度信息并保存传入tan[]内 ,主要结合float degree = (float) (Math.atan2(mTan[1], mTan[0]) * 180 / Math.PI);
    这个公式 就可以求得当前点的切线和X正半轴的角度

具体绘制:

  if (mIsArrow) {
            //mPos是当前路径点的坐标
            //mTan通过下面公式可以得到当前点的切线角度
            mPathMeasure.getPosTan(mAnimatedValue * mLength, mPos, mTan);
            float degree = (float) (Math.atan2(mTan[1], mTan[0]) * 180 / Math.PI);
            Log.e("lzh", "onDraw:  degree=" + degree);
            canvas.save();
            canvas.translate(300, 300);
            canvas.drawCircle(mPos[0], mPos[1], 10, mPaint);
            canvas.rotate(degree);
            //绘制切线
            canvas.drawLine(0, -200, 200, -200, mPaint);
            canvas.restore();

        }

最后:
PathMeasure大大简化了我们去实现一个复杂路径的成本,只需要绘制好一个Path,剩下的就都交给PathMeasure搞定!
当然,其实动画本身最可贵的还是源于一个好的想法。
如果觉有有用,可以点赞鼓励一下哈O(∩_∩)O~~

附上:代码Github