Android属性动画(Animator)是如何实现的

        与补间动画不同,属性动画是对对象的属性进行修改的,主要涉及到ValueAnimator和ObjectAnimator,其中ObjectAnimator又是继承自ValueAnimator,所以这里从ValueAnimator入手,首先来看一下ValueAnimator的简单用法:

private void testValueAnimator(){
    ValueAnimator valueAnimator = ValueAnimator.ofFloat(0,100);
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float animatedFraction = animation.getAnimatedFraction();
            float value = (float) animation.getAnimatedValue();
            Log.d(TAG, "onAnimationUpdate: = "+animatedFraction+"    value = "+value);
        }
    });
    valueAnimator.start();
}

再来看下输出内容:

06-07 11:37:33.377 29237-29237/? D/MainActivity: onAnimationUpdate: = 0.0    value = 0.0
06-07 11:37:33.402 29237-29237/? D/MainActivity: onAnimationUpdate: = 0.0    value = 0.0
06-07 11:37:33.444 29237-29237/? D/MainActivity: onAnimationUpdate: = 0.031359017    value = 3.1359017
06-07 11:37:33.526 29237-29237/? D/MainActivity: onAnimationUpdate: = 0.33063102    value = 33.063103
06-07 11:37:33.535 29237-29237/? D/MainActivity: onAnimationUpdate: = 0.42178285    value = 42.178284
06-07 11:37:33.551 29237-29237/? D/MainActivity: onAnimationUpdate: = 0.5052359    value = 50.52359
06-07 11:37:33.568 29237-29237/? D/MainActivity: onAnimationUpdate: = 0.5936906    value = 59.369057
06-07 11:37:33.585 29237-29237/? D/MainActivity: onAnimationUpdate: = 0.67918396    value = 67.918396
06-07 11:37:33.602 29237-29237/? D/MainActivity: onAnimationUpdate: = 0.7590136    value = 75.90136
06-07 11:37:33.618 29237-29237/? D/MainActivity: onAnimationUpdate: = 0.82671034    value = 82.671036
06-07 11:37:33.635 29237-29237/? D/MainActivity: onAnimationUpdate: = 0.88857293    value = 88.85729
06-07 11:37:33.652 29237-29237/? D/MainActivity: onAnimationUpdate: = 0.93815327    value = 93.81532
06-07 11:37:33.668 29237-29237/? D/MainActivity: onAnimationUpdate: = 0.9738842    value = 97.38842
06-07 11:37:33.685 29237-29237/? D/MainActivity: onAnimationUpdate: = 0.99384415    value = 99.384415
06-07 11:37:33.702 29237-29237/? D/MainActivity: onAnimationUpdate: = 1.0    value = 100.0

有没有发现,后面的值就是前面的值乘以100,这个100就是我们前面设置的。那这个内部是怎么实现的呢?那这里我们就从start()这个方法开始入手:

@Override
public void start() {
    start(false);
}
private void start(boolean playBackwards) {
    //这里检查是否是在主线程中开启的动画
    if (Looper.myLooper() == null) {
        throw new AndroidRuntimeException("Animators may only be run on Looper threads");
    }
    ......
    //看意思应该是动画的回调
    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);
        }
    }
}

这里先执行了一个动画的回调addAnimatorCallback(0),这个回调里具体做了些什么呢?进去看看:

private void addAnimationCallback(long delay) {
    if (!mSelfPulse) {
        return;
    }
    getAnimationHandler().addAnimationFrameCallback(this, delay);
}
public AnimationHandler getAnimationHandler() {
    return AnimationHandler.getInstance();
}

这里是先获取到了一个单列对象AnimationHandler,看字面上的意思应该是处理动画的,而addAnimationFrameCallback()从方法名上猜测应该是每一次帧动画执行的时候回调用到,这里回调的接口是AnimationHandler.AnimationFrameCallback,ValueAnimation本身是实现了这个接口的,所以这里传进去的是ValuesAnimator本身,我们先来看看这个接口的两个方法:

interface AnimationFrameCallback {
    //这里是动画的执行逻辑实现
    boolean doAnimationFrame(long frameTime);
    void commitAnimationFrame(long frameTime);
}

这里有一个概念需要先了解,Android的屏幕刷新是每隔16ms,当这个时间到来时,可以看做是动画执行了一帧,不过这个信号并不是每次都会传递到应用层,而是要我们去系统层注册,每注册一次,下一次的屏幕刷新就会传递到这个应用。现在再来说说这个接口的作用,doAnimationFrame()主要就是计算一帧动画所需要的参数,当屏幕刷新信号到来时可以执行一帧动画。

    弄清这个接口后,还是跟着主线继续往下看,现在该是到AnimationHandle的addAnimationFrameCallback():

public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
    // 这里是一个单列对象,mAnimationCallBacks为0意味着没有动画在执行的时候,这时候就需要我们去
    // 系统层注册,下次屏幕刷新时就会调用到mFrameCallbak
    if (mAnimationCallbacks.size() == 0) {
        getProvider().postFrameCallback(mFrameCallback);
    }
    // 这里是判断集合中是否含有这个回调,如果含有就不会再次添加进去,也就是说这个动画还在执行就
    // 不会添加进去,不过如果在这个动画还在执行的时候再次开启这个动画,那么这个动画会从初始状态
    // 再次开始执行
    if (!mAnimationCallbacks.contains(callback)) {
        mAnimationCallbacks.add(callback);
    }

    if (delay > 0) {
        mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
    }
}

这里就不去看屏幕刷新时是怎么回调到这里的,接下来要看的是屏幕刷新时回调到的这个对象mFrameCallback:

private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
    @Override
    public void doFrame(long frameTimeNanos) {
        doAnimationFrame(getProvider().getFrameTime());
        // mAnimationCallbacks不为0的时候说明还有动画没执行完,就会再去底层注册请求下次屏幕刷新时
        // 回调到这里,直到不再有动画执行
        if (mAnimationCallbacks.size() > 0) {
            getProvider().postFrameCallback(this);
        }
    }
};

doAnimationFrame()是执行一帧动画,如果动画没执行完成,那就再去注册屏幕刷新信号,所以这里主要看的就是doAnimationFrame():

private void doAnimationFrame(long frameTime) {
    long currentTime = SystemClock.uptimeMillis();
    final int size = mAnimationCallbacks.size();
    for (int i = 0; i < size; i++) {
        //当动画执行完之后,会将对应的动画回调接口置为null
        final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
        if (callback == null) {
            continue;
        }
        //这里就是判断当前时间是否可以开始执行动画了,有些动画的执行是有延迟时间的
        if (isCallbackDue(callback, currentTime)) {
            //这里的这个callback就是从ValueAnimator中传进来的
            callback.doAnimationFrame(frameTime);
            ......
        }
    }
    //这里就是清除执行完的动画的回调
    cleanUpList();
}

在我们进入到AnimationHandler的时候传进来一个回调接口,这里就用到了这个传进来的回调接口,所以这里callback.doAnimationFrame()实际就又回到了ValueAnimator,到这,就可以总结一下AnimationHandler这个类的作用了:

    1、会收集应用层所有注册动画的回调;

    2、有去底层注册屏幕刷新信号的功能,每注册一次,下一次屏幕刷新就会回调到这里;

    3、当屏幕信号到来时,会执行所收集的动画回调,如果动画没执行完,就会再次去注册屏幕刷新信号。

接下来就该看看回调里到底做了些什么,看ValueAnimator的doAnimationFrame()方法:

public final boolean doAnimationFrame(long frameTime) {
    ......

    // 主要就是根据是否需要延迟执行动画还有一些其他的条件来确定mRunning的值,我这里的理解就是
    // 是否执行了startAnimation()这个方法,执行了mRunning就是true,没有执行就是false
    if (!mRunning) {
        if (mStartTime > frameTime && mSeekFraction == -1) {
            return false;
        } else {
            mRunning = true;
            startAnimation();
        }
    }
    ......

    final long currentTime = Math.max(frameTime, mStartTime);
    //这里就是根据当前时间来计算动画执行所需要的相关值以及确定动画是否执行完成
    boolean finished = animateBasedOnTime(currentTime);

    if (finished) {
        //1、动画执行完了就会执行到这里,在这个方法里主要是对一些变量复位
        //2、动画执行完后的回调
        //3、置null在AnimationHandler中添加的回调
        endAnimation();
    }
    return finished;
}

这里分三步来看:

    1、动画是否初始化了,startAnimation()就是对动画进行初始化;

    2、计算动画执行的相关数据;

    3、动画是否执行完了

      这里先看下startAnimation(),这个方法在一开始调用start()时,里面就有判断这个方法是否需要执行,正好这里就一起看下:

private void startAnimation() {
    ......
    
    mAnimationEndRequested = false;
    //初始化动画相关数据
    initAnimation();
    //这里就对mRunning赋值为true
    mRunning = true;
    ......
}
void initAnimation() {
    if (!mInitialized) {
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            //创建ValueAnimator的时候传进来的数值就是在这里初始化
            mValues[i].init();
        }
        mInitialized = true;
    }
}

主要是初始化动画相关的数据,而且看到这里对mRunning赋值为true。

    动画相关的计算比较赋值,这里就放到最后来看,先来看下动画结束时执行的逻辑endAnimation():

private void endAnimation() {
    if (mAnimationEndRequested) {
        return;
    }
    //这里就是将添加到AnimationHandler中的回调置null
    removeAnimationCallback();
    ......
    
    if (notify && mListeners != null) {
        ArrayList<AnimatorListener> tmpListeners =
                (ArrayList<AnimatorListener>) mListeners.clone();
        int numListeners = tmpListeners.size();
        for (int i = 0; i < numListeners; ++i) {
            //如果添加了动画执行完成的回调,那在这里会执行
            tmpListeners.get(i).onAnimationEnd(this, mReversing);
        }
    }
    ......
}
private void removeAnimationCallback() {
    if (!mSelfPulse) {
        return;
    }
    getAnimationHandler().removeCallback(this);
}
public void removeCallback(AnimationFrameCallback callback) {
    mCommitCallbacks.remove(callback);
    mDelayedCallbackStartTime.remove(callback);
    int id = mAnimationCallbacks.indexOf(callback);
    if (id >= 0) {
        mAnimationCallbacks.set(id, null);
        mListDirty = true;
    }
}

这里贴出的主要是动画执行完后,AnimationHandler中添加的回调是如何置null的,整个流程还是比较简单的,还记得在AnimationHandler中doAnimationFrame()个方法中就有对回调事件的判null,如果为null就会在集合中清掉。

    接下来就是动画执行数据的分析了,这里就来到了animateBaseOnTime()这个方法了:

boolean animateBasedOnTime(long currentTime) {
    boolean done = false;
    //动画初始化后这里mRunning为true
    if (mRunning) {
        ......
        // 前面省略的代码主要就是在计算currentIterationFraction这个值
        // 这个值是根据时间来计算的,很重要,后面具体值的计算就是根据
        // 这个来计算的,这个值的取值范围是在0和1之间
        animateValue(currentIterationFraction);
    }
    //动画执行完这里放回true,没执行完返回false
    return done;
}
void animateValue(float fraction) {
    //mInterpolation是插值器,如果没有设置就会使用默认的
    fraction = mInterpolator.getInterpolation(fraction);
    mCurrentFraction = fraction;
    int numValues = mValues.length;
    for (int i = 0; i < numValues; ++i) {
        //mValues中存储的就是创建ValueAnimator时传进来的数值,
        mValues[i].calculateValue(fraction);
    }
    if (mUpdateListeners != null) {
        int numListeners = mUpdateListeners.size();
        for (int i = 0; i < numListeners; ++i) {
            //这里就是数据更新的回调
            mUpdateListeners.get(i).onAnimationUpdate(this);
        }
    }
}

看到这里,一开始给出的使用中打印出的结果就可以找到答案了。

这里在看下打印结果的两个方法:

public float getAnimatedFraction() {
    return mCurrentFraction;
}
public Object getAnimatedValue() {
    if (mValues != null && mValues.length > 0) {
        return mValues[0].getAnimatedValue();
    }
    return null;
}
相关值和值的计算在上面都可以找到,在这里就不在跟进去看了,整个流程就差不多走了一遍了。这里只是分析了整个流程,但并没有涉及到对对象属性值的修改,如果要对对象的值进行修改,那就的去看看ObjectAnimator了,这篇就不在分析了,下篇再来。如果有疑问,欢迎留言。



猜你喜欢

转载自blog.csdn.net/tangedegushi/article/details/80607041