属性动画源码阅读

前言

属性动画是Android3.0之后开发的主流动画框架,开发者除了能够熟练使用它做动画效果之外,还需要近一步了解属性动画的实现原理,这里就来简单的分析一下属性动画的代码实现。

代码分析

补间动画的偏移缩放等操作都可以通过属性动画ObjectAnimator来实现,通常实现的代码如下所示:

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)

现在就从这个最简单的生成float类型属性动画的接口开始分析,看代码实现就是生成了一个ObjectAnimator对象并且为它设置了所有的float属性值。

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
    ObjectAnimator anim = new ObjectAnimator(target, propertyName);
    anim.setFloatValues(values);
    return anim;
}

接着查看ObjectAnimator的构造方法,构造方法中调用了setTarget和setPropertyName两个方法。

private ObjectAnimator(Object target, String propertyName) {
        setTarget(target);
        setPropertyName(propertyName);
    }

查看setTarget方法的实现源码发现ObjectAnimator有一个mTarget的弱引用对象,负责保持做动画对象的引用。

@Override
public void setTarget(@Nullable Object target) {
    final Object oldTarget = getTarget();
    if (oldTarget != target) {
        if (isStarted()) {
            cancel();
        }
        mTarget = target == null ? null : new WeakReference<Object>(target);
        // New target should cause re-initialization prior to starting
        mInitialized = false;
    }
}

接着查看setPropertyName方法的实现,这个时候mValues是空对象,需要到setValues调用才会做初始化,所以只是把propertyName存储到mPropertyName属性里。

public void setPropertyName(@NonNull String propertyName) {
    // mValues could be null if this is being constructed piecemeal. Just record the
    // propertyName to be used later when setValues() is called if so.
    if (mValues != null) {
        PropertyValuesHolder valuesHolder = mValues[0];
        String oldName = valuesHolder.getPropertyName();
        valuesHolder.setPropertyName(propertyName);
        mValuesMap.remove(oldName);
        mValuesMap.put(propertyName, valuesHolder);
    }
    mPropertyName = propertyName;
    // New property/values/target should cause re-initialization prior to starting
    mInitialized = false;
}

现在开始查询最后一句anim.setFloatValues(values)的实现源码,前面已经设置了mPropertyName而且mValues为空,所以走的是setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));这一句。

@Override
public void setFloatValues(float... values) {
    if (mValues == null || mValues.length == 0) {
        // No values yet - this animator is being constructed piecemeal. Init the values with
        // whatever the current propertyName is
        if (mProperty != null) {
            setValues(PropertyValuesHolder.ofFloat(mProperty, values));
        } else {
            setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
        }
    } else {
        super.setFloatValues(values);
    }
}

这个方法会把属性值名称和定义属性动画时的值信息一起传入到PropertyValuesHolder对象中生成一个新的Holder对象,这个对象可以保存一个属性和动画执行过程中该属性的中间值。查看PropertyValuesHolder对象的实现代码,发现ofFloat其实返回了PropertyValuesHolder的一个子内部类也就是FloatPropertyValuesHolder类的对象。

public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
    return new FloatPropertyValuesHolder(propertyName, values);
}

查看FloatPropertyValuesHolder类的构造方法,它首先记录了需要执行动画的属性,之后调用setFloatValues生成了KeyFrameSet对象,也就是关键帧集合对象。

// PropertyValuesHolder的构造方法
private PropertyValuesHolder(String propertyName) {
    mPropertyName = propertyName;
}

// FloatPropertyValuesHolder的构造方法
public FloatPropertyValuesHolder(String propertyName, float... values) {
    super(propertyName);
    setFloatValues(values);
}

// PropertyValuesHolder的setFloatValues
public void setFloatValues(float... values) {
    mValueType = float.class;
    mKeyframes = KeyframeSet.ofFloat(values);
}

// FloatPropertyValuesHolder的setFloatValues
@Override
public void setFloatValues(float... values) {
    super.setFloatValues(values);
    mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
}

查看KeyFrameSet对象的ofFloat实现源码,

public static KeyframeSet ofFloat(float... values) {
    boolean badValue = false;
    int numKeyframes = values.length;

    // FloatKeyFrame数组,根据提供的float值个数判断
    FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];

    // 如果用户只提供了一个float数值,那么生成开始和结束两个关键帧
    if (numKeyframes == 1) {
        keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
        keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
        if (Float.isNaN(values[0])) {
            badValue = true;
        }
    } else {
        // 否则生成多个关键帧
        keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
        for (int i = 1; i < numKeyframes; ++i) {
            keyframes[i] =
                    (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
            if (Float.isNaN(values[i])) {
                badValue = true;
            }
        }
    }
    if (badValue) {
        Log.w("Animator", "Bad value (NaN) in float animator");
    }
    return new FloatKeyframeSet(keyframes);
}

那么KeyFrame对象的实现又是怎么样的呢,查看其实现源码发现它主要记录了关键帧在动画中的位置比例,属性值和属性值类型,它们只是简单的数据保存类。

 static class FloatKeyframe extends Keyframe {
    // 记录当前值
    float mValue;

    FloatKeyframe(float fraction, float value) {
        mFraction = fraction;
        mValue = value;
        mValueType = float.class;
        mHasValue = true;
    }

    FloatKeyframe(float fraction) {
        mFraction = fraction;
        mValueType = float.class;
    }

    public float getFloatValue() {
        return mValue;
    }

    public Object getValue() {
        return mValue;
    }

    public void setValue(Object value) {
        if (value != null && value.getClass() == Float.class) {
            mValue = ((Float)value).floatValue();
            mHasValue = true;
        }
    }
}

而KeyFrameSet类也主要负责记录所有的关键帧数据,并且根据提供的动画进度值计算当前帧属性值数值。

public class KeyframeSet implements Keyframes {
    // 关键帧数据
    int mNumKeyframes;
    // 只在单一关键帧使用
    Keyframe mFirstKeyframe;
    Keyframe mLastKeyframe;
    TimeInterpolator mInterpolator; 

    // 只在多关键帧时使用
    List<Keyframe> mKeyframes;
    TypeEvaluator mEvaluator;

    public KeyframeSet(Keyframe... keyframes) {
        // 初始化主要的关键帧属性
        mNumKeyframes = keyframes.length;
        // immutable list
        mKeyframes = Arrays.asList(keyframes);
        mFirstKeyframe = keyframes[0];
        mLastKeyframe = keyframes[mNumKeyframes - 1];
        mInterpolator = mLastKeyframe.getInterpolator();
    }
    ....
}

综合上面的代码可知,用户在ObjectAnimator里设置的值会被作为关键帧保存在PropertyValuesHolder里面,以便后续的动画播放使用之前保存的所有关键帧。接着查看setValues方法的实现,可以看出一个ObjectAnimator可以包含多个PropertyValuesHolder也就实现了同时对多个属性做动画的能力。这里暂时只考虑对单一属性做动画的情况,mValuesMap保存了每个属性值对应的PropertyValuesHolder对象。

public void setValues(PropertyValuesHolder... values) {
    int numValues = values.length;
    mValues = values;
    mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
    for (int i = 0; i < numValues; ++i) {
        PropertyValuesHolder valuesHolder = values[i];
        mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
    }
    // New property/values/target should cause re-initialization prior to starting
    mInitialized = false;
}

以上就已经将属性动画需要的数据都准备好,在设置好duration之后调用start方法就能够执行动画效果了。

@Override
public void start() {
    // 清除以前正在播放的动画
    AnimationHandler.getInstance().autoCancelBasedOn(this);

    // 打印日志
    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));
        }
    }
    // 开始执行
    super.start();
}

会发现调用了super.start()方法来执行真正的动画操作。查看ObjectAnimator的实现代码可以发现它继承自ValueAnimator,查看ValueAnimator的实现发现它继承子Animator类。首先来分析一下Animator类的实现逻辑,查看源码发现它的start、stop等动画执行方法都是空实现,而它的内部包含了各种生命周期的回调对象列表,可见Animator主要做属性动画对象的各种基础数据和接口定义的任务。

// Animator类主要定义属性动画的各种实现接口和回调数据结构
public abstract class Animator implements Cloneable {

    public static final long DURATION_INFINITE = -1;
    ArrayList<AnimatorListener> mListeners = null;
    ArrayList<AnimatorPauseListener> mPauseListeners = null;
    boolean mPaused = false;

    public void start() {
    }

    public void cancel() {
    }

    public void pause() {
        if (isStarted() && !mPaused) {
            mPaused = true;
            if (mPauseListeners != null) {
                ArrayList<AnimatorPauseListener> tmpListeners =
                        (ArrayList<AnimatorPauseListener>) mPauseListeners.clone();
                int numListeners = tmpListeners.size();
                for (int i = 0; i < numListeners; ++i) {
                    tmpListeners.get(i).onAnimationPause(this);
                }
            }
        }
    }

  ....
}

接下来开始查看ValueAnimator的实现逻辑,start把调用转给了start(boolean)内部实现,mStartDelay代表第一次启动的延时,通常设置还都是0延时,start内部会调用startAnimation。mSeekFraction代表当前动画执行已经流逝的百分比,默认的名mSeekFraction值是-1,后面同样会执行setCurrentPlayTime(0)。

private void start(boolean playBackwards) {
    ....
    mLastFrameTime = -1;
    mFirstFrameTime = -1;
    mStartTime = -1;
    addAnimationCallback(0);
    ....
    if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
        startAnimation();
        if (mSeekFraction == -1) {
            setCurrentPlayTime(0);
        } else {
            setCurrentFraction(mSeekFraction);
        }
    }
}

startAnimation的实现中会调用initAnimation,这个时候开始每个mValues.init方法,也就是PropertyValuesHolder的init方法。

private void startAnimation() {
    mAnimationEndRequested = false;
    initAnimation();
    mRunning = true;
    if (mSeekFraction >= 0) {
        mOverallFraction = mSeekFraction;
    } else {
        mOverallFraction = 0f;
    }
    if (mListeners != null) {
        notifyStartListeners();
    }
}

void initAnimation() {
    if (!mInitialized) {
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            mValues[i].init();
        }
        mInitialized = true;
    }
}

也就是PropertyValuesHolder的init方法会为KeyFrameSet设置属性值估值器,用来根据动画执行百分比计算属性值。

void init() {
    if (mEvaluator == null) {
        mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
                (mValueType == Float.class) ? sFloatEvaluator :
                null;
    }
    if (mEvaluator != null) {
        mKeyframes.setEvaluator(mEvaluator);
    }
}

上面的initAnimation是ValueAnimator的实现,实际上ObjectAnimator对initAnimation做了覆盖操作,增加了将mTarget对象设置到PropertyValuesHolder里的操作。

// ObjectAnimator的initAnimation方法
void initAnimation() {
    if (!mInitialized) {
        final Object target = getTarget();
        if (target != null) {
            final int numValues = mValues.length;
            for (int i = 0; i < numValues; ++i) {
               // 将目标对象设置到PropertyValuesHolder里 mValues[i].setupSetterAndGetter(target);
            }
        }
        super.initAnimation();
    }
}

这里查看FloatPropertyValuesHolder的setupSetterAndGetter方法,实际上是把mTarget对象的mProperty属性的getter/setter方法保存到了PropertyValuesHolder中。

Method mSetter = null;
Method mGetter = null;

void setupSetterAndGetter(Object target) {
    Class targetClass = target.getClass();
    if (mSetter == null) {
        setupSetter(targetClass);
    }
    List<Keyframe> keyframes = mKeyframes.getKeyframes();
    int keyframeCount = keyframes == null ? 0 : keyframes.size();
    for (int i = 0; i < keyframeCount; i++) {
        Keyframe kf = keyframes.get(i);
        if (!kf.hasValue() || kf.valueWasSetOnStart()) {
            if (mGetter == null) {
                setupGetter(targetClass);
                if (mGetter == null) {
                    // Already logged the error - just return to avoid NPE
                    return;
                }
            }
        }
    }
    ....
}

void setupSetter(Class targetClass) {
    Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
    mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
}

private void setupGetter(Class targetClass) {
    mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
}

保存setter/getter其实就是用反射的方法得到属性的获取和设置方法,所以属性动画其实是使用反射最终改变了内部的属性值。

接着查看setCurrentPlayTime(0);的实现最终调用到了animateValue方法,它会根据流逝时间比例先用插值器计算动画运动到的比例,然后调用PropertyValuesHolder.calculate计算当前的属性值。

public void setCurrentPlayTime(long playTime) {
    float fraction = mDuration > 0 ? (float) playTime / mDuration : 1;
    setCurrentFraction(fraction);
}

public void setCurrentFraction(float fraction) {
    initAnimation();
    fraction = clampFraction(fraction);
    mStartTimeCommitted = true; // do not allow start time to be compensated for jank
    if (isPulsingInternal()) {
        long seekTime = (long) (getScaledDuration() * fraction);
        long currentTime = AnimationUtils.currentAnimationTimeMillis();
        mStartTime = currentTime - seekTime;
    } else {
        mSeekFraction = fraction;
    }
    mOverallFraction = fraction;
    final float currentIterationFraction = getCurrentIterationFraction(fraction, mReversing);
    animateValue(currentIterationFraction);
}

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

PropertyValuesHolder.calculate会根据动画执行的流式时间比值,上面的方法都是ValueAnimator的实现,实际上ObjectAnimator覆盖了animateValue方法,在计算完属性值之后还会调用PropertyValuesHolder的setAnimatedValue方法。

@Override
void animateValue(float fraction) {
    final Object target = getTarget();
    if (mTarget != null && target == null) {
        // We lost the target reference, cancel and clean up. Note: we allow null target if the
        /// target has never been set.
        cancel();
        return;
    }

    super.animateValue(fraction);
    int numValues = mValues.length;
    for (int i = 0; i < numValues; ++i) {
        mValues[i].setAnimatedValue(target);
    }
}

查看PropertyValuesHolder.setAnimatedValue方法实现,可以看到正在使用mSetter方法来修改target的属性值。

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

前面的分析已经把动画的第一帧展示出来了,接下来该如何继续动画后面执行呢,其实在start(boolean)方法里有一个addAnimationCallback(0)方法的调用。

private void start(boolean playBackwards) {
    ....
    mLastFrameTime = -1;
    mFirstFrameTime = -1;
    mStartTime = -1;

    // 添加后续回调操作
    addAnimationCallback(0);
    ....
    if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
        startAnimation();
        if (mSeekFraction == -1) {
            setCurrentPlayTime(0);
        } else {
            setCurrentFraction(mSeekFraction);
        }
    }
}

查看addAnimationCallback(0)的源码会发现它会发送一个回调到AnimatorHandler对象中,这个回调的方法就是doAnimationFrame方法。查看Provider的源码会发现那些post方法都会被委托给Choreographer对象,Choreographer内部就是使用Looper和Handler实现的,感兴趣的可以自己翻阅它的实现代码。

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

public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
    if (mAnimationCallbacks.size() == 0) {
        // 在这里提交了一个mFrameCallback回调
        getProvider().postFrameCallback(mFrameCallback);
    }
    if (!mAnimationCallbacks.contains(callback)) {
        mAnimationCallbacks.add(callback);
    }

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

// mFrameCallback里实现了递归的调用doAnimationFrame
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
    @Override
    public void doFrame(long frameTimeNanos) {
        doAnimationFrame(getProvider().getFrameTime());

        // 递归实现调用doAnimationFrame
        if (mAnimationCallbacks.size() > 0) {
            getProvider().postFrameCallback(this);
        }
    }
};

doAnimationFrame方法会根据当前的时间来决定是否继续执行animateBasedOnTime方法,这个事件就是当前动画已经执行的时间。

public final boolean doAnimationFrame(long frameTime) {
    ....
    final long currentTime = Math.max(frameTime, mStartTime);
    boolean finished = animateBasedOnTime(currentTime);

    if (finished) {
        endAnimation();
    }
    return finished;
}

boolean animateBasedOnTime(long currentTime) {
    boolean done = false;
    if (mRunning) {
        ....
        float currentIterationFraction = getCurrentIterationFraction(
                mOverallFraction, mReversing);
        animateValue(currentIterationFraction);
    }
    return done;
}

在animateBasedOnTime里又调用了animateValue方法不断的生成之后的属性值,animateValue之后的操作和第一帧的相同这里不再做分析。

总结

以上的代码执行流程就是整个属性代码的基本执行过程,整个过程可以大致总结如下:ObjectAnimator会将用户设置的targetView和values放到PropertyValuesHolder对象中管理,PropertyValuesHolder对象又会把所有的values封装到KeyFrameSet中,而每一个value正好对应一个KeyFrame关键帧对象;用户调用ObjectAnimatior.start方法后会将targetView的setter/getter方法也设置到PropertyValuesHolder对象中,之后数值的动画过程由ValueAnimator来实现,而ValueAnimator主要负责计算当前动画流逝的时间比例,在调用用户设置的插值器计算动画执行到的比例,PropertyValuesHolder通过动画执行比例和关键帧中的数据结合估值器计算出当前属性值,最后通过反射调用targetView的属性设置方法实现动画效果。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值