Android Animator 源碼分析

下面分析下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());
        }
    }
}

動畫流程就是這樣

 

來自:http://blog.csdn.net/y874961524/article/details/53984282

 

 本文由用戶 fange4828 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!