属性动画的工作原理

来源:互联网 发布:网络交友网站 编辑:程序博客网 时间:2024/05/01 12:40

属性动画的工作原理

以ObjectAnimator.ofInt(button,”width”,500).setDuration(5000).start()为例。

ObjectAnimator#start()

@Overridepublic 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();                }            }        }    }    //Log    if (DBG) {        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));        }    }    //调用父类的start    super.start();}  

ObjectAnimator继承了ValueAnimator,我们再来看super.start();

ValueAnimator#start()

private void start(boolean playBackwards) {    if (Looper.myLooper() == null) { //表明需要运行在有Looper的线程中        throw new AndroidRuntimeException("Animators may only be run on Looper threads");    }    mPlayingBackwards = playBackwards;    mCurrentIteration = 0;    mPlayingState = STOPPED;    mStarted = true;    mStartedDelay = false;    mPaused = false;    updateScaledDuration(); // in case the scale factor has changed since creation time    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是一个Runnable,调用runnable.start();    //经过一系列的方法    // -> 调用DisplayEventReceiver#nativeScheduleVsync() [native方法]    // -> 调用ValueAnimator.doAnimationFrame();    animationHandler.start();}  

最终将调用ValueAnimator.doAnimationFrame();

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);    //[重点方法] -> 在此方法中调用animateValue()    return animationFrame(currentTime);}  

在animationFrame中将调用animateValue()

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

mValues是一个PropertyValuesHolder数组,我们再来看PropertyValuesHolder

在初始化的时候,如果属性的初始值没有提供,则get方法将会被调用 [通过反射进行调用]

//初始化属性 private void setupValue(Object target, Keyframe kf) {    if (mProperty != null) {        Object value = convertBack(mProperty.get(target));        kf.setValue(value);    }    try {        if (mGetter == null) {            Class targetClass = target.getClass();            setupGetter(targetClass);            if (mGetter == null) {                // Already logged the error - just return to avoid NPE                return;            }        }        //通过反射进行调用        Object value = convertBack(mGetter.invoke(target));        kf.setValue(value);    } catch (InvocationTargetException e) {        Log.e("PropertyValuesHolder", e.toString());    } catch (IllegalAccessException e) {        Log.e("PropertyValuesHolder", e.toString());    }}

当下一帧到来的时候,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());        }    }}  

重点

  1. 属性动画最终会调用ValueAnimator来进行处理
  2. 动画的本质是通过改变坐标和属性,快速刷新来形成动画的错觉
  3. 属性值的设置通过反射来进行
0 0