Android Animator 源码分析

关于Android 属性动画的使用,请见
blog.csdn.net/y874961524/…

下面分析下Animator在Framework层的实现

从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);
}
// 设置目标对象
@Override
public void setTarget(@Nullable Object target) {
    final Object oldTarget = getTarget();
    if (oldTarget != target) {
        if (isStarted()) {
            cancel();
        }
        // target必须是一个弱引用对象
        mTarget = target == null ? null : new WeakReference<Object>(target);
        // New target should cause re-initialization prior to starting
        mInitialized = false; // 记录尚未初始化,ValueAnimator的标志位,一会要用
    }
}
// 设置属性名称
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进行
        // Animator只进行数值计算
        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;
}

ofFloat还有一步就是调用这个方法

@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
        // 这是mProperty是为null的 
        if (mProperty != null) {
            setValues(PropertyValuesHolder.ofFloat(mProperty, values));
        } else {
   setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
        }
    } else {
        super.setFloatValues(values);
    }
}

setValues这个过程有点长,按照顺序先写下吧

// PropertyValueHolder中
// 获取PropertyValuesHolder
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) {
    // 这个过程会取得value的类型
    super.setFloatValues(values);
    mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
}

// super.setFloatValues()
// 取得Value的类型
public void setFloatValues(float... values) {
    mValueType = float.class;
    mKeyframes = KeyframeSet.ofFloat(values);
}

然后设置KeyFrame了,KeyFrame时属性动画中的关键帧,通过设置关键帧来保证动画执行时序性

// ~KeyFrameSet中
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中
public static Keyframe ofFloat(float fraction) {
    return new FloatKeyframe(fraction);
}

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

仔细看了看,发现KeyFrame其实只是对当前的value和fraction进行一个记录,不同类型的KeyFrame会设置不同的mValueType

KeyFrame中的属性值,一会回来再看

/** * Flag to indicate whether this keyframe has a valid value. This flag is used when an * animation first starts, to populate placeholder keyframes with real values derived * from the target object. */
boolean mHasValue;

/** * Flag to indicate whether the value in the keyframe was read from the target object or not. * If so, its value will be recalculated if target changes. */
boolean mValueWasSetOnStart;


/** * The time at which mValue will hold true. */
float mFraction;

/** * The type of the value in this Keyframe. This type is determined at construction time, * based on the type of the <code>value</code> object passed into the constructor. */
Class mValueType;

/** * The optional time interpolator for the interval preceding this keyframe. A null interpolator * (the default) results in linear interpolation over the interval. */
private TimeInterpolator mInterpolator = null;

回来看哪个keyframe的ofFloat方法,最终会创建一个关键帧集合


public KeyframeSet(Keyframe... keyframes) {
    mNumKeyframes = keyframes.length;
    // immutable list
    mKeyframes = Arrays.asList(keyframes);
    mFirstKeyframe = keyframes[0];
    mLastKeyframe = keyframes[mNumKeyframes - 1];
    mInterpolator = mLastKeyframe.getInterpolator();
}

ObjectAnimator的ofFloat过程就结束了,下面看下其他方法

setDuration()

设置动画的执行时间
这个就是将执行时间写入属性中,一会会用到

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

setInterpolator()

设置插值器,默认的插值器是带有加速度的

// The time interpolator to be used if none is set on the animation
private static final TimeInterpolator sDefaultInterpolator =
        new AccelerateDecelerateInterpolator();
@Override
public void setInterpolator(TimeInterpolator value) {
    if (value != null) {
        mInterpolator = value;
    } else {
        mInterpolator = new LinearInterpolator();
    }
}

setEvaluator()

设置估值器
估值器实际上是在KeyFrame中使用的

public void setEvaluator(TypeEvaluator value) {
   if (value != null && mValues != null && mValues.length > 0) {
       mValues[0].setEvaluator(value);
   }
}
public void setEvaluator(TypeEvaluator evaluator) {
    mEvaluator = evaluator;
    mKeyframes.setEvaluator(evaluator);
}
//KeyFrameSet中
public void setEvaluator(TypeEvaluator evaluator) {
    // 使用属性值进行保存
    mEvaluator = evaluator;
}

start()

开始分析开始动画的方法
从ObjectAnimator开始,会调用到ValueAnimator的start(boolean playBackwards)方法

// playBackwards 是否倒序播放,我们此时传入的是false
private void start(boolean playBackwards) {
    if (Looper.myLooper() == null) {
        throw new AndroidRuntimeException("Animators may only be run on Looper threads");
    }
    mReversing = playBackwards;
    // Special case: reversing from seek-to-0 should act as if not seeked at all.
    // mSeekFraction这个标志位,第一次启动动画时是-1,暂时不会进入
    if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {
        if (mRepeatCount == INFINITE) {
            // Calculate the fraction of the current iteration.
            float fraction = (float) (mSeekFraction - Math.floor(mSeekFraction));
            mSeekFraction = 1 - fraction;
        } else {
            mSeekFraction = 1 + mRepeatCount - mSeekFraction;
        }
    }
    // 记录标志位
    mStarted = true;
    mPaused = false;
    mRunning = false;
    // Resets mLastFrameTime when start() is called, so that if the animation was running,
    // calling start() would put the animation in the
    // started-but-not-yet-reached-the-first-frame phase.
    mLastFrameTime = 0;
    // 这里从线程中取出AnimatonHandler,一会分析
    AnimationHandler animationHandler = AnimationHandler.getInstance();
    animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));
    // mStartedDelay指示这个动画是否已经从startDelay中开始执行。
    // 这里mStartDelay=0可以顺利启动
    if (mStartDelay == 0 || mSeekFraction >= 0) {
        // 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.
            // 第一次启动,设置当前启动时间为0
            setCurrentPlayTime(0);
        } else {
            setCurrentFraction(mSeekFraction);
        }
    }
}

AnimationHandler分析

AnimationHandler 是一个实现了Runnable接口的ValueAnimator内部类

从当前线程中取得AnimationHandler对象

public static AnimationHandler getInstance() {
    if (sAnimatorHandler.get() == null) {
        sAnimatorHandler.set(new AnimationHandler());
    }
    return sAnimatorHandler.get();
}

然后注册了两个回调,看下具体方法

/** * Register to get a callback on the next frame after the delay. */
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
    if (mAnimationCallbacks.size() == 0) {
        getProvider().postFrameCallback(mFrameCallback);
    }
    if (!mAnimationCallbacks.contains(callback)) {
        mAnimationCallbacks.add(callback);
    }

    if (delay > 0) {
        mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
    }
}
interface AnimationFrameCallback {
  /** * Run animation based on the frame time. * @param frameTime The frame start time, in the {@link SystemClock#uptimeMillis()} time * base. */
    // 每一帧动画开始时回调
   void doAnimationFrame(long frameTime);

   /** * This notifies the callback of frame commit time. Frame commit time is the time after * traversals happen, as opposed to the normal animation frame time that is before * traversals. This is used to compensate expensive traversals that happen as the * animation starts. When traversals take a long time to complete, the rendering of the * initial frame will be delayed (by a long time). But since the startTime of the * animation is set before the traversal, by the time of next frame, a lot of time would * have passed since startTime was set, the animation will consequently skip a few frames * to respect the new frameTime. By having the commit time, we can adjust the start time to * when the first frame was drawn (after any expensive traversals) so that no frames * will be skipped. * * @param frameTime The frame time after traversals happen, if any, in the * {@link SystemClock#uptimeMillis()} time base. */
    // 每一帧开始遍历时回调
   void commitAnimationFrame(long frameTime);
}

从ValueAnimator中看下具体实现

// 这里对每一帧进行处理,如果时从暂停恢复,将调整开始时间
public final void doAnimationFrame(long frameTime) {
    AnimationHandler handler = AnimationHandler.getInstance();
    if (mLastFrameTime == 0) {
        // First frame
        handler.addOneShotCommitCallback(this);
        if (mStartDelay > 0) {
            startAnimation();
        }
        if (mSeekFraction < 0) {
            mStartTime = frameTime;
        } else {
            long seekTime = (long) (getScaledDuration() * mSeekFraction);
            mStartTime = frameTime - seekTime;
            mSeekFraction = -1;
        }
        mStartTimeCommitted = false; // allow start time to be compensated for jank
    }
    mLastFrameTime = frameTime;
    if (mPaused) {
        mPauseTime = frameTime;
        handler.removeCallback(this);
        return;
    } else if (mResumed) {
        mResumed = false;
        if (mPauseTime > 0) {
            // Offset by the duration that the animation was paused
            mStartTime += (frameTime - mPauseTime);
            mStartTimeCommitted = false; // allow start time to be compensated for jank
        }
        handler.addOneShotCommitCallback(this);
    }
    // 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);
    boolean finished = animateBasedOnTime(currentTime);

    if (finished) {
        endAnimation();
    }
}

这个回调也是遍历时调整启动时间的

public void commitAnimationFrame(long frameTime) {
    if (!mStartTimeCommitted) {
        mStartTimeCommitted = true;
        long adjustment = frameTime - mLastFrameTime;
        if (adjustment > 0) {
            mStartTime += adjustment;
            if (DEBUG) {
                Log.d(TAG, "Adjusted start time by " + adjustment + " ms: " + toString());
            }
        }
    }
}

startAnimation()

这段代码时start()中真正启动动画的代码,必须在UI线程,仔细研究下

private void startAnimation() {
    if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
        Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
                System.identityHashCode(this));
    }

    mAnimationEndRequested = false;
    // 初始化动画
    initAnimation();
    mRunning = true;
    if (mSeekFraction >= 0) {
        mOverallFraction = mSeekFraction;
    } else {
        mOverallFraction = 0f;
    }
    if (mListeners != null) {
        // 通知所有回调
        notifyStartListeners();
    }
}

先会调用ObjectAnimator的initAnimation,只要是初始化反射的方法,对Target的属性值进行修改

@CallSuper
@Override
void initAnimation() {
    if (!mInitialized) {
        // mValueType may change due to setter/getter setup; do this before calling super.init(),
        // which uses mValueType to set up the default type evaluator.
        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();
    }
}

PropertyValuesHolder的setupSetterAndGetter()

初始化反射修改器,这里代码有点多

void setupSetterAndGetter(Object target) {
    mKeyframes.invalidateCache();
    if (mProperty != null) {
        // check to make sure that mProperty is on the class of target
        // try-catch判断是否这个属性在这个类里
        try {
            Object testValue = null;
            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 (testValue == null) {
                        testValue = convertBack(mProperty.get(target));
                    }
                    kf.setValue(testValue);
                    kf.setValueWasSetOnStart(true);
                }
            }
            return;
        } catch (ClassCastException e) {
            Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
                    ") on target object " + target + ". Trying reflection instead");
            mProperty = null;
        }
    }
    // 如果还没有找到属性的话,判断get和set方法是否存在
    // 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) {
            // 初始化setter
            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) {
                    // 初始化getter
                    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());
                }
            }
        }
    }
}

继续寻找set和get方法

private Method setupSetterOrGetter(Class targetClass,
        HashMap<Class, HashMap<String, Method>> propertyMapMap,
        String prefix, Class valueType) {
    Method setterOrGetter = null;
    synchronized(propertyMapMap) {
        // Have to lock property map prior to reading it, to guard against
        // another thread putting something in there after we've checked it
        // but before we've added an entry to it

        HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
        boolean wasInMap = false;
        if (propertyMap != null) {
            wasInMap = propertyMap.containsKey(mPropertyName);
            if (wasInMap) {
                setterOrGetter = propertyMap.get(mPropertyName);
            }
        }
        if (!wasInMap) {
            // 通过属性值寻找方法
            setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
            if (propertyMap == null) {
                propertyMap = new HashMap<String, Method>();
                propertyMapMap.put(targetClass, propertyMap);
            }
            // 放入map中
            propertyMap.put(mPropertyName, setterOrGetter);
        }
    }
    //返回给mSetter或mGetter
    return setterOrGetter;
}

然后调用ValueAnimator的initAnimation

这里会初始化每一个PropertyValuesHolder

void initAnimation() {
    if (!mInitialized) {
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            // 这里会初始化每一个PropertyValuesHolder
            mValues[i].init();
        }
        mInitialized = true;
    }
}

PropertyValuesHolder中的init()

void init() {
    if (mEvaluator == null) {
        // We already handle int and float automatically, but not their Object
        // equivalents
        mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
                (mValueType == Float.class) ? sFloatEvaluator :
                null;
    }
    if (mEvaluator != null) {
        // KeyframeSet knows how to evaluate the common types - only give it a custom
        // evaluator if one has been set on this class
        // 给每一个KeyFrame设置估值器,前面讲过
        mKeyframes.setEvaluator(mEvaluator);
    }
}

start()中的setCurrentPlayTime()

start()中调用的启动动画方法

public void setCurrentPlayTime(long playTime) {
    // 计算fraction
    float fraction = mDuration > 0 ? (float) playTime / mDuration : 1;
    setCurrentFraction(fraction);
}
// 将计算出来的Fraction设置给动画
public void setCurrentFraction(float fraction) {
    initAnimation();
    fraction = clampFraction(fraction);
    long seekTime = (long) (getScaledDuration() * fraction);
    // 当前执行动画的时间
    long currentTime = AnimationUtils.currentAnimationTimeMillis();
    mStartTime = currentTime - seekTime;
    mStartTimeCommitted = true; // do not allow start time to be compensated for jank
    if (!isPulsingInternal()) {
        // If the animation loop hasn't started, the startTime will be adjusted in the first
        // frame based on seek fraction.
        mSeekFraction = fraction;
    }
    mOverallFraction = fraction;
    final float currentIterationFraction = getCurrentIterationFraction(fraction);
    // 拿到Fraction以后,开始变化数值
    animateValue(currentIterationFraction);
}

对动画数值进行运算

先会调用ObjectAnimator中的animateValue

@Override
void animateValue(float fraction) {
    final Object target = getTarget();
    if (mTarget != null && target == null) {
        // We lost the target reference, cancel and clean up.
        cancel();
        return;
    }
    // 这里调用ValueAnimator中的animateValue计算数值
    // ValueAnimator与属性值无关的,一会再看
    super.animateValue(fraction);
    int numValues = mValues.length;
    //反射修改每个方法值
    //这里修改完这一轮动画就结束了
    for (int i = 0; i < numValues; ++i) {
        mValues[i].setAnimatedValue(target);
    }
}

ValueAnimator中的animateValue进行插值运算

void animateValue(float fraction) {
    // 插值运算在这里
    fraction = mInterpolator.getInterpolation(fraction);
    // 获取当前的Fraction
    mCurrentFraction = fraction;
    int numValues = mValues.length;
    for (int i = 0; i < numValues; ++i) {
        // 对每个PropertyValuesHolder计算数值
        mValues[i].calculateValue(fraction);
    }
    // 回调update监听器
    if (mUpdateListeners != null) {
        int numListeners = mUpdateListeners.size();
        for (int i = 0; i < numListeners; ++i) {
            mUpdateListeners.get(i).onAnimationUpdate(this);
        }
    }
}

PropertyValuesHolder中的calculateValue()

通过获取的fraction,对每个属性值进行变化,这个过程通过反射进行

void calculateValue(float fraction) {
   // 从KeyFrame中获取计算完成的属性值,我们来看下这个方法
   Object value = mKeyframes.getValue(fraction);
   // 这里取到属性值
   mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
}

KeyFrameSet中的getValue()

//KeyFrameSet中
public Object getValue(float fraction) {
    // Special-case optimization for the common case of only two keyframes
    // 只有两个关键帧的情况
    if (mNumKeyframes == 2) {
        if (mInterpolator != null) {
            fraction = mInterpolator.getInterpolation(fraction);
        }
        // 通过估值器进行取值
        return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(),
                mLastKeyframe.getValue());
    }
    // 此处处理多个关键帧的情况,取出俩个关键帧之前的Fraction
    // 进行计算
    if (fraction <= 0f) {
        final Keyframe nextKeyframe = mKeyframes.get(1);
        final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
        if (interpolator != null) {
            fraction = interpolator.getInterpolation(fraction);
        }
        final float prevFraction = mFirstKeyframe.getFraction();
        float intervalFraction = (fraction - prevFraction) /
            (nextKeyframe.getFraction() - prevFraction);
        return mEvaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(),
                nextKeyframe.getValue());
    } else if (fraction >= 1f) {
        final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2);
        final TimeInterpolator interpolator = mLastKeyframe.getInterpolator();
        if (interpolator != null) {
            fraction = interpolator.getInterpolation(fraction);
        }
        final float prevFraction = prevKeyframe.getFraction();
        float intervalFraction = (fraction - prevFraction) /
            (mLastKeyframe.getFraction() - prevFraction);
        return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
                mLastKeyframe.getValue());
    }
    Keyframe prevKeyframe = mFirstKeyframe;
    // 对两个关键帧之前的fraction使用估值器进行计算
    for (int i = 1; i < mNumKeyframes; ++i) {
        Keyframe nextKeyframe = mKeyframes.get(i);
        if (fraction < nextKeyframe.getFraction()) {
            final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
            final float prevFraction = prevKeyframe.getFraction();
            float intervalFraction = (fraction - prevFraction) /
                (nextKeyframe.getFraction() - prevFraction);
            // Apply interpolator on the proportional duration.
            if (interpolator != null) {
                intervalFraction = interpolator.getInterpolation(intervalFraction);
            }
            return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
                    nextKeyframe.getValue());
        }
        prevKeyframe = nextKeyframe;
    }
    // shouldn't reach here
    return mLastKeyframe.getValue();
}

PropertyValueHolder中的setAnimatedValue()反射修改属性值

如果是ofFloat创建的FloatPropertyValueHolder,那么该方法为

@Override
void setAnimatedValue(Object target) {
    if (mFloatProperty != null) {
        mFloatProperty.setValue(target, mFloatAnimatedValue);
        return;
    }
    if (mProperty != null) {
        mProperty.set(target, mFloatAnimatedValue);
        return;
    }
    // 针对jni属性
    if (mJniSetter != 0) {
        nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
        return;
    }
    // 反射修改数值
    if (mSetter != null) {
        try {
            mTmpValueArray[0] = mFloatAnimatedValue;
            mSetter.invoke(target, mTmpValueArray);
        } catch (InvocationTargetException e) {
            Log.e("PropertyValuesHolder", e.toString());
        } catch (IllegalAccessException e) {
            Log.e("PropertyValuesHolder", e.toString());
        }
    }
}

动画流程就是这样

    原文作者:Android源码分析
    原文地址: https://juejin.im/entry/58afa7e05c497d0067788656
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞