天天看点

属性动画源码分析属性动画简析

属性动画简析

本文主要结合源码阐述以下几部分:

1.属性值不断变化是怎么做到的?定时器?Handler?Choreographer?

2.值是怎么设置到View上的?

3.插值器和估值器何时起作用的?

4.自定义插值器,估值器

一. 常规用法

可以使用ObjectAnimator,ValueAnimator,View 的animate(),AnimatorSet 等来实现,此处对如何使用不做展开。

ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(item, "translationX", 0, 40, -30, 20, -10, 5, -5, 0);
objectAnimator.setDuration(1000);
objectAnimator.setEvaluator(new FloatEvaluator());
objectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
objectAnimator.start();
           

查看源码,可知ObjectAnimator继承自ValueAnimator;animate()快捷方式是得到ViewPropertyAnimator,而ViewPropertyAnimator内部其实使用的也是ValueAnimator。

产生动画效果可简单理解为:1. 值变; 2. UI更新。ValueAnimator扮演的就是属性值变更和通知的角色,ObjectAnimator,ViewPropertyAnimator则是在他上又一次的封装。

二.start之后

start()最终会调用如下代码,由注释和源码可见器主要作用是完成一些参数和标志的初始化,同时注册监听,来实现不断的变化回调。

/**
 * Start the animation playing. This version of start() takes a boolean flag that indicates
 * whether the animation should play in reverse. The flag is usually false, but may be set
 * to true if called from the reverse() method.
 *
 * <p>The animation started by calling this method will be run on the thread that called
 * this method. This thread should have a Looper on it (a runtime exception will be thrown if
 * this is not the case). Also, if the animation will animate
 * properties of objects in the view hierarchy, then the calling thread should be the UI
 * thread for that view hierarchy.</p>
 *
 * @param playBackwards Whether the ValueAnimator should start playing in reverse.
 */
private void start(boolean playBackwards) {
    if (Looper.myLooper() == null) {
        throw new AndroidRuntimeException("Animators may only be run on Looper threads");
    }
    mReversing = playBackwards;
    mSelfPulse = !mSuppressSelfPulseRequested;
    // Special case: reversing from seek-to-0 should act as if not seeked at all.
    if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {
        if (mRepeatCount == INFINITE) {
            // Calculate the fraction of the current iteration.
            float fraction = (float) (mSeekFraction - Math.floor(mSeekFraction));
            mSeekFraction = 1 - fraction;
        } else {
            mSeekFraction = 1 + mRepeatCount - mSeekFraction;
        }
    }
    //设置标志位
    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;
    }
    //AnimationFrameCallback 
    getAnimationHandler().addAnimationFrameCallback(this, delay);
}
................
................
 
 
AnimationHandler.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) {
        //进行注册,关注mFrameCallback里面的逻辑,会根据动画执行状态再次post,来达到不断循环的目的
        getProvider().postFrameCallback(mFrameCallback);
    }
    if (!mAnimationCallbacks.contains(callback)) {
        mAnimationCallbacks.add(callback);
    }
 
    if (delay > 0) {
        mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
    }
}
 
 
//上面的 getProvider()得到的是一个MyFrameCallbackProvider对象。通过postFrameCallback,最终执行到mChoreographer.postFrameCallback(callback);注册到mChoreographer里
 
 
/**
 * Default provider of timing pulse that uses Choreographer for frame callbacks.
 */
private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {
 
    final Choreographer mChoreographer = Choreographer.getInstance();
 
    @Override
    public void postFrameCallback(Choreographer.FrameCallback callback) {
        mChoreographer.postFrameCallback(callback);
    }
 
    @Override
    public void postCommitCallback(Runnable runnable) {
        mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, runnable, null);
    }
 
    @Override
    public long getFrameTime() {
        return mChoreographer.getFrameTime();
    }
 
    @Override
    public long getFrameDelay() {
        return Choreographer.getFrameDelay();
    }
 
    @Override
    public void setFrameDelay(long delay) {
        Choreographer.setFrameDelay(delay);
    }
}
 
 
 
调用Choreographer的postFrameCallback注册后,会在下一帧刷新时回调,由注释可知回调执行一次之后便被清掉了,因此,要想不断地循环就需要每次收到回调之后再进行注册

/**
 * Posts a frame callback to run on the next frame.
 * <p>
 * The callback runs once then is automatically removed.
 * </p>
 *
 * @param callback The frame callback to run during the next frame.
 *
 * @see #postFrameCallbackDelayed
 * @see #removeFrameCallback
 */
public void postFrameCallback(FrameCallback callback) {
    postFrameCallbackDelayed(callback, 0);
}
我们注册的回调mFrameCallback,里进行了再次post
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
    @Override
    public void doFrame(long frameTimeNanos) {
        doAnimationFrame(getProvider().getFrameTime());
        if (mAnimationCallbacks.size() > 0) {
            getProvider().postFrameCallback(this);
        }
    }
};
           

三.属性变化监听

数值变更流程:

属性动画源码分析属性动画简析

View属性设置流程:

上文分析了整个属性动画达到不断循环,随着时间不断调整动画“进度”的整体流程。这一部分则是概述一下,动画属性值改变之后是怎么设置到View上的。

1. 对于我们自己直接使用ValueAnimator实现的动画,只是提供数值的变化,通过onAnimationUpdate接口回调给我们,我们在这里自己把实时变化的属性值设置到想要设置的View上即可。

2. ObjectAnimator是Android提供的对ValueAnimator的进一步封装,来简化我们的开发。这里则主要分析它的实现原理。

A.创建ObjectAnimator对象时记录对应的target和属性名

/**
 * Constructs and returns an ObjectAnimator that animates between float values. A single
 * value implies that that value is the one being animated to, in which case the start value
 * will be derived from the property being animated and the target object when {@link #start()}
 * is called for the first time. Two values imply starting and ending values. More than two
 * values imply a starting value, values to animate through along the way, and an ending value
 * (these values will be distributed evenly across the duration of the animation).
 *
 * @param target The object whose property is to be animated.
 * @param property The property being animated.
 * @param values A set of values that the animation will animate between over time.
 * @return An ObjectAnimator object that is set up to animate between the given values.
 */
public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> property,
        float... values) {
    ObjectAnimator anim = new ObjectAnimator(target, property);
    anim.setFloatValues(values);
    return anim;
}
.......
/**
 * Private utility constructor that initializes the target object and property being animated.
 *
 * @param target The object whose property is to be animated.
 * @param property The property being animated.
 */
private <T> ObjectAnimator(T target, Property<T, ?> property) {
    setTarget(target);
    setProperty(property);
}
           

B.在动画渐进过程,进行通知回调,回调中设置View属性, 接口回调的链路上文已记录,这里从回调之后开始,即回调doAnimationFrame(...)后的处理逻辑。大致时序图如下。

属性动画源码分析属性动画简析

其中,setAnimatedValue(...)方法便完成了属性设置,这里是通过反射设置属性到target,也即要设置属性动画的View对象。(此处以FloatPropertyValuesHolder为例,其他类似)

/**
 * Internal function to set the value on the target object, using the setter set up
 * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
 * to handle turning the value calculated by ValueAnimator into a value set on the object
 * according to the name of the property.
 * @param target The target object on which the value is set
 */
@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) {
        try {
            mTmpValueArray[0] = mFloatAnimatedValue;
            mSetter.invoke(target, mTmpValueArray);
        } catch (InvocationTargetException e) {
            Log.e("PropertyValuesHolder", e.toString());
        } catch (IllegalAccessException e) {
            Log.e("PropertyValuesHolder", e.toString());
        }
    }
}
           

上面流程省略了反射方法存储记录相关流程,这一部分主要是伴随着动画创建,参数初始化层层传递,具体实现都在PropertyValuesHolder中,包括属性方法是否存在,set/get方法和属性拼接,大小写转换等,此处不做展开。

插值器和估值器何时起作用的?

接上文时序图,可以看到在更新view属性是,有一步是要执行animateValue(...);插值器和估值器就是在这里使用的

/**
 * This method is called with the elapsed fraction of the animation during every
 * animation frame. This function turns the elapsed fraction into an interpolated fraction
 * and then into an animated value (from the evaluator. The function is called mostly during
 * animation updates, but it is also called when the <code>end()</code>
 * function is called, to set the final value on the property.
 *
 * <p>Overrides of this method must call the superclass to perform the calculation
 * of the animated value.</p>
 *
 * @param fraction The elapsed fraction of the animation.
 */
@CallSuper
void animateValue(float fraction) {
    //使用插值器getInterpolation
    fraction = mInterpolator.getInterpolation(fraction);
    mCurrentFraction = fraction;
    int numValues = mValues.length;
    for (int i = 0; i < numValues; ++i) {
        //跳转到PropertyValuesHolder,针对ofFloat(...)方法,跳转到的是其子类FloatPropertyValuesHolder
        mValues[i].calculateValue(fraction);
    }
    if (mUpdateListeners != null) {
        int numListeners = mUpdateListeners.size();
        for (int i = 0; i < numListeners; ++i) {
            mUpdateListeners.get(i).onAnimationUpdate(this);
        }
    }
}
.....
//FloatPropertyValuesHolder
@Override
void calculateValue(float fraction) {
    //进一步会跳转到KeyFrame,针对float属性跳转到的是实现类FloatKeyframeSet.java
    mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
}
.....
 
 
//FloatKeyframeSet.java
@Override
public float getFloatValue(float fraction) {
    if (fraction <= 0f) {
        final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
        final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(1);
        float prevValue = prevKeyframe.getFloatValue();
        float nextValue = nextKeyframe.getFloatValue();
        float prevFraction = prevKeyframe.getFraction();
        float nextFraction = nextKeyframe.getFraction();
        final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
        if (interpolator != null) {
            fraction = interpolator.getInterpolation(fraction);
        }
        float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
        return mEvaluator == null ?
                prevValue + intervalFraction * (nextValue - prevValue) :
                //不同条件下,进入到对应分支
                ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
                        floatValue();
    } else if (fraction >= 1f) {
        final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 2);
        final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 1);
        float prevValue = prevKeyframe.getFloatValue();
        float nextValue = nextKeyframe.getFloatValue();
        float prevFraction = prevKeyframe.getFraction();
        float nextFraction = nextKeyframe.getFraction();
        final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
        if (interpolator != null) {
            fraction = interpolator.getInterpolation(fraction);
        }
        float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
        return mEvaluator == null ?
                prevValue + intervalFraction * (nextValue - prevValue) :
                //不同条件下,进入到对应分支
                ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
                        floatValue();
    }
    FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
    for (int i = 1; i < mNumKeyframes; ++i) {
        FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(i);
        if (fraction < nextKeyframe.getFraction()) {
            final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
            float intervalFraction = (fraction - prevKeyframe.getFraction()) /
                (nextKeyframe.getFraction() - prevKeyframe.getFraction());
            float prevValue = prevKeyframe.getFloatValue();
            float nextValue = nextKeyframe.getFloatValue();
            // Apply interpolator on the proportional duration.
            if (interpolator != null) {
                intervalFraction = interpolator.getInterpolation(intervalFraction);
            }
            return mEvaluator == null ?
                    prevValue + intervalFraction * (nextValue - prevValue) :
                    //不同条件下,进入到对应分支
                    ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
                        floatValue();
        }
        prevKeyframe = nextKeyframe;
    }
    // shouldn't get here
    return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).floatValue();
}