美文网首页工作生活
Android源码学习-属性动画源码浅析

Android源码学习-属性动画源码浅析

作者: m1Ku | 来源:发表于2019-06-30 16:40 被阅读0次

介绍

安卓的动画有:View动画,帧动画和属性动画。其中View动画只是让View的影像发生变化,并不能真的改变View的属性,例如平移动画后的View并不能响应点击事件,而且也只有平移、缩放、旋转和透明度四种动画。帧动画是按照顺序播放一组定义好的图片,这种动画比较简单,但是如果图片过大会引起OOM。属性动画能作用于任何对象,而且能真正的改变对象的属性,使用简单且灵活,它对应于ValueAnimator和ObjectAnimator,ObjectAnimator继承自ValueAnimator,ValueAnimator可以不指定对象,ObjectAnimator需要指定动画作用的对象。

以ObjectAnimator为例,下面代码让一个Button在3秒内沿X方向平移200像素

ObjectAnimator animator = ObjectAnimator
                .ofFloat(btn, "translationX", 200)
                .setDuration(3000);
        animator.setInterpolator(new DecelerateInterpolator());
        animator.start();

源码分析

下面进入源码,分析下属性动画的原理

ObjectAnimator.ofFloat方法

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

这里首先初始化了ObjectAnimator对象,并记录了动画作用的目标对象和属性

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

在初始化ObjectAnimator后,又调用了其setFloatValues方法

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

该方法的入参是可变参数,是我们在ofFloat方法中传入的参数。上例中只传了一个参数,它会作为可变参数的最后一项,如果未指定起始值,程序会获取该属性的起始值。

这里mProperty为空,走到else分支。分支中调用PropertyValuesHolder.ofFloat方法构建FloatPropertyValuesHolder对象,并将其传入到setValues方法中。PropertyValuesHolder对象是对属性以及该属性要变化的值的封装,这些值的类型为Float时对应着FloatPropertyValuesHolder对象。

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

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

下面看对values的处理,调用父类PropertyValuesHolder的setFloatValues

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

调用KeyframeSet.ofFloat

public static KeyframeSet ofFloat(float... values) {
    boolean badValue = false;
    int numKeyframes = values.length;
    FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
    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,其名为关键帧。它是一个抽象类,可以通过工厂方法得到特定类型的实现类,这里是FloatKeyframe。这个对象包装了动画的时间以及对应的值,该时间是当前动画执行时间相对于动画总时间的进度比例。这里会初始化一个最小长度为2的Keyframe数组,然后判断传入的values长度为1的话,会构造一个Keyframe对象赋值给数组第一个元素。如果values长度大于1的话,遍历构造Keyframe对象并赋值给数组。最后使用Keyframe数组构造FloatKeyframeSet对象返回。

FloatPropertyValuesHolder对象构建完成,继续回到ObjectAnimator中将其传入setValues方法

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

这里遍历将属性名和其对应的PropertyValuesHolder添加到一个HashMap中管理。

setDuration方法

@NonNull
public ObjectAnimator setDuration(long duration) {
    super.setDuration(duration);
    return this;
}
@Override
 public ValueAnimator setDuration(long duration) {
     if (duration < 0) {
          throw new IllegalArgumentException("Animators cannot have negative duration: " +
                  duration);
      }
      mDuration = duration;
      return this;
 }

这个方法就是设置到动画的执行时长,为mDuration属性赋值。

setInterpolator方法

@Override
public void setInterpolator(TimeInterpolator value) {
    if (value != null) {
        mInterpolator = value;
    } else {
        mInterpolator = new LinearInterpolator();
    }
}

这里采用策略设计模式,将差值器从外界注入,默认为加速减速差值器。差值器决定了属性值随时间的变化情况,比如是加速还是加速等,在计算属性值时用到。

start方法

@Override
public void start() {
    AnimationHandler.getInstance().autoCancelBasedOn(this);
    //..省略log代码
    super.start();
}

第一句代码是根据mAutoCancel标志位以及当前动画和前面的动画是否有相同的对象和属性,如果标志位设为true并且后面条件满足的话,就会自动取消动画。

调用父类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");
    }
    mReversing = playBackwards;
    mSelfPulse = !mSuppressSelfPulseRequested;
    //...
    addAnimationCallback(0);

    if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
        startAnimation();
        if (mSeekFraction == -1) {
            setCurrentPlayTime(0);
        } else {
            setCurrentFraction(mSeekFraction);
        }
    }
}

调用addAnimationCallback添加动画回调

private void addAnimationCallback(long delay) {
    if (!mSelfPulse) {
        return;
    }
    getAnimationHandler().addAnimationFrameCallback(this, delay);
}
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
    if (mAnimationCallbacks.size() == 0) {
        getProvider().postFrameCallback(mFrameCallback);
    }
    if (!mAnimationCallbacks.contains(callback)) {
        mAnimationCallbacks.add(callback);
    }
  //...
}

判断mAnimationCallbacks集合size为0即属性动画第一次启动时,这里会调用mChoreographer的postFrameCallback方法添加VSYNC的刷新信号回调,然后再添加动画的AnimationFrameCallback回调。

private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
    @Override
    public void doFrame(long frameTimeNanos) {
        doAnimationFrame(getProvider().getFrameTime());
        if (mAnimationCallbacks.size() > 0) {
            getProvider().postFrameCallback(this);
        }
    }
};

这个就是接收到垂直信号的回调,判断当前还存在动画回调的话,再将这个mFrameCallback回调添加到mChoreographer以继续进行垂直信号的回调。这里doAnimationFrame方法的逻辑留在后面分析,我们继续看start方法接下来的逻辑。

没有设置延迟,延迟时间为0,直接调用startAnimation方法

private void startAnimation() {
    //...省略log代码
    mAnimationEndRequested = false;
    initAnimation();
    mRunning = true;
    if (mSeekFraction >= 0) {
        mOverallFraction = mSeekFraction;
    } else {
        mOverallFraction = 0f;
    }
    if (mListeners != null) {
        notifyStartListeners();
    }
}

调用initAnimation方法,并且回调外部设置的listener的onAnimationStart方法。

@CallSuper
@Override
void initAnimation() {
    if (!mInitialized) {
        final Object target = getTarget();
        if (target != null) {
            final int numValues = mValues.length;
            for (int i = 0; i < numValues; ++i) {
                mValues[i].setupSetterAndGetter(target);
            }
        }
        super.initAnimation();
    }
}

遍历mValues数组,调用PropertyValuesHolder的setupSetterAndGetter方法

void setupSetterAndGetter(Object target) {
    //...省略mProperty != null的分支
    // We can't just say 'else' here because the catch statement sets mProperty to null.
    if (mProperty == null) {
        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;
                    }
                }
                try {
                    Object value = convertBack(mGetter.invoke(target));
                    kf.setValue(value);
                    kf.setValueWasSetOnStart(true);
                } catch (InvocationTargetException e) {
                    Log.e("PropertyValuesHolder", e.toString());
                } catch (IllegalAccessException e) {
                    Log.e("PropertyValuesHolder", e.toString());
                }
            }
        }
    }
}

如果mSetter或者mGetter为空的话,就分别调用setupSetter和setupGetter通过反射获取到对应的方法。初始化get方法后,调用目标对象的get方法获取到属性的初始值并转化为set方法需要的类型,赋值给KeyFrame。

调用父类ValueAnimator的initAnimation

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

遍历mValues数组为PropertyValuesHolder的KeyFrameSet设置估值器对象。看完了startAnimation的逻辑,我们继续回到start方法中,接下来执行setCurrentPlayTime

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

方法的最后调用了animateValue方法

 @CallSuper
    @Override
    void animateValue(float fraction) {
        final Object target = getTarget();
        if (mTarget != null && target == null) {
            cancel();
            return;
        }
        super.animateValue(fraction);
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            mValues[i].setAnimatedValue(target);
        }
    }

调用父类的animateValue

@CallSuper
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数组并调用calculateValue方法,最后调用动画更新回调方法。

@Override
void calculateValue(float fraction) {
    mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
}

调用FloatKeyframeSet的getFloatValue方法

@Override
public float getFloatValue(float fraction) {
    if (fraction <= 0f) {
        final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
        final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(1);
        float prevValue = prevKeyframe.getFloatValue();
        float nextValue = nextKeyframe.getFloatValue();
        float prevFraction = prevKeyframe.getFraction();
        float nextFraction = nextKeyframe.getFraction();
        final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
        if (interpolator != null) {
            fraction = interpolator.getInterpolation(fraction);
        }
        float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
        return mEvaluator == null ?
                prevValue + intervalFraction * (nextValue - prevValue) :
                ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
                        floatValue();
    } else if (fraction >= 1f) {
        final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 2);
        final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 1);
        float prevValue = prevKeyframe.getFloatValue();
        float nextValue = nextKeyframe.getFloatValue();
        float prevFraction = prevKeyframe.getFraction();
        float nextFraction = nextKeyframe.getFraction();
        final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
        if (interpolator != null) {
            fraction = interpolator.getInterpolation(fraction);
        }
        float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
        return mEvaluator == null ?
                prevValue + intervalFraction * (nextValue - prevValue) :
                ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
                        floatValue();
    }
    FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
    for (int i = 1; i < mNumKeyframes; ++i) {
        FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(i);
        if (fraction < nextKeyframe.getFraction()) {
            final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
            float intervalFraction = (fraction - prevKeyframe.getFraction()) /
                (nextKeyframe.getFraction() - prevKeyframe.getFraction());
            float prevValue = prevKeyframe.getFloatValue();
            float nextValue = nextKeyframe.getFloatValue();
            // Apply interpolator on the proportional duration.
            if (interpolator != null) {
                intervalFraction = interpolator.getInterpolation(intervalFraction);
            }
            return mEvaluator == null ?
                    prevValue + intervalFraction * (nextValue - prevValue) :
                    ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
                        floatValue();
        }
        prevKeyframe = nextKeyframe;
    }
    // shouldn't get here
    return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).floatValue();
}

在这里会计算动画过程中的属性值,首先根据时间流逝的比例通过差值器计算属性变化的比例,然后再通过估值器计算出当前的属性值返回。

得到属性值后再回到ObjectAnimator的animateValue方法,遍历mValues并调用setAnimatedValue方法

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

最后调用目标对象的set方法将为目标对象的属性进行了赋值,这样就完成了目标对象属性值的改变。

前面还有个方法没分析,在接收到垂直信号回调时调用了doAnimationFrame方法

private void doAnimationFrame(long frameTime) {
    long currentTime = SystemClock.uptimeMillis();
    final int size = mAnimationCallbacks.size();
    for (int i = 0; i < size; i++) {
        final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
        if (callback == null) {
            continue;
        }
        if (isCallbackDue(callback, currentTime)) {
            callback.doAnimationFrame(frameTime);
            if (mCommitCallbacks.contains(callback)) {
                getProvider().postCommitCallback(new Runnable() {
                    @Override
                    public void run() {
                        commitAnimationFrame(callback, getProvider().getFrameTime());
                    }
                });
            }
        }
    }
    cleanUpList();
}

遍历动画回调,调用了回调的doAnimationFrame方法,该方法在ValueAnimator中实现

public final boolean doAnimationFrame(long frameTime) {
    //...省略代码
    final long currentTime = Math.max(frameTime, mStartTime);
    boolean finished = animateBasedOnTime(currentTime);

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

调用animateBasedOnTime方法

boolean animateBasedOnTime(long currentTime) {
    boolean done = false;
    if (mRunning) {
        final long scaledDuration = getScaledDuration();
        final float fraction = scaledDuration > 0 ?
                (float)(currentTime - mStartTime) / scaledDuration : 1f;
        final float lastFraction = mOverallFraction;
        final boolean newIteration = (int) fraction > (int) lastFraction;
        final boolean lastIterationFinished = (fraction >= mRepeatCount + 1) &&
                (mRepeatCount != INFINITE);
        if (scaledDuration == 0) {
            // 0 duration animator, ignore the repeat count and skip to the end
            done = true;
        } else if (newIteration && !lastIterationFinished) {
            // Time to repeat
            if (mListeners != null) {
                int numListeners = mListeners.size();
                for (int i = 0; i < numListeners; ++i) {
                    mListeners.get(i).onAnimationRepeat(this);
                }
            }
        } else if (lastIterationFinished) {
            done = true;
        }
        mOverallFraction = clampFraction(fraction);
        float currentIterationFraction = getCurrentIterationFraction(
                mOverallFraction, mReversing);
        animateValue(currentIterationFraction);
    }
    return done;
}

最后又调用到了熟悉的animateValue方法,于是随着每一个垂直信号的到来都会进行属性值的计算和赋值。该方法有一个布尔类型的返回值,表示当前动画是否结束,如果结束调用endAnimation方法。

private void endAnimation() {
    if (mAnimationEndRequested) {
        return;
    }
    removeAnimationCallback();

    mAnimationEndRequested = true;
    mPaused = false;
    boolean notify = (mStarted || mRunning) && mListeners != null;
    if (notify && !mRunning) {
        // If it's not yet running, then start listeners weren't called. Call them now.
        notifyStartListeners();
    }
    mRunning = false;
    mStarted = false;
    mStartListenersCalled = false;
    mLastFrameTime = -1;
    mFirstFrameTime = -1;
    mStartTime = -1;
    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);
        }
    }
    // mReversing needs to be reset *after* notifying the listeners for the end callbacks.
    mReversing = false;
}

在该方法中移除了动画的回调,将相关参数值重置,最后调用了动画结束的回调方法通知外界动画执行完毕。

相关文章

网友评论

    本文标题:Android源码学习-属性动画源码浅析

    本文链接:https://www.haomeiwen.com/subject/jrubcctx.html