出处:http://blog.csdn.net/singwhatiwanna/article/details/17853275
前言
本文为Android动画系列的最后一篇文章,通过对源码的分析,能够让大家更深刻地理解属性动画的工作原理,这有助于我们更好地使用属性动画。但是,由于动画的底层实现已经深入到jni层,并且涉及到显示子系统,因此,深入地分析动画的底层实现不仅比较困难而且意义不大,因此,本文的分析到jni层为止。
Android动画系列:
Android动画进阶—使用开源动画库nineoldandroids
Android源码分析—属性动画的工作原理属性动画的原理
属性动画要求动画作用的对象提供该属性的set方法,属性动画根据你传递的该熟悉的初始值和最终值,以动画的效果多次去调用set方法,每次传递给set方法的值都不一样,确切来说是随着时间的推移,所传递的值越来越接近最终值。如果动画的时候没有传递初始值,那么还要提供get方法,因为系统要去拿属性的初始值。对于属性动画来说,其动画过程中所做的就是这么多,下面看源码分析。
源码分析
首先我们要找一个入口,就从ObjectAnimator.ofInt(mButton, "width", 500).setDuration(5000).start()开始吧,其他动画都是类似的。
看ObjectAnimator的start方法
- @Override
- public void start() {
- // See if any of the current active/pending animators need to be canceled
- AnimationHandler handler = sAnimationHandler.get();
- if (handler != null) {
- int numAnims = handler.mAnimations.size();
- for (int i = numAnims - 1; i >= 0; i--) {
- if (handler.mAnimations.get(i) instanceof ObjectAnimator) {
- ObjectAnimator anim = (ObjectAnimator) handler.mAnimations.get(i);
- if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
- anim.cancel();
- }
- }
- }
- numAnims = handler.mPendingAnimations.size();
- for (int i = numAnims - 1; i >= 0; i--) {
- if (handler.mPendingAnimations.get(i) instanceof ObjectAnimator) {
- ObjectAnimator anim = (ObjectAnimator) handler.mPendingAnimations.get(i);
- if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
- anim.cancel();
- }
- }
- }
- numAnims = handler.mDelayedAnims.size();
- for (int i = numAnims - 1; i >= 0; i--) {
- if (handler.mDelayedAnims.get(i) instanceof ObjectAnimator) {
- ObjectAnimator anim = (ObjectAnimator) handler.mDelayedAnims.get(i);
- if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
- anim.cancel();
- }
- }
- }
- }
- if (DBG) {
- Log.d("ObjectAnimator", "Anim target, duration: " + mTarget + ", " + getDuration());
- for (int i = 0; i < mValues.length; ++i) {
- PropertyValuesHolder pvh = mValues[i];
- ArrayList<Keyframe> keyframes = pvh.mKeyframeSet.mKeyframes;
- Log.d("ObjectAnimator", " Values[" + i + "]: " +
- pvh.getPropertyName() + ", " + keyframes.get(0).getValue() + ", " +
- keyframes.get(pvh.mKeyframeSet.mNumKeyframes - 1).getValue());
- }
- }
- super.start();
- }
- private void start(boolean playBackwards) {
- if (Looper.myLooper() == null) {
- throw new AndroidRuntimeException("Animators may only be run on Looper threads");
- }
- mPlayingBackwards = playBackwards;
- mCurrentIteration = 0;
- mPlayingState = STOPPED;
- mStarted = true;
- mStartedDelay = false;
- mPaused = false;
- AnimationHandler animationHandler = getOrCreateAnimationHandler();
- animationHandler.mPendingAnimations.add(this);
- if (mStartDelay == 0) {
- // This sets the initial value of the animation, prior to actually starting it running
- setCurrentPlayTime(0);
- mPlayingState = STOPPED;
- mRunning = true;
- notifyStartListeners();
- }
- animationHandler.start();
- }
- final boolean doAnimationFrame(long frameTime) {
- if (mPlayingState == STOPPED) {
- mPlayingState = RUNNING;
- if (mSeekTime < 0) {
- mStartTime = frameTime;
- } else {
- mStartTime = frameTime - mSeekTime;
- // Now that we're playing, reset the seek time
- mSeekTime = -1;
- }
- }
- if (mPaused) {
- if (mPauseTime < 0) {
- mPauseTime = frameTime;
- }
- return false;
- } else if (mResumed) {
- mResumed = false;
- if (mPauseTime > 0) {
- // Offset by the duration that the animation was paused
- mStartTime += (frameTime - mPauseTime);
- }
- }
- // 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);
- return animationFrame(currentTime);
- }
- 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);
- }
- if (mUpdateListeners != null) {
- int numListeners = mUpdateListeners.size();
- for (int i = 0; i < numListeners; ++i) {
- mUpdateListeners.get(i).onAnimationUpdate(this);
- }
- }
- }
get方法:在初始化的时候,如果属性的初始值没有提供,则get方法将会被调用。
- private void setupValue(Object target, Keyframe kf) {
- if (mProperty != null) {
- kf.setValue(mProperty.get(target));
- }
- try {
- if (mGetter == null) {
- Class targetClass = target.getClass();
- setupGetter(targetClass);
- if (mGetter == null) {
- // Already logged the error - just return to avoid NPE
- return;
- }
- }
- kf.setValue(mGetter.invoke(target));
- } catch (InvocationTargetException e) {
- Log.e("PropertyValuesHolder", e.toString());
- } catch (IllegalAccessException e) {
- Log.e("PropertyValuesHolder", e.toString());
- }
- }
set方法:当动画的下一帧到来的时候,PropertyValuesHolder中的setAnimatedValue方法会将新的属性值设置给对象,调用其set方法
- 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());
- }
- }
- }
总结
我觉得这篇源码分析写的逻辑有点混乱,希望不要给大家带来误导。从源码上来说,属性动画的源码逻辑层次有点跳跃,不过没关系,大家只要了解属性动画的工作原理就好,源码的作用在于让我们发现其工作原理的确如此。到此为止,Android动画系列已经全部完成,十分感谢大家阅读,希望能给大家带来一点帮助!