属性动画绘制原理

3,121 阅读4分钟

本文主要解决以下问题:

  • 动画的整体框架?
  • view是如何动起来的?

好了,接下来,开始打怪~

一、动画的整体框架

首先,我们平时写属性动画是这么写的:

ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(v,"scaleX",0,1);
        objectAnimator.setEvaluator(new FloatEvaluator());
        objectAnimator.setInterpolator(new LinearInterpolator());
        objectAnimator.setDuration(1000);
        objectAnimator.start();

so,我们先从ofFloat开始

    //1、入口
    public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
        anim.setFloatValues(values);
        return anim;
    }
    
    //2、看new ObjectAnimator做了什么?
        private ObjectAnimator(Object target, String propertyName) {
        setTarget(target);
        setPropertyName(propertyName);
    }
    
    //3、setTarget 进去look look
        @Override
    public void setTarget(@Nullable Object target) {
        .....
        //就弱引用,没啥
            mTarget = target == null ? null : new WeakReference<Object>(target);
          ....
        }
    }
    //4、setPropertyName 瞄瞄
    
        public void setPropertyName(@NonNull String propertyName) {
       ......
       //依旧只是保存值
        mPropertyName = propertyName;
       ......
    }
    
    // 5、看看anim.setFloatValues(values) 干了啥
       @Override
    public void setFloatValues(float... values) {
        if (mValues == null || mValues.length == 0) {
            if (mProperty != null) {
            //注意注意,重点人物之一的PropertyValuesHolder出现了!!!
                setValues(PropertyValuesHolder.ofFloat(mProperty, values));
            } else {
                setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
            }
        } else {
            super.setFloatValues(values);
        }
    }
//6、跟踪 PropertyValuesHolder.ofFloat 方法,最终会调用setFloatValues这个方法
    public void setFloatValues(float... values) {
        mValueType = float.class;
        //注意注意,重点人物 KeyframeSet 也出现了!!!
        mKeyframes = KeyframeSet.ofFloat(values);
    }
    
  // 7、跟踪KeyframeSet.ofFloat方法  
        public static KeyframeSet ofFloat(float... values) {
       .....
        int numKeyframes = values.length;
        FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
        if (numKeyframes == 1) {
            .....
        } else {
        //可以看到,这里根据values的长度,构造了keyframes数组,
        //然后分别通过Keyframe的ofFloat方法,去构造keyframe对象
        //Keyframe就是关键帧,它里面保存了类型、数值以及估值器
            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
            for (int i = 1; i < numKeyframes; ++i) {
                keyframes[i] =
                        (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
                if (Float.isNaN(values[i])) {
                    badValue = true;
                }
            }
        }
        ......
        return new FloatKeyframeSet(keyframes);
    }
    
// 9、还是你们看一个Keyframes源码吧,其实就是一个接口

public interface Keyframes extends Cloneable {

    void setEvaluator(TypeEvaluator evaluator);
    Class getType();
    Object getValue(float fraction);
    List<Keyframe> getKeyframes();
    Keyframes clone()
    ....
}

第一步我们就跟完了,我们看到,在初始化的整个过程中,涉及到了以下几个对象:

而这几个对象就构成了我们属性动画的框架。

接下来,我们要来看一下我们的view是怎么动起来的~

二、view是怎么动起来的


//1、从 objectAnimator.start()进入
    public void start() {
        AnimationHandler.getInstance().autoCancelBasedOn(this);
       .....
        super.start();
    }
 
 //2、点击super.start()一路跟到ValueAnimator的start(playBackwrds)方法
 private void start(boolean playBackwards) {
      .....
      //跟踪这个方法
        addAnimationCallback(0);
        if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
        ......
        //这个方法其实就是去初始化Animation,这里就不跟踪了
        //小伙伴可以进行去阅读
            startAnimation();
            if (mSeekFraction == -1) {
                .....
                setCurrentPlayTime(0);
            } else {
                setCurrentFraction(mSeekFraction);
            }
        }
    }
    
//3、跟踪 addAnimationCallback(0)方法 ,最后可以来到AnimationHandler中的addAnimationFrameCallback方法
    public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
    //这里发送了一个mFrameCallback,跟一下mFrameCallback
       if (mAnimationCallbacks.size() == 0) {
            getProvider().postFrameCallback(mFrameCallback);
        }
       //在这里添加了一个callback,并且这个callback是ValueAnimator!!记住,很重要!!
        if (!mAnimationCallbacks.contains(callback)) {
            mAnimationCallbacks.add(callback);
        }
....
    } 
  //4、mFrameCallback
  private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
        @Override
        public void doFrame(long frameTimeNanos) {
            doAnimationFrame(getProvider().getFrameTime());
            if (mAnimationCallbacks.size() > 0) {
                getProvider().postFrameCallback(this);
            }
        }
    };
   //5、看Choreographer.FrameCallback的定义
    /**
     * Implement this interface to receive a callback when a new display frame is
     * being rendered.  The callback is invoked on the {@link Looper} thread to
     * which the {@link Choreographer} is attached.
     */
    public interface FrameCallback {
        /**
         * Called when a new display frame is being rendered.
         * <p>
         * 也就是说,每次重新开始渲染屏幕上的帧时,这个方法将会被调用。
         */
        public void doFrame(long frameTimeNanos);
  }
  //6、那这个doFrame方法在哪里被调用呢?用doFrame作为关键字在Choreographer查找,最后发现,在这里进行了调用
  private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
        private boolean mHavePendingVsync;
        private long mTimestampNanos;
        private int mFrame;

        public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
            super(looper, vsyncSource);
        }

//看到熟悉的Vysnc没,从字眼我们也大概可以推断,
//每次Vysnc信号来时,都会回调这个方法,最终会执行run()方法里面的代码
//最后,回调回去我们的Choreographer.FrameCallback的doFrame方法
//这个回调的频率大概是16ms一次
        @Override
        public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
           ....
           Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }

        @Override
        public void run() {
            mHavePendingVsync = false;
            doFrame(mTimestampNanos, mFrame);
        }
    }
    
//7、这一段追完了,回去我们的 mFrameCallback   
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
        @Override
        public void doFrame(long frameTimeNanos){
        //跟进这个方法
            doAnimationFrame(getProvider().getFrameTime());
            if (mAnimationCallbacks.size() > 0) {
                getProvider().postFrameCallback(this);
            }
        }
    };
//8、 doAnimationFrame
    private void doAnimationFrame(long frameTime) {
        long currentTime = SystemClock.uptimeMillis();
        final int size = mAnimationCallbacks.size();
        for (int i = 0; i < size; i++) {
            final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
            if (callback == null) {
                continue;
            }
            if (isCallbackDue(callback, currentTime)) {
            //还记得前面add了个callback吗?那个callback是ValueAnimator,所以,现在我们要去看一下ValueAnimator的doAnimationFrame方法了
                callback.doAnimationFrame(frameTime);
                .....
            }
        }
        ....
    }
//9、 ValueAnimator的doAnimationFrame  ,还有几步就完了,坚持一下
    public final boolean doAnimationFrame(long frameTime) {
     ....
     //重点在这个方法
        boolean finished = animateBasedOnTime(currentTime);
    ....
        return finished;
    }
    
//10、animateBasedOnTime
 boolean animateBasedOnTime(long currentTime) {
          ....
          //跟进这个方法,这里要注意,如果直接点击去,会去到ValueAnimator里面的animateValue,
          //但是实际上,ObjectAnimator覆盖了这个方法,所以我们要去看ObjectAnimator的animateValue方法
            animateValue(currentIterationFraction);
         ....
    }
//11、ObjectAnimator的animateValue方法
   @Override
    void animateValue(float fraction) {
      ....
      //super.animateValue可以自己去跟,其实就是利用interpolator和evaluator计算出当前动画对应的数值
        super.animateValue(fraction);
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
        //看这个setAnimatedValue方法
            mValues[i].setAnimatedValue(target);
        }
    }
、、12、PropertyValuesHolder的 setAnimatedValue
   void setAnimatedValue(Object target) {
        if (mProperty != null) {
            mProperty.set(target, getAnimatedValue());
        }
        if (mSetter != null) {
            try {
                mTmpValueArray[0] = getAnimatedValue();
                //看到没,其实就是通过反射动态调用setXX方法
                //通过set方法去动态改变View的属性,从而实现动画效果
                mSetter.invoke(target, mTmpValueArray);
            } ...
        }
    }

呼,累死了~~

到这里,让view动起来的整个流程就结束了。总结一下:

  • 监听Vsync信号,每次信号一来就回调ValueAnimatordoAnimationFrame方法
  • 通过InterpolatorEvaluator计算出当前动画的百分比和具体数值
  • 调用PropertyValuesHoldersetAnimatedValue方法,去反射调用setXX,从而实现动态改变view的属性

简化后的流程就是:

本文结束!


感谢观看,有不对的地方,欢迎大家指出~~