天天看點

屬性動畫源碼分析屬性動畫簡析

屬性動畫簡析

本文主要結合源碼闡述以下幾部分:

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();
}