Android 属性动画原理

4,156 阅读4分钟

ValueAnimator

本身不会产生任何的动画,它的作用是根据输入的值区间,产生一个在某个时间点的具体值。下面用个通俗的例子来说明。

假设A,B两地相距1000km,一辆汽车要以均匀速度由A到B,假设我们要求在10小时必须到达B。

有了以上的条件我们可以求得任何时刻汽车的具体位置。下面先从一个基本的例子开始

ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 100);
valueAnimator.setDuration(3000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        int currentValue = (Integer) animation.getAnimatedValue(); //在PropertyValuesHolder中取值
        setX(currentValue);
    }
});
valueAnimator.start();

类比上面的问题,0~100是区间,时间是3000ms,可以求得start()后的3000ms中的变化。取得某个时刻的值,然后设置,连续起来就成了动画。

public static ValueAnimator ofInt(int... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setIntValues(values);
    return anim;
}

public void setIntValues(int... values) {
    if (values == null || values.length == 0) {
        return;
    }
    if (mValues == null || mValues.length == 0) {
        setValues(PropertyValuesHolder.ofInt("", values)); //注意这里属性名字为空,在ObjectAnimator就必须为带有setter和getter的方法
    } else {
        PropertyValuesHolder valuesHolder = mValues[0];
        valuesHolder.setIntValues(values);
    }
    // New property/values/target should cause re-initialization prior to starting
    mInitialized = false;
}

//PropertyValuesHolder.ofInt("", values)内部调用
@Override
public void setIntValues(int... values) {
    super.setIntValues(values);
    mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes;
}

Keyframe类表示动画的关键时刻(这里例子这有起始点和终止点)包括关键时间和该时间点对应的值以及插值器等,也就是我们设置的特殊点,比如ValueAnimator.ofInt(0, 100, 200),这样特殊点的数量就是3个了,当只有1个特殊点时,内部会处理为2个,其中第一个的值默认设为0。接下来分析start()方法

private void start(boolean playBackwards){
    ...对于重复播放、反转播放的处理
    animationHandler.mPendingAnimations.add(this);
    if (mStartDelay == 0) {
        // This sets the initial value of the animation, prior to actually starting it running
        if (prevPlayingState != SEEKED) {
            setCurrentPlayTime(0);
        }
        mPlayingState = STOPPED;
        mRunning = true;
        notifyStartListeners();
    }
    animationHandler.start();
}

我们发现这里的主要功能是把待播放动画加入到animationHandler.mPendingAnimations,表示该动画待播放。接着启动这个动画队列。

public void start() {
    scheduleAnimation();
}

private void scheduleAnimation() {
    if (!mAnimationScheduled) {
        mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
        mAnimationScheduled = true;
    }
}

final Runnable mAnimate = new Runnable() {
    @Override
    public void run() {
        mAnimationScheduled = false;
        doAnimationFrame(mChoreographer.getFrameTime());
    }
};

mChoreographer.postCallback()执行一个Runnable,在Runnable中执行doAnimationFrame(),这个函数比较容易理解,就是执行动画的一帧,把满足条件的待执行的动画启动起来,已经执行的执行下一帧。

//AnimationHandler
void doAnimationFrame(long frameTime) {
    mLastFrameTime = frameTime;

    // mPendingAnimations holds any animations that have requested to be started
    // We're going to clear mPendingAnimations, but starting animation may
    // cause more to be added to the pending list (for example, if one animation
    // starting triggers another starting). So we loop until mPendingAnimations
    // is empty.
    while (mPendingAnimations.size() > 0) {
        ArrayList pendingCopy =
                (ArrayList) mPendingAnimations.clone();
        mPendingAnimations.clear();
        int count = pendingCopy.size();
        for (int i = 0; i < count; ++i) {
            ValueAnimator anim = pendingCopy.get(i);
            // If the animation has a startDelay, place it on the delayed list
            if (anim.mStartDelay == 0) {
                anim.startAnimation(this); //初始化,加入handler.mAnimations
            } else {
                mDelayedAnims.add(anim);
            }
        }
    }

    // Next, process animations currently sitting on the delayed queue, adding
    // them to the active animations if they are ready
    int numDelayedAnims = mDelayedAnims.size();
    for (int i = 0; i < numDelayedAnims; ++i) {
        ValueAnimator anim = mDelayedAnims.get(i);
        if (anim.delayedAnimationFrame(frameTime)) {
            mReadyAnims.add(anim);
        }
    }
    int numReadyAnims = mReadyAnims.size();
    if (numReadyAnims > 0) {
        for (int i = 0; i < numReadyAnims; ++i) {
            ValueAnimator anim = mReadyAnims.get(i);
            anim.startAnimation(this);
            anim.mRunning = true;
            mDelayedAnims.remove(anim);
        }
        mReadyAnims.clear();
    }

    // Now process all active animations. The return value from animationFrame()
    // tells the handler whether it should now be ended
    int numAnims = mAnimations.size(); //上面添加进来的
    for (int i = 0; i < numAnims; ++i) {
        mTmpAnimations.add(mAnimations.get(i));
    }
    for (int i = 0; i < numAnims; ++i) {
        ValueAnimator anim = mTmpAnimations.get(i);
        if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) { //执行动画
            mEndingAnims.add(anim);
        }
    }
    mTmpAnimations.clear();
    if (mEndingAnims.size() > 0) {                    //已经执行完的动画
        for (int i = 0; i < mEndingAnims.size(); ++i) {
            mEndingAnims.get(i).endAnimation(this);
        }
        mEndingAnims.clear();
    }

    // Schedule final commit for the frame.
    mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, mCommit, null);

    // If there are still active or delayed animations, schedule a future call to
    // onAnimate to process the next frame of the animations.
    if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
        scheduleAnimation();                           //请求执行下一帧,循环请求,所以才有动画效果
    }
}

好的,很顺利,下面看看anim.doAnimationFrame(frameTime),动画的执行过程。

final boolean doAnimationFrame(long frameTime) {
    ...
    return animationFrame(currentTime);
}

方法核心就是执行animationFrame(),接着看看到底是个什么。

boolean animationFrame(long currentTime) {
    boolean done = false;
    switch (mPlayingState) {
    case RUNNING:
    case SEEKED:
        float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
        if (mDuration == 0 && mRepeatCount != INFINITE) {
            // Skip to the end
            mCurrentIteration = mRepeatCount;
            if (!mReversing) {
                mPlayingBackwards = false;
            }
        }
        if (fraction >= 1f) {
            if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
                // Time to repeat
                if (mListeners != null) {
                    int numListeners = mListeners.size();
                    for (int i = 0; i < numListeners; ++i) {
                        mListeners.get(i).onAnimationRepeat(this);
                    }
                }
                if (mRepeatMode == REVERSE) {
                    mPlayingBackwards = !mPlayingBackwards;
                }
                mCurrentIteration += (int) fraction;
                fraction = fraction % 1f;
                mStartTime += mDuration;
                // Note: We do not need to update the value of mStartTimeCommitted here
                // since we just added a duration offset.
            } else {
                done = true;
                fraction = Math.min(fraction, 1.0f);
            }
        }
        if (mPlayingBackwards) {
            fraction = 1f - fraction;
        }
        animateValue(fraction);
        break;
    }

    return done;
}

就是计算当前时间占总时间的百分比,另外处理反转重复时时间的相对位置,用来我们以后计算在这个事件点我们动画应该执行到什么位置。下面看看animateValue()吧。

@CallSuper
void animateValue(float fraction) {
    fraction = mInterpolator.getInterpolation(fraction); //根据插值器取得具体的值
    mCurrentFraction = fraction;
    int numValues = mValues.length;
    for (int i = 0; i < numValues; ++i) {
        mValues[i].calculateValue(fraction);  //计算mAnimatedValue,在回调中使用
    }
    if (mUpdateListeners != null) {
        int numListeners = mUpdateListeners.size();
        for (int i = 0; i < numListeners; ++i) {
            mUpdateListeners.get(i).onAnimationUpdate(this); //回调给Listener
        }
    }
}
//PropertyValuesHolder
void calculateValue(float fraction) {
    Object value = mKeyframes.getValue(fraction);
    mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
}

这里我们看看mKeyframes.getValue(fraction)的实现,前面说过“关键点”,这里主要看一下两个关键点的情况。

public Object getValue(float fraction) {
    // Special-case optimization for the common case of only two keyframes
    if (mNumKeyframes == 2) {
        if (mInterpolator != null) {
            fraction = mInterpolator.getInterpolation(fraction);
        }
        return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(),
                mLastKeyframe.getValue());
    }
    //....其他数量的关键点情况
}

这里又有一个插值器,默认为空,下面就是根据比列获取正式值。然后赋值给mAnimatedValue,在AnimatorUpdateListener中取得该值即可。

ObjectAnimator

我们知道ObjectAnimatorValueAnimator的子类,所以很多逻辑实际都是一样的,就是多了步把动画的值主动设置给某个对象。下面看看不同部分。

@CallSuper
@Override
void animateValue(float fraction) {
    final Object target = getTarget();
    if (mTarget != null && target == null) {
        // We lost the target reference, cancel and clean up.
        cancel();
        return;
    }

    super.animateValue(fraction); //调用父类的计算方法
    int numValues = mValues.length;
    for (int i = 0; i < numValues; ++i) {
        mValues[i].setAnimatedValue(target); //赋值给指定的对象
    }
}

//PropertyValuesHolder
void setAnimatedValue(Object target) {
    if (mProperty != null) {
        mProperty.set(target, getAnimatedValue());
    }
    if (mSetter != null) {
        try {
            mTmpValueArray[0] = getAnimatedValue();
            mSetter.invoke(target, mTmpValueArray);//反射调用
        } catch (InvocationTargetException e) {
            Log.e("PropertyValuesHolder", e.toString());
        } catch (IllegalAccessException e) {
            Log.e("PropertyValuesHolder", e.toString());
        }
    }
}

还有部分不同就是需要获取settergetter方法,这个利用java反射很容易取得,这里就不多讲了,大家可以看看PropertyValuesHolder#setupSetterAndGetter(obj)

参考

Android属性动画ValueAnimator源码简单分析