Android Property Animation 介绍(四)

原创
2014/11/04 15:02
阅读数 11

这篇文章想要从源码的角度来介绍下Property Animation。先来看下相关类的继承结构

Animator是一个抽象类,他提供了基础方法 start、cancel、end、pause、resume等,还定义了2个监听器,AnimatorListener和AnimatorPauseListener。Animator类没有具体动画逻辑的实现,具体实现由子类来完成

AnimatorSet定义了一组动画的实现逻辑,这个在之后的文章中再详细介绍。下面主要介绍下ValueAnimator类。

通过类的名称就可以知道这个类是显示对某个值的动画变化。其实现动画的原理有以下几个步骤:

1、动画的对象初始化

2、启动动画后,会获取一个AnimationHandler,不断的轮询来计算value在这一时刻的值

3、计算value值由2个步骤,先通过interpolator算出一个interpolated fraction。

4、在通过TypeEvaluator来计算出真实的属性值

5、真实的值计算出来后再调用监听器updateListeners通知更新属性。

下面来具体介绍下每个步骤

1、动画对象的初始化

ValueAnimator对象提供了很多种的方法来初始化动画的相关数据:ofInt、ofArgb、ofFalot、ofPropertyValueHolder、ofObject。

初始化主要是设置了动画关键的帧对应的value值。以ofInt方法为例:

  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));
        } else {
            PropertyValuesHolder valuesHolder = mValues[0];
            valuesHolder.setIntValues(values);
        }
        // New property/values/target should cause re-initialization prior to starting
        mInitialized = false;
    }

要设置的value值实际上是要存储在PropertyValuesHolder中。在来看看PropertyValueHolder中的实现:

 public void setIntValues(int... values) {
        mValueType = int.class;
        mKeyframes = KeyframeSet.ofInt(values);
    }

public static KeyframeSet ofInt(int... values) {
        int numKeyframes = values.length;
        IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];
        if (numKeyframes == 1) {
            keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
            keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
        } else {
            keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);
            for (int i = 1; i < numKeyframes; ++i) {
                keyframes[i] =
                        (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
            }
        }
        return new IntKeyframeSet(keyframes);
    }

可以看到PropertyValueHolder存储了value的类型valueType为int.class和KeyframeSet,value值真正是存在keyframeSet中的,也就是一个帧对象的集合。先来了解下帧对象keyframe

public abstract class Keyframe implements Cloneable {
   
    boolean mHasValue;
    boolean mValueWasSetOnStart;
    float mFraction;
    Class mValueType;
    private TimeInterpolator mInterpolator = null;

Keyframe是一个抽象类,定义了几个参数

mHasValue:是否有值

mValueWasSetOnStart:onstart时是否设置了值

mFraction:该帧对应时间片段(0-1的一个数)

mValueType:值的类型

mInterpolation:插值器

他的子类有IntKeyframe、FloatKeyframe、ObjectKetframe。子类并没有什么而外的实现,只是确定了具体值的类型。

再来看下KeyframeSet中的ofInt方法。帧集合最少2帧,每一帧的对应的时间片段是0-1等比例分的一个值。

这样初始化的时候就把关键的一些帧对应的值给设置的,通常是设置第一帧和最后一帧。

ofArgb和ofObject方法除了设置value值外还设置了TypeEvaluator。

2、启动动画

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

通过以上代码可以看到启动动画的时候除了做相关属性的初始化,还有2个关键点获取AnimationHandler和AnimationHandler的启动

1、AnimationHandler的获取

private static AnimationHandler getOrCreateAnimationHandler() {
        AnimationHandler handler = sAnimationHandler.get();
        if (handler == null) {
            handler = new AnimationHandler();
            sAnimationHandler.set(handler);
        }
        return handler;
    }

    可以看到是通过sAnimationHandler获取的AnimationHandler,sAnimationHandler是一个ThreadLocal<AnimationHandler>对象,意味着同一个线程中共享一个animationHandler对象。

2、AnimationHandler的启动

 public void start() {
            scheduleAnimation();
        }

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

可以看到是通过choreographer的回掉来执行AnimationHandler的run方法,关于choreographer这个类比较复杂了,是android用于绘制界面的消息处理器,想了解的可以看下这篇文章

http://blog.csdn.net/farmer_cc/article/details/18619429

3、动画绘制

在AnimationHandler中定义了多种动画列表我们先来了解下

mPendingAnimations:下一帧要启动的动画,这类的动画在调用启动方法时加入

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

mAnimations:正在执行的动画,在动画真正开始执行时加入

private void startAnimation(AnimationHandler handler) {
        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
            Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
                    System.identityHashCode(this));
        }
        initAnimation();
        handler.mAnimations.add(this);
        if (mStartDelay > 0 && mListeners != null) {
            // Listeners were already notified in start() if startDelay is 0; this is
            // just for delayed animations
            notifyStartListeners();
        }
    }

mDelayAnims、EndingAnims、mReadyAnims:分别是延迟的动画、结束的动画、准备中的动画

延迟动画是指还未到延迟时间的动画,准备中的动画是指已经超过延迟时间的动画,结束动画是指动画已结束的动画。

具体的类型的动画通过以下的代码来区分

private void doAnimationFrame(long 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<ValueAnimator> pendingCopy =
                        (ArrayList<ValueAnimator>) 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);
                    } 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();
            }

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

只有延迟时间为0的或是准备中的动画才会执行动画的startAnimation方法启动动画,如果还有未处理的动画还会执行scheduleAnimation()方法重新调用doAnimationFrame()方法来判断。

通过调用anim.doAnimationFrame(frameTime)方法来计算这个时刻的属性值

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

void calculateValue(float fraction) {
        Object value = mKeyframes.getValue(fraction);
        mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
    }

先通过Interpolation获取相应的值,再mKeyframe的getValue方法获取通过TypeEvaluator计算出属性的值,

并通过updateListener通知动画更新。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部