属性动画源码解析

1.创建属性动画

通常我们可以通过ObjectAnimator.ofXX()的各种方法创建属性动画,所以我们可以以其中一个方法ofFloat为例进行说明。

val imageView = ImageView(context)
val anim = ObjectAnimator.ofFloat(imageView, "translationY",0f, 100f)

上面就是调用ObjectAnimator的静态方法来创建动画对象。下面说明一下各个参数的作用。

//ObjectAnimator.java
    public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
  • target: 动画作用的对象,可以是任何类型的对象
  • propertyName: 属性名,动画执行时,这个属性的值将会被改变
  • values: 可以认为是执行动画过程,动画变化的取值范围。
    进到ObjectAnimator的源码中,我们可以看到,ofFloat这个方法做了以下操作:
  1. 传入target和propertyName参数到ObjectAnimator的构造函数中,创建ObjectAnimator对象。
    private ObjectAnimator(Object target, String propertyName) {
        setTarget(target);
        setPropertyName(propertyName);
    }
  1. 往ObjectAnimator对象中设置动画值。
//ObjectAnimator.java
    @Override
    public void setFloatValues(float... values) {
        if (mValues == null || mValues.length == 0) {
            // No values yet - this animator is being constructed piecemeal. Init the values with
            // whatever the current propertyName is
            if (mProperty != null) {
                setValues(PropertyValuesHolder.ofFloat(mProperty, values));
            } else {//第一次设置mProperty肯定是为null,所以走到这里
                setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
            }
        } else {
            super.setFloatValues(values);
        }
    }

这里主要看PropertyValuesHolder.ofFloat(mPropertyName, values)

//PropertyValuesHolder.java
  public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
        return new FloatPropertyValuesHolder(propertyName, values);
    }


  public FloatPropertyValuesHolder(String propertyName, float... values) {
            super(propertyName);
            setFloatValues(values);
        }

  @Override
    public void setFloatValues(float... values) {
            super.setFloatValues(values);
            mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
        }

 public void setFloatValues(float... values) {
        mValueType = float.class;
        mKeyframes = KeyframeSet.ofFloat(values);
    }

然后就来到了KeyFrameSet中,这里就是保存KeyFrame的集合,KeyFrame就是:在float的变化范围内(0f~1f),在这个范围内每个float值所对应的数值(这个数值是我们设置进来的values)。

//KeyFrameSet.java
 public static KeyframeSet ofFloat(float... values) {
        boolean badValue = false;
        int numKeyframes = values.length;//我们传入的值的数量,也就是帧数
        FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];//最少是有两帧
//当我们只设置了一个数值时
        if (numKeyframes == 1) {
            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);//默认第一个数值为0
            keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);//values[0]就是我们设置的值,
            if (Float.isNaN(values[0])) {
                badValue = true;
            }
        } else {
//如果多于两个
            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
            for (int i = 1; i < numKeyframes; ++i) {
////把values里面的值一个一个拿出来放在KeyFrame中封装起来
                keyframes[i] =
                        (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
                if (Float.isNaN(values[i])) {
                    badValue = true;
                }
            }
        }
        if (badValue) {
            Log.w("Animator", "Bad value (NaN) in float animator");
        }
        return new FloatKeyframeSet(keyframes);
    }

初始化属性动画的过程可以用以下时序图表示:


创建属性动画

2. 开始属性动画

 anim.start()
//ObjectAnimator.java

    @Override
    public void start() {
      //应该是取消一些操作,这部分源码可以略过  AnimationHandler.getInstance().autoCancelBasedOn(this);
        if (DBG) {//debug部分也可以略过
            Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration());
            for (int i = 0; i < mValues.length; ++i) {
                PropertyValuesHolder pvh = mValues[i];
                Log.d(LOG_TAG, "   Values[" + i + "]: " +
                    pvh.getPropertyName() + ", " + pvh.mKeyframes.getValue(0) + ", " +
                    pvh.mKeyframes.getValue(1));
            }
        }
        super.start();//看父类的start的方法
    }
//ValueAnimator.java
   @Override
    public void start() {
        start(false);
    }
private void start(boolean playBackwards) {
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
...//这里略过一些代码
        mStarted = true;
        mPaused = false;
        mRunning = false;
        mAnimationEndRequested = false;
        // Resets mLastFrameTime when start() is called, so that if the animation was running,
        // calling start() would put the animation in the
        // started-but-not-yet-reached-the-first-frame phase.
        mLastFrameTime = -1;
        mFirstFrameTime = -1;
        mStartTime = -1;
        
//需要关注的第一点
addAnimationCallback(0);

        if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
            // If there's no start delay, init the animation and notify start listeners right away
            // to be consistent with the previous behavior. Otherwise, postpone this until the first
            // frame after the start delay.
//需要关注的第二点
            startAnimation();
            if (mSeekFraction == -1) {
                // No seek, start at play time 0. Note that the reason we are not using fraction 0
                // is because for animations with 0 duration, we want to be consistent with pre-N
                // behavior: skip to the final value immediately.
                setCurrentPlayTime(0);
            } else {
                setCurrentFraction(mSeekFraction);
            }
        }
    }
    private void addAnimationCallback(long delay) {
        if (!mSelfPulse) {
            return;
        }
        getAnimationHandler().addAnimationFrameCallback(this, delay);
    }
    private void startAnimation() {
        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
            Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
                    System.identityHashCode(this));
        }

        mAnimationEndRequested = false;
        initAnimation();
        mRunning = true;
        if (mSeekFraction >= 0) {
            mOverallFraction = mSeekFraction;
        } else {
            mOverallFraction = 0f;
        }
        if (mListeners != null) {
            notifyStartListeners();
        }
    }

   @CallSuper
    void initAnimation() {
        if (!mInitialized) {
            int numValues = mValues.length;
            for (int i = 0; i < numValues; ++i) {
                mValues[i].init();
            }
            mInitialized = true;
        }
    }
  • 首先看需要关注的第一个点addAnimationCallback,它是添加callback到AnimatorHandler中,前面getAnimationHandler().addAnimationFrameCallback(this, delay),添加的callback的实现就是在ValueAnimator中:
//AnimatorHandler.java
    /**
     * Register to get a callback on the next frame after the delay.
     */
    public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
        if (mAnimationCallbacks.size() == 0) {
            getProvider().postFrameCallback(mFrameCallback);
        }
        if (!mAnimationCallbacks.contains(callback)) {
            mAnimationCallbacks.add(callback);
        }

        if (delay > 0) {
            mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
        }
    }

找到AnimationFrameCallbackValueAnimator的实现,看源码这个callback会被post到Choreographer中,这里不展开。

public final boolean doAnimationFrame(long frameTime) {
...//省略一些代码
        if (!mRunning) {
            // If not running, that means the animation is in the start delay phase of a forward
            // running animation. In the case of reversing, we want to run start delay in the end.
            if (mStartTime > frameTime && mSeekFraction == -1) {
                // This is when no seek fraction is set during start delay. If developers change the
                // seek fraction during the delay, animation will start from the seeked position
                // right away.
                return false;
            } else {
                // If mRunning is not set by now, that means non-zero start delay,
                // no seeking, not reversing. At this point, start delay has passed.
                mRunning = true;
                startAnimation();
            }
        }

        if (mLastFrameTime < 0) {
            if (mSeekFraction >= 0) {
                long seekTime = (long) (getScaledDuration() * mSeekFraction);
                mStartTime = frameTime - seekTime;
                mSeekFraction = -1;
            }
            mStartTimeCommitted = false; // allow start time to be compensated for jank
        }
        mLastFrameTime = frameTime;
        // The frame time might be before the start time during the first frame of
        // an animation.  The "current time" must always be on or after the start
        // time to avoid animating frames at negative time intervals.  In practice, this
        // is very rare and only happens when seeking backwards.
        final long currentTime = Math.max(frameTime, mStartTime);
//animateBasedOnTime这里需要关注一下
        boolean finished = animateBasedOnTime(currentTime);

        if (finished) {
            endAnimation();
        }
        return finished;
    }

  • 需要关注的第二个重点startAnimation(),看代码知道它是在 ValueAnimator中调用initAnimation,但这里需要注意的是,initAnimation方法上面有一个注解,由于ValueAnimator是父类,所以我们要看一下子类是否有重写这个方法,来到ObjectAnimator可以看到如下:
  @CallSuper
  @Override
  void initAnimation() {
      if (!mInitialized) {
          // mValueType may change due to setter/getter setup; do this before calling super.init(),
          // which uses mValueType to set up the default type evaluator.
          final Object target = getTarget();
          if (target != null) {
              final int numValues = mValues.length;
              for (int i = 0; i < numValues; ++i) {
                  mValues[i].setupSetterAndGetter(target);
                }
            }
        super.initAnimation();
        }
    }

可以看到初始化动画的时候,这里是先拿到target,而这个target在创建ObjectAnimator的时候,我们已经设置进去了。然后再通过PropertyValuesHolder调用setupSetterAndGetter这个方法,mValues里面存放的就是PropertyValuesHolder对象,mValues的值也是在创建ObjectAnimator对象的时候设置进去了。
这里就来到了PropertyValuesHolder

void setupSetterAndGetter(Object target) {
        if (mProperty != null) {
            // check to make sure that mProperty is on the class of target
            try {
                Object testValue = null;
                List keyframes = mKeyframes.getKeyframes();
                int keyframeCount = keyframes == null ? 0 : keyframes.size();
                for (int i = 0; i < keyframeCount; i++) {
                    Keyframe kf = keyframes.get(i);
                    if (!kf.hasValue() || kf.valueWasSetOnStart()) {
                        if (testValue == null) {
                            testValue = convertBack(mProperty.get(target));
                        }
                        kf.setValue(testValue);
                        kf.setValueWasSetOnStart(true);
                    }
                }
                return;
            } catch (ClassCastException e) {
                Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
                        ") on target object " + target + ". Trying reflection instead");
                mProperty = null;
            }
        }
        // We can't just say 'else' here because the catch statement sets mProperty to null.
        if (mProperty == null) {
            Class targetClass = target.getClass();
            if (mSetter == null) {
                setupSetter(targetClass);
            }
            List keyframes = mKeyframes.getKeyframes();
            int keyframeCount = keyframes == null ? 0 : keyframes.size();
            for (int i = 0; i < keyframeCount; i++) {
                Keyframe kf = keyframes.get(i);
                if (!kf.hasValue() || kf.valueWasSetOnStart()) {
                    if (mGetter == null) {
                        setupGetter(targetClass);
                        if (mGetter == null) {
                            // Already logged the error - just return to avoid NPE
                            return;
                        }
                    }
                    try {
                        Object value = convertBack(mGetter.invoke(target));
                        kf.setValue(value);
                        kf.setValueWasSetOnStart(true);
                    } catch (InvocationTargetException e) {
                        Log.e("PropertyValuesHolder", e.toString());
                    } catch (IllegalAccessException e) {
                        Log.e("PropertyValuesHolder", e.toString());
                    }
                }
            }
        }
    }
   void setupSetter(Class targetClass) {
        Class propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
        mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
    }
  private Method setupSetterOrGetter(Class targetClass,
            HashMap> propertyMapMap,
            String prefix, Class valueType) {
        Method setterOrGetter = null;
        synchronized(propertyMapMap) {
            // Have to lock property map prior to reading it, to guard against
            // another thread putting something in there after we've checked it
            // but before we've added an entry to it
            HashMap propertyMap = propertyMapMap.get(targetClass);
            boolean wasInMap = false;
            if (propertyMap != null) {
                wasInMap = propertyMap.containsKey(mPropertyName);
                if (wasInMap) {
                    setterOrGetter = propertyMap.get(mPropertyName);
                }
            }
            if (!wasInMap) {
   //这里拿到setter方法
             setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
                if (propertyMap == null) {
                    propertyMap = new HashMap();
                    propertyMapMap.put(targetClass, propertyMap);
                }
                propertyMap.put(mPropertyName, setterOrGetter);
            }
        }
        return setterOrGetter;
    }
    private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
        // TODO: faster implementation...
        Method returnVal = null;
        String methodName = getMethodName(prefix, mPropertyName);
...//省略一些代码
}

//这个方法就是通过我们之前传进来的属性名来拼接成setXX方法名
   static String getMethodName(String prefix, String propertyName) {
        if (propertyName == null || propertyName.length() == 0) {
            // shouldn't get here
            return prefix;
        }
        char firstLetter = Character.toUpperCase(propertyName.charAt(0));
        String theRest = propertyName.substring(1);
        return prefix + firstLetter + theRest;
    }

可以看出子类的initAnimation()方法就是通过反射拿到目标对象的setter方法。
然后我们到父类ValueAnimatorinitAnimation()方法可以看到,它调用的是PropertyValuesHolder的init()方法,里面是给KeyFrames设置估值器,也就是在动画执行时,用于计算动画数值的。
走到这里,动画的初始化基本就完成了。
完成的工作主要是这两个方面:

  1. 设置关键帧
  2. 反射拿到setter方法

3.回调更新对象属性

上面有提到动画的执行是在回调doAnimationFrame中,我们重点关注里面调用到的方法animateBasedOnTime

//ValueAnimator.java
    boolean animateBasedOnTime(long currentTime) {
        boolean done = false;
        if (mRunning) {
            final long scaledDuration = getScaledDuration();
            final float fraction = scaledDuration > 0 ?
                    (float)(currentTime - mStartTime) / scaledDuration : 1f;
            final float lastFraction = mOverallFraction;
            final boolean newIteration = (int) fraction > (int) lastFraction;
            final boolean lastIterationFinished = (fraction >= mRepeatCount + 1) &&
                    (mRepeatCount != INFINITE);
            if (scaledDuration == 0) {
                // 0 duration animator, ignore the repeat count and skip to the end
                done = true;
            } else if (newIteration && !lastIterationFinished) {
                // Time to repeat
                if (mListeners != null) {
                    int numListeners = mListeners.size();
                    for (int i = 0; i < numListeners; ++i) {
                        mListeners.get(i).onAnimationRepeat(this);
                    }
                }
            } else if (lastIterationFinished) {
                done = true;
            }
            mOverallFraction = clampFraction(fraction);
            float currentIterationFraction = getCurrentIterationFraction(
                    mOverallFraction, mReversing);

    //这里是重点     
       animateValue(currentIterationFraction);
        }
        return done;
    }


    void animateValue(float fraction) {
        fraction = mInterpolator.getInterpolation(fraction);
        mCurrentFraction = fraction;
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
           
    //这里又到了PropertyValuesHolder mValues[i].calculateValue(fraction);
        }
        if (mUpdateListeners != null) {
            int numListeners = mUpdateListeners.size();
            for (int i = 0; i < numListeners; ++i) {
                mUpdateListeners.get(i).onAnimationUpdate(this);
            }
        }
    }
//PropertyValuesHolder.java
  void calculateValue(float fraction) {
//通过fraction拿到value,mKeyframes的value之前初始化ObjectAnimator的时候就设置进去了
        Object value = mKeyframes.getValue(fraction);

//计算出动画的值
        mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
    }

上面就是那个更新的值的过程,那么更新的动画值在哪里设置进去并更新对象的属性呢?我们再看一下ValueAnimator的animateValue方法,这里有个坑就是ObjectAnimator也重写了这个方法,刚开始哪里都是找不到更细属性值的地方,原来是在ObjectAnimator里面调用了,所以我们来到这里

//ObjectAnimator.java
   @CallSuper
    @Override
    void animateValue(float fraction) {
        final Object target = getTarget();
        if (mTarget != null && target == null) {
            // We lost the target reference, cancel and clean up. Note: we allow null target if the
            /// target has never been set.
            cancel();
            return;
        }

   //这里调用父类的方法计算动画的值
     super.animateValue(fraction);
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
//计算后的值是更新到PropertyValuesHolder中,在这里调用了设置动画值            mValues[i].setAnimatedValue(target);
        }
    }
//PropertyValuesHolder.java
       @Override
        void setAnimatedValue(Object target) {
            if (mFloatProperty != null) {
                mFloatProperty.setValue(target, mFloatAnimatedValue);
                return;
            }
            if (mProperty != null) {
                mProperty.set(target, mFloatAnimatedValue);
                return;
            }
            if (mJniSetter != 0) {
                nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
                return;
            }
            if (mSetter != null) {
//之前我们有看到通过反射拿到了setter方法,然后这里就是通过反射去更新属性值了
                try {
                    mTmpValueArray[0] = mFloatAnimatedValue;
                    mSetter.invoke(target, mTmpValueArray);
                } catch (InvocationTargetException e) {
                    Log.e("PropertyValuesHolder", e.toString());
                } catch (IllegalAccessException e) {
                    Log.e("PropertyValuesHolder", e.toString());
                }
            }
        }

到这里,属性动画从创建、启动到更新属性值的大概过程已经梳理出来了。最后放上动画启动的源码调用时序图。

启动属性动画时序图

你可能感兴趣的:(属性动画源码解析)