Android ObjectAnimator源碼不一樣的分析

轉(zhuǎn)載請(qǐng)注明原文鏈接:http://www.itdecent.cn/p/b0e7c92ecb43

1.前言

最近在看項(xiàng)目代碼中,突然冒出這樣一個(gè)想法:如果在ObjectAnimator動(dòng)畫結(jié)束回調(diào)函數(shù)onAnimationEnd中重新start此動(dòng)畫,是否能達(dá)到循環(huán)播放動(dòng)畫呢?其實(shí)通過setRepeatCount()和setRepeatMode()函數(shù)配合使用也是可以實(shí)現(xiàn)動(dòng)畫循環(huán)播放的效果,但是出于好奇,我還是想通過代碼實(shí)現(xiàn)來驗(yàn)證自己的想法,代碼如下:

final ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(target, "scaleX", 1.0f, 2.0f)
        .setDuration(5000);
objectAnimator.addListener(new Animator.AnimatorListener() {
    @Override
    public void onAnimationStart(Animator animation) {
        Log.d("MainActivity", "onAnimationStart");
    }

    @Override
    public void onAnimationEnd(Animator animator) {
        Log.d("MainActivity", "onAnimationEnd");
        objectAnimator.start();
    }

    @Override
    public void onAnimationCancel(Animator animation) {
        Log.d("MainActivity", "onAnimationCancel");
    }

    @Override
    public void onAnimationRepeat(Animator animation) {
        Log.d("MainActivity", "onAnimationRepeat");
    }
});
objectAnimator.start();

通過代碼實(shí)驗(yàn),驚奇地發(fā)現(xiàn),動(dòng)畫并沒有達(dá)到我預(yù)期的效果,這個(gè)漸顯動(dòng)畫只是播放了兩次而已,且在第二次動(dòng)畫過程中沒有onAnimationStart和onAnimationEnd方法的回調(diào)。

這是為什么呢?由此激起了我對(duì)ObjectAnimator源碼實(shí)現(xiàn)的興趣。在這邊博客中,主要分析ObjectAnimator動(dòng)畫實(shí)現(xiàn)機(jī)制以及ObjectAnimator在Duration時(shí)間內(nèi)是如何更新Target屬性值,希望對(duì)感興趣的同學(xué)有一些幫助。

2.基本使用

ObjectAnimator
        .ofFloat(button, "alpha", 0.0f, 1.0f)
        .setDuration(5000)
        .start();

3.深入分析

3.1 從ObjectAnimator.ofFloat()開始

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

這個(gè)靜態(tài)方法會(huì)創(chuàng)建一個(gè)ObjectAnimator對(duì)象,在構(gòu)造時(shí)同時(shí)設(shè)置屬性動(dòng)畫的目標(biāo)target和作用于target的屬性名propertyName,根據(jù)上面的例子,target為button,propertyName為alpha。

3.1.1 ObjectAnimator構(gòu)造函數(shù)

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

在ObjectAnimator構(gòu)造函數(shù)中分別調(diào)用了setTarget和setPropertyName方法

3.1.2 下面我們來分析ObjectAnimator.setTarget()方法

@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
        // 記錄尚未初始化,ValueAnimator的初始化標(biāo)志位
        mInitialized = false; 
    }
}

從源碼中,我們可以分析出兩點(diǎn):

  • 當(dāng)oldTarget不等于target,且已經(jīng)調(diào)用了start()方法使得mStarted標(biāo)志位為true時(shí),需要先cancel掉此動(dòng)畫,進(jìn)入ValueAnimator.cancel()函數(shù),你會(huì)發(fā)現(xiàn),系統(tǒng)會(huì)先遍歷mLiseners調(diào)用AnimatorLisener.onAnimatorCancel()函數(shù),接著調(diào)用ObjectAnimator.endAnimation函數(shù),最后遍歷mLiseners調(diào)用AnimatorLisener.onAnimatorEnd()函數(shù)。這里矯正了我之前的一個(gè)誤區(qū),不知道你之前是否也這樣的誤區(qū),在調(diào)用ObjectAnimtor.cancel()動(dòng)畫時(shí),不僅僅會(huì)回調(diào)AnimatorLisener.onAnimatorCancel()方法,還會(huì)回調(diào)AnimatorLisener.onAnimatorEnd()方法;
  • mTarget是一個(gè)軟引用,而不是一個(gè)強(qiáng)引用哦,這樣ObjectAnimator就不會(huì)持有View的引用,不會(huì)影響Activity的正常回收,從而不會(huì)引起Activity內(nèi)存泄漏。

3.1.3 接著分析ObjectAnimator.setPropertyName()方法

回到ObjectAnimator構(gòu)造函數(shù)中,setPropertyName方法設(shè)置屬性名稱,即為alpha

// 設(shè)置屬性名稱,也就是上面設(shè)置的”alpha"
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.
    // mValues是一個(gè)數(shù)組,用于保存PropertyValuesHolder
    if (mValues != null) {
        // 屬性值得更新操作委托給PropertyValuesHolder進(jìn)行
        // Animator只進(jìn)行數(shù)值計(jì)算
        PropertyValuesHolder valuesHolder = mValues[0];
        String oldName = valuesHolder.getPropertyName();
        // 更新第一個(gè)PropertyValuesHolder的PropertyName
        valuesHolder.setPropertyName(propertyName);
        mValuesMap.remove(oldName);
        mValuesMap.put(propertyName, valuesHolder);
    }
    mPropertyName = propertyName;
    // New property/values/target should cause re-initialization prior to starting
    // 記錄尚未初始化,ValueAnimator的標(biāo)志位
    mInitialized = false;
}

回到ObjectAnimator.OfFloat()方法中,還有一步就是調(diào)用ObjectAnimator.setFloatValues()方法

3.1.4 ObjectAnimator.setFloatValues()方法

@Override
public void setFloatValues(float... values) {
    // 第一次調(diào)用時(shí),values為null
    if (mValues == null || mValues.length == 0) {
        // No values yet - this animator is being constructed piecemeal. Init the values with
        // whatever the current propertyName is
        // 我們?cè)O(shè)置了mPropertyName,在這里mProperty為null
        if (mProperty != null) {
            setValues(PropertyValuesHolder.ofFloat(mProperty, values));
        } else {
            setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
        }
    } else {
        super.setFloatValues(values);
    }
}

在setValues之前先調(diào)用PropertyValuesHolder.ofFloat(mPropertyName, values)方法初始化PropertyValuesHolder對(duì)象

3.1.5 PropertyValuesHolder.ofFloat方法

// 靜態(tài)方法構(gòu)造FloatPropertyValuesHolder
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
    return new FloatPropertyValuesHolder(propertyName, values);
}

調(diào)用FloatPropertyValuesHolder構(gòu)造方法

// FloatPropertyValuesHolder構(gòu)造方法
public FloatPropertyValuesHolder(String propertyName, float... values) {
    super(propertyName);
    // 調(diào)用FloatPropertyValuesHolder.setFloatValues方法
    setFloatValues(values);
}

調(diào)用FloatPropertyValuesHolder的setFloatValues()方法

// FloatPropertyValuesHolder
@Override
public void setFloatValues(float... values) {
    // 調(diào)用PropertyValuesHolder.setFloatValues方法
    super.setFloatValues(values);
    mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
}

調(diào)用父類PropertyValuesHolder的setFloatValues()方法初始化關(guān)鍵幀集合

// PropertyValuesHolder.setFloatValues
public void setFloatValues(float... values) {
    // 記錄mValueType值,便于使用反射方式遍歷獲取目標(biāo)target對(duì)應(yīng)的set和get方法
    // 具體可以看getPropertyFunction方法
    mValueType = float.class;
    mKeyframes = KeyframeSet.ofFloat(values);
}

然后設(shè)置mKeyframes,KeyFrame是屬性動(dòng)畫中的關(guān)鍵幀,通過設(shè)置關(guān)鍵幀來保證動(dòng)畫執(zhí)行的時(shí)序性

3.1.6 KeyframeSet.ofFloat()方法

// 靜態(tài)方法KeyframeSet.ofFloat
public static KeyframeSet ofFloat(float... values) {
    boolean badValue = false;
    int numKeyframes = values.length;
    // 至少初始化兩個(gè)關(guān)鍵幀
    FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
    // 如果values只有一個(gè)數(shù)值時(shí),那么只有開始和結(jié)束這兩個(gè)關(guān)鍵幀
    if (numKeyframes == 1) {
        keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
        // 當(dāng)values只有一個(gè)數(shù)值時(shí),作為結(jié)束幀
        keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
        // 判斷values中的數(shù)值是否是有效值
        if (Float.isNaN(values[0])) {
            badValue = true;
        }
    } else {
        // 給values中的每一個(gè)數(shù)值都設(shè)置一個(gè)關(guān)鍵幀
        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;
            }
        }
    }
    // 當(dāng)values數(shù)值不是有效值時(shí),打印出日志,但是不做其他處理
    if (badValue) {
        Log.w("Animator", "Bad value (NaN) in float animator");
    }
    return new FloatKeyframeSet(keyframes);
}

接下來我們看一下關(guān)鍵幀Keyframe是如何創(chuàng)建的

public static Keyframe ofFloat(float fraction, float value) {
    return new FloatKeyframe(fraction, value);
}

在FloatKeyFrame構(gòu)造函數(shù)中保存fraction和value等值

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

KeyFrame其實(shí)只是對(duì)當(dāng)前的fraction和value做了一個(gè)保存作用,mValueType就是根據(jù)不同類型的KeyFrame設(shè)置不同的值,這里設(shè)置了float.class值

KeyFrame下面,我們接著看KeyframeSet.ofFloat方法,最終會(huì)創(chuàng)建一個(gè)關(guān)鍵幀的集合FloatKeyframeSet

public FloatKeyframeSet(FloatKeyframe... keyframes) {
    // 調(diào)用父類KeyframeSet的構(gòu)造方法
    super(keyframes);
}
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的過程就結(jié)束了,下面我們一起來看其他方法

3.2 分析ObjectAnimator.setDuration()方法

setDuration()用于設(shè)置動(dòng)畫執(zhí)行的時(shí)間,這個(gè)方法比較簡(jiǎn)單

public ValueAnimator setDuration(long duration) {
    // 檢查duration,小于零則拋出異常
    if (duration < 0) {
        throw new IllegalArgumentException("Animators cannot have negative duration: " +
                duration);
    }
    // 使用mUnscaledDuration保存未做縮放的動(dòng)畫執(zhí)行時(shí)間
    mUnscaledDuration = duration;
    // 計(jì)算經(jīng)過縮放的動(dòng)畫執(zhí)行時(shí)間,默認(rèn)情況下mDuration=duration
    updateScaledDuration();
    return this;
}

3.3 ObjectAnimator.setInterpolator()方法

setInterpolator()設(shè)置動(dòng)畫執(zhí)行時(shí)使用到的插值器,默認(rèn)的插值器是帶有加減速度的插值器

// 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 {
        // value為空時(shí),使用線性插值器
        mInterpolator = new LinearInterpolator();
    }
}

3.4 接下分析ObjectAnimator.start()方法

start()方法是ObjectAnimator中最重要的一個(gè)方法,控制著屬性動(dòng)畫的啟動(dòng),這里是見證奇跡的地方,不管你會(huì)不會(huì)激動(dòng),反正我是激動(dòng)了。

3.4.1 從ObjectAnimator.start()方法開始

@Override
public void start() {
    // See if any of the current active/pending animators need to be canceled
    AnimationHandler handler = sAnimationHandler.get();
    // 第一次啟動(dòng),handler為空
    if (handler != null) {
        // 通過遍歷mAnimations隊(duì)列來cancel當(dāng)前動(dòng)畫
        int numAnims = handler.mAnimations.size();
        for (int i = numAnims - 1; i >= 0; i--) {
            if (handler.mAnimations.get(i) instanceof ObjectAnimator) {
                ObjectAnimator anim = (ObjectAnimator) handler.mAnimations.get(i);
                if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
                    anim.cancel();
                }
            }
        }
        // 通過遍歷mPendingAnimations隊(duì)列來cancel當(dāng)前動(dòng)畫
        numAnims = handler.mPendingAnimations.size();
        for (int i = numAnims - 1; i >= 0; i--) {
            if (handler.mPendingAnimations.get(i) instanceof ObjectAnimator) {
                ObjectAnimator anim = (ObjectAnimator) handler.mPendingAnimations.get(i);
                if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
                    anim.cancel();
                }
            }
        }
        // 通過遍歷mDelayedAnims隊(duì)列來cancel當(dāng)前動(dòng)畫
        numAnims = handler.mDelayedAnims.size();
        for (int i = numAnims - 1; i >= 0; i--) {
            if (handler.mDelayedAnims.get(i) instanceof ObjectAnimator) {
                ObjectAnimator anim = (ObjectAnimator) handler.mDelayedAnims.get(i);
                if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
                    anim.cancel();
                }
            }
        }
    }
    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));
        }
    }
    // 轉(zhuǎn)到ValueAnimator的start()方法
    super.start();
}

AnimationHandler有三個(gè)重要的參數(shù):mAnimations、mPendingAnimations以及mDelayedAnims,具體如下:

  • mAnimations是一個(gè)用來保存當(dāng)前正在執(zhí)行的動(dòng)畫列表,
  • mPendingAnimation是一個(gè)用來保存已經(jīng)調(diào)用了start(boolean playBackwards)方法加入進(jìn)來的動(dòng)畫列表,不管是否設(shè)置mStartDelay延遲時(shí)間,都會(huì)加入到此列表中
  • mDelayedAnims是一個(gè)用來保存設(shè)置了mStartDelay延遲時(shí)間的動(dòng)畫列表,mStartDelay要大于零

3.4.2 最終調(diào)用到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;
    mPlayingBackwards = playBackwards;
    // 此時(shí)playBackwards為false,第一次啟動(dòng)動(dòng)畫時(shí)mSeekFraction為-1,不會(huì)進(jìn)入
    if (playBackwards && mSeekFraction != -1) {
        if (mSeekFraction == 0 && mCurrentIteration == 0) {
            // special case: reversing from seek-to-0 should act as if not seeked at all
            mSeekFraction = 0;
        } else if (mRepeatCount == INFINITE) {
            mSeekFraction = 1 - (mSeekFraction % 1);
        } else {
            mSeekFraction = 1 + mRepeatCount - (mCurrentIteration + mSeekFraction);
        }
        mCurrentIteration = (int) mSeekFraction;
        mSeekFraction = mSeekFraction % 1;
    }
    if (mCurrentIteration > 0 && mRepeatMode == REVERSE &&
            (mCurrentIteration < (mRepeatCount + 1) || mRepeatCount == INFINITE)) {
        // if we were seeked to some other iteration in a reversing animator,
        // figure out the correct direction to start playing based on the iteration
        if (playBackwards) {
            mPlayingBackwards = (mCurrentIteration % 2) == 0;
        } else {
            mPlayingBackwards = (mCurrentIteration % 2) != 0;
        }
    }
    int prevPlayingState = mPlayingState;
    mPlayingState = STOPPED;
    // 在調(diào)用start方法之后,mStarted狀態(tài)改為true
    mStarted = true;
    mStartedDelay = false;
    mPaused = false;
    updateScaledDuration(); // in case the scale factor has changed since creation time
    AnimationHandler animationHandler = getOrCreateAnimationHandler();
    // 把當(dāng)前動(dòng)畫加入到animationHandler.mPendingAnimation隊(duì)列中
    animationHandler.mPendingAnimations.add(this);
    // 此處啟動(dòng)動(dòng)畫,等一下分析
  if (mStartDelay == 0) {
        // This sets the initial value of the animation, prior to actually starting it running
        if (prevPlayingState != SEEKED) {
            // 第一次啟動(dòng),設(shè)置當(dāng)前啟動(dòng)時(shí)間為0
            setCurrentPlayTime(0);
        }
        mPlayingState = STOPPED;
        mRunning = true;
        // 回調(diào)AnimatorListener.onAnimationStart()方法通知用戶
        notifyStartListeners();
    }
    animationHandler.start();
}

這里代碼量有點(diǎn)多,主要是根據(jù)是否設(shè)置動(dòng)畫循環(huán)播放來設(shè)置標(biāo)志位和狀態(tài),在start方法中調(diào)用ValueAnimator.setCurrentPlayTime()方法來設(shè)置動(dòng)畫的播放時(shí)間

3.4.3 緊接著進(jìn)入ValueAnimator.setCurrentPlayTime()方法

// ValueAnimator
public void setCurrentPlayTime(long playTime) {
    // mUnscaledDuration就是我們?cè)O(shè)置的動(dòng)畫執(zhí)行時(shí)間,這里為5000毫秒
    // 第一次執(zhí)行時(shí),我們的playTime傳進(jìn)來就是0
    float fraction = mUnscaledDuration > 0 ? (float) playTime / mUnscaledDuration : 1;
    // 第一次執(zhí)行時(shí),fraction=0,調(diào)用ValueAnimator.setCurrentFraction()方法
    setCurrentFraction(fraction);
}

3.4.4 調(diào)用到ValueAnimator.setCurrentFraction()方法

接下來一起進(jìn)入setCurrentFraction(float fraction)方法

public void setCurrentFraction(float fraction) {
    // 接下來馬上分析
    initAnimation();
    // 此時(shí)傳進(jìn)來的fraction=0
    if (fraction < 0) {
        fraction = 0;
    }
    int iteration = (int) fraction;
    // 循環(huán)動(dòng)畫時(shí)才會(huì)進(jìn)入
    if (fraction == 1) {
        iteration -= 1;
    } else if (fraction > 1) {
        if (iteration < (mRepeatCount + 1) || mRepeatCount == INFINITE) {
            if (mRepeatMode == REVERSE) {
                mPlayingBackwards = (iteration % 2) != 0;
            }
            fraction = fraction % 1f;
        } else {
            fraction = 1;
            iteration -= 1;
        }
    } else {
        mPlayingBackwards = mReversing;
    }
    mCurrentIteration = iteration;
    // 默認(rèn)情況下,sDurationScale為1.0f,這里mDuration就是我們?cè)O(shè)置的動(dòng)畫執(zhí)行時(shí)間5000ms
    long seekTime = (long) (mDuration * fraction);
    // 獲取當(dāng)前動(dòng)畫執(zhí)行的時(shí)間點(diǎn)
    long currentTime = AnimationUtils.currentAnimationTimeMillis();
    // 計(jì)算當(dāng)前動(dòng)畫已經(jīng)執(zhí)行的時(shí)長(zhǎng)
    mStartTime = currentTime - seekTime;
    mStartTimeCommitted = true; // do not allow start time to be compensated for jank
    if (mPlayingState != RUNNING) {
        mSeekFraction = fraction;
        mPlayingState = SEEKED;
    }
    if (mPlayingBackwards) {
        fraction = 1f - fraction;
    }
    animateValue(fraction);
}

從上面的代碼分析來看,其中調(diào)用到了兩個(gè)方法,分別是initAnimation()和animateValue(fraction)方法

3.4.5 我們先來看ObjectAnimator.initAnimation()方法

@Override
// ObjectAnimator
void initAnimation() {
    // 第一次執(zhí)行時(shí),mInitialized為false,初始化后該標(biāo)志位置為true,可以避免多次init
    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) {
                // 執(zhí)行PropertyValuesHolder的setupSetterAndGetter()方法
                mValues[i].setupSetterAndGetter(target);
            }
        }
        super.initAnimation();
    }
}

3.4.6 分析PropertyValuesHolder.setupSetterAndGetter()

PropertyValuesHolder.setupSetterAndGetter(Object target)方法,主要是初始化反射方法mSetter和mGetter

// PropertyValuesHolder
void setupSetterAndGetter(Object target) {
    mKeyframes.invalidateCache();
    // 按照上面的例子,我們?cè)O(shè)置的是mPropertyName,mProperty為null,不會(huì)進(jìn)去
    if (mProperty != null) {
        // check to make sure that mProperty is on the class of target
        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;
        }
    }
    // We can't just say 'else' here because the catch statement sets mProperty to null.
    // mProperty為空,判斷get和set方法是否存在
    if (mProperty == null) {
        Class targetClass = target.getClass();
        if (mSetter == null) {
            // 查找目標(biāo)屬性的set方法,初始化mSetter方法
            setupSetter(targetClass);
        }
        // 遍歷關(guān)鍵幀集合
        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) {
                    // 查找目標(biāo)屬性的get方法,初始化mGetter方法
                    setupGetter(targetClass);
                    // mGetter為null時(shí),直接return
                    if (mGetter == null) {
                        // Already logged the error - just return to avoid NPE
                        return;
                    }
                }
                try {
                    // 通過mGetter反射獲取屬性值
                    Object value = convertBack(mGetter.invoke(target));
                    // 初始化關(guān)鍵幀Keyframe的mValue值
                    kf.setValue(value);
                    // 設(shè)置Keyframe標(biāo)志位為true,表示已經(jīng)初始化mValue
                    kf.setValueWasSetOnStart(true);
                } catch (InvocationTargetException e) {
                    Log.e("PropertyValuesHolder", e.toString());
                } catch (IllegalAccessException e) {
                    Log.e("PropertyValuesHolder", e.toString());
                }
            }
        }
    }
}

在上面代碼中調(diào)用到setupSetter和setupGetter方法,這個(gè)兩個(gè)方法最終都是調(diào)用setupSetterOrGetter方法

3.4.7 分析PropertyValuesHolder.setupSetterOrGetter()

// 參數(shù)prefix值為“set”或者“get”,在這里valueType為float.class
private Method setupSetterOrGetter(Class targetClass,
        HashMap<Class, HashMap<String, Method>> propertyMapMap,
        String prefix, Class valueType) {
    Method setterOrGetter = null;
    // 進(jìn)行同步鎖判斷
    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
        // 根據(jù)targetClass獲取HashMap,這個(gè)propertyMap是以mPropertyName為key,set或者get方法作為value
        // 在這里mPropertyName為"alpha"
        HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
        boolean wasInMap = false;
        if (propertyMap != null) {
            wasInMap = propertyMap.containsKey(mPropertyName);
            if (wasInMap) {
                setterOrGetter = propertyMap.get(mPropertyName);
            }
        }
        // 第一次初始化,wasInMap為false
        if (!wasInMap) {
            // 初始化setterOrGetter
            setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
            if (propertyMap == null) {
                propertyMap = new HashMap<String, Method>();
                propertyMapMap.put(targetClass, propertyMap);
            }
            propertyMap.put(mPropertyName, setterOrGetter);
        }
    }
    return setterOrGetter;
}

在setupSetterOrGetter方法中調(diào)用到了getPropertyFunction函數(shù)來初始化mSetter或者mGetter參數(shù)

3.4.8 接下來分析PropertyValuesHolder.getPropertyFunction()

private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
    // TODO: faster implementation...
    Method returnVal = null;
    // 通過prefix和mPropertyName拼接出方法名,如setAlpha或者getAlpha
    String methodName = getMethodName(prefix, mPropertyName);
    Class args[] = null;
    // valueType為Float.class
    if (valueType == null) {
        try {
            returnVal = targetClass.getMethod(methodName, args);
        } catch (NoSuchMethodException e) {
            // Swallow the error, log it later
        }
    } else {
        args = new Class[1];
        Class typeVariants[]; 
        // typeVariants為FLOAT_VARIANTS
        if (valueType.equals(Float.class)) {
            typeVariants = FLOAT_VARIANTS;
        } else if (valueType.equals(Integer.class)) {
            typeVariants = INTEGER_VARIANTS;
        } else if (valueType.equals(Double.class)) {
            typeVariants = DOUBLE_VARIANTS;
        } else {
            typeVariants = new Class[1];
            typeVariants[0] = valueType;
        }
        // FLOAT_VARIANTS,遍歷含有float.class、Float.class、double.class、Double.class等參數(shù)的方法
        // 只要是相關(guān)的基本類型,都會(huì)遍歷反射查找set或者get方法,看到這里是不是感覺太神奇了
        for (Class typeVariant : typeVariants) {
            args[0] = typeVariant;
            try {
                // 反射獲取方法,成功則直接返回
                returnVal = targetClass.getMethod(methodName, args);
                if (mConverter == null) {
                    // change the value type to suit
                    mValueType = typeVariant;
                }
                return returnVal;
            } catch (NoSuchMethodException e) {
                // Swallow the error and keep trying other variants
            }
        }
        // If we got here, then no appropriate function was found
    }

    if (returnVal == null) {
        Log.w("PropertyValuesHolder", "Method " +
                getMethodName(prefix, mPropertyName) + "() with type " + valueType +
                " not found on target class " + targetClass);
    }

    return returnVal;
}

PropertyValuesHolder.setupSetterAndGetter方法已經(jīng)分析完畢,回到ObjectAnimator.initAnimation()方法中來,遍歷mValues初始化關(guān)鍵幀Keyframe的mSetter、mGetter和初始值,發(fā)現(xiàn)還會(huì)調(diào)用父類ValueAnimator.initAnimation()方法

3.4.9 接下來分析ValueAnimator.initAnimation()

// ValueAnimator.initAnimator方法
void initAnimation() {
    if (!mInitialized) {
        int numValues = mValues.length;
        // 遍歷mValues,調(diào)用PropertyValuesHolder.init()方法
        for (int i = 0; i < numValues; ++i) {
            mValues[i].init();
        }
        mInitialized = true;
    }
}

3.4.10 PropertyValuesHolder中的init方法

// PropertyValuesHolder
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
        // 給關(guān)鍵幀設(shè)置估值器
        mKeyframes.setEvaluator(mEvaluator);
    }
}

3.4.11 然后來分析ObjectAnimator.animateValue()方法

ObjectAnimator的initAnimatation過程已經(jīng)完畢,接下來我們繼續(xù)回到ValueAnimator.setCurrentFraction(float fraction)方法,最后調(diào)用了ObjectAnimator.animateValue(float fraction),這是一個(gè)很重要的方法,主要是通過反射的方式來修改目標(biāo)mTarget的屬性值,這里即是alpha值,在后面中會(huì)提到,這里可以先記錄一下

@Override
// ObjectAnimator
void animateValue(float fraction) {
    final Object target = getTarget();
    // mTarget是一個(gè)軟引用,判斷target是否已經(jīng)被回收
    if (mTarget != null && target == null) {
        // We lost the target reference, cancel and clean up.
        cancel();
        return;
    }

    // 這里調(diào)用父類ValueAnimator的animateValue來計(jì)算數(shù)值
    super.animateValue(fraction);
    int numValues = mValues.length;
    for (int i = 0; i < numValues; ++i) {
        // 反射修改每一個(gè)屬性值,這里修改完這一輪動(dòng)畫就結(jié)束了
        mValues[i].setAnimatedValue(target);
    }
}

分析可以看出,如果軟引用mTarget已經(jīng)被回收,就直接調(diào)用cancel方法后return,不再執(zhí)行動(dòng)畫刷新動(dòng)作;

上面調(diào)用父類ValueAnimator中的animateValue方法來進(jìn)行插值計(jì)算

// ValueAnimator
void animateValue(float fraction) {
    // 通過插值器進(jìn)行計(jì)算
    fraction = mInterpolator.getInterpolation(fraction);
    // 獲取當(dāng)前的fraction值
    mCurrentFraction = fraction;
    int numValues = mValues.length;
    for (int i = 0; i < numValues; ++i) {
        // 對(duì)每一個(gè)PropertyValuesHolder計(jì)算數(shù)值
        mValues[i].calculateValue(fraction);
    }
    if (mUpdateListeners != null) {
        int numListeners = mUpdateListeners.size();
        for (int i = 0; i < numListeners; ++i) {
            // 回調(diào)mUpdateListeners監(jiān)聽器
            mUpdateListeners.get(i).onAnimationUpdate(this);
        }
    }
}

回到子類ObjectAnimator的animateValue(float fraction)方法,遍歷mValues調(diào)用PropertyValuesHolder.setAnimatedValue(target)方法,通過反射方式來修改target的屬性值,上面的例子我們是通過PropertyValuesHolder.ofFloat來創(chuàng)建FloatPropertyValuesHolder,那么調(diào)用的就是FloatPropertyValuesHolder的setAnimatedValue方法

3.4.12 FloatPropertyValuesHolder.setAnimatedValue

@Override
void setAnimatedValue(Object target) {
    // 我們傳進(jìn)來的是mPropertyName
    if (mFloatProperty != null) {
        mFloatProperty.setValue(target, mFloatAnimatedValue);
        return;
    }
    if (mProperty != null) {
        mProperty.set(target, mFloatAnimatedValue);
        return;
    }
    // 針對(duì)jni屬性
    if (mJniSetter != 0) {
        nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
        return;
    }
    // 終于到了,反射修改屬性值就在這里執(zhí)行的
    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());
        }
    }
}

在這里,我們已經(jīng)把setCurrentPlayTime()方法分析完畢,回到ValueAnimator.start(boolean playBackwards)方法中,最后一行調(diào)用到了AnimationHandlers.start()方法,這里是啟動(dòng)動(dòng)畫執(zhí)行,在mDuration時(shí)間內(nèi),通過間隔時(shí)間來更新目標(biāo)的屬性值,從而實(shí)現(xiàn)一系列的動(dòng)畫變化效果。接下來我們一起來分析AnimationHandlers.start()方法,一起來探究是怎么實(shí)現(xiàn)在mDuration時(shí)間內(nèi)來實(shí)現(xiàn)動(dòng)畫變化效果的。

3.4.13 AnimationHandlers.start()啟動(dòng)動(dòng)畫執(zhí)行

// AnimationHandlers.start()
public void start() {
    scheduleAnimation();
}
// AnimationHandlers.scheduleAnimation()
private void scheduleAnimation() {
    if (!mAnimationScheduled) {
        mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
        // mAnimationScheduled置為true,控制動(dòng)畫執(zhí)行,避免在同一幀渲染中重復(fù)執(zhí)行
        mAnimationScheduled = true;
    }
}

start()方法最終是調(diào)用scheduleAnimation()方法,分析scheduleAnimation()方法可以得出,是通過mChoreographer的postCallback方法來啟動(dòng)動(dòng)畫執(zhí)行的,相當(dāng)于起了一個(gè)定時(shí)器來不斷更新屬性值alpha來實(shí)現(xiàn)動(dòng)畫刷新,那么mChoreographer是干嘛的呢,有興趣的可以去看一下其實(shí)現(xiàn)的源碼深入了解一下

在這里我簡(jiǎn)單描述一下,Choreographer這個(gè)類是用來控制同步處理輸入(Input)、動(dòng)畫(Animation)以及繪制(Draw)三個(gè)UI操作的,通過接收顯示系統(tǒng)的時(shí)間脈沖(垂直同步信號(hào)-VSync信號(hào)),在下一個(gè)Frame渲染時(shí)控制執(zhí)行這些操作。

上面的代碼中

mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);

參數(shù)mAnimate是一個(gè)Runnable,其實(shí)是在下一個(gè)幀進(jìn)行渲染時(shí),Choreographer執(zhí)行這個(gè)Runnable(即mAnimate的run方法)

// Called by the Choreographer.
final Runnable mAnimate = new Runnable() {
    @Override
    public void run() {
        // mAnimationScheduled置為false
        mAnimationScheduled = false;
        doAnimationFrame(mChoreographer.getFrameTime());
    }
};

從mAnimate的實(shí)現(xiàn)可以看到,最終會(huì)調(diào)到AnimatationHandlers的doAnimationFrame(long frameTime)方法

3.1.14 分析AnimatationHandlers.doAnimationFrame方法是如何實(shí)現(xiàn)動(dòng)畫刷新的

// AnimatationHandlers
void doAnimationFrame(long frameTime) {
    mLastFrameTime = 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.
    // 循環(huán)判斷mPendingAnimations列表是否有數(shù)據(jù)
    while (mPendingAnimations.size() > 0) {
        // 備份mPendingAnimations列表
        ArrayList<ValueAnimator> pendingCopy =
                (ArrayList<ValueAnimator>) mPendingAnimations.clone();
        // 清空mPendingAnimations列表
        mPendingAnimations.clear();
        int count = pendingCopy.size();
        // 遍歷pendingCopy
        for (int i = 0; i < count; ++i) {
            ValueAnimator anim = pendingCopy.get(i);
            // If the animation has a startDelay, place it on the delayed list
            // animation如果設(shè)置了mStartDelay時(shí)間,加入到mDelayedAnims隊(duì)列中             // 以便在下一個(gè)Frame渲染時(shí)遍歷查看mStartDelay時(shí)間是否已經(jīng)到了
            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();
    // 遍歷mDelayedAnims隊(duì)列,判斷mStartDelay時(shí)間是否已經(jīng)到了,如果是的話則暫存到mReadyAnims隊(duì)列中
    for (int i = 0; i < numDelayedAnims; ++i) {
        ValueAnimator anim = mDelayedAnims.get(i);
        // 判斷anime的mStartDelay時(shí)間是否到期
        if (anim.delayedAnimationFrame(frameTime)) {
            // 把a(bǔ)nim加入到mReadyAnims隊(duì)列中
            mReadyAnims.add(anim);
        }
    }
    int numReadyAnims = mReadyAnims.size();
    // mReadyAnims保存的就是mDelayedAnims隊(duì)列中delay時(shí)間已經(jīng)到期的animator,遍歷mReadyAnims隊(duì)列
    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
        mReadyAnims.clear();
    }

    // Now process all active animations. The return value from animationFrame()
    // tells the handler whether it should now be ended
    // 把mAnimations隊(duì)列中的數(shù)據(jù)備份到mTmpAnimations隊(duì)列中
    int numAnims = mAnimations.size();
    for (int i = 0; i < numAnims; ++i) {
        mTmpAnimations.add(mAnimations.get(i));
    }
    // 遍歷mTmpAnimations隊(duì)列
    for (int i = 0; i < numAnims; ++i) {
        ValueAnimator anim = mTmpAnimations.get(i);
        // 判斷anim動(dòng)畫是否執(zhí)行完畢,則加入到mEndingAnims隊(duì)列中
        if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) {
            mEndingAnims.add(anim);
        }
    }
    // 清空mTmpAnimations
    mTmpAnimations.clear();
    // 遍歷mEndingAnims隊(duì)列,調(diào)用ValueAnimator的endAnimation方法
    if (mEndingAnims.size() > 0) {
        for (int i = 0; i < mEndingAnims.size(); ++i) {
            mEndingAnims.get(i).endAnimation(this);
        }
        mEndingAnims.clear();
    }

    // Schedule final commit for the frame.
    // 更新ValueAnimator的mStartTime時(shí)間,暫時(shí)不去看
    mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, mCommit, null);

    // If there are still active or delayed animations, schedule a future call to
    // onAnimate to process the next frame of the animations.
    // 如果還有動(dòng)畫正在執(zhí)行,或者還有未執(zhí)行的動(dòng)畫,則調(diào)用scheduleAnimation方法
    if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
        scheduleAnimation();
    }
}

這里代碼量有點(diǎn)多,我們先分析在遍歷mPendingAnimations和mDelayedAnims隊(duì)列時(shí),都調(diào)用到ValueAnimator的startAnimation(AnimationHandler handler)方法,下面我們一起來分析

3.4.15 ValueAnimator.startAnimation方法

// ValueAnimator
private void startAnimation(AnimationHandler handler) {
    if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
        Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
                System.identityHashCode(this));
    }
    // 注意到?jīng)]有,這里又調(diào)用到了ObjectAnimator的initAnimation方法
    initAnimation();
    // 把當(dāng)前正在執(zhí)行的動(dòng)畫加入到AnimationHandler的mAnimations隊(duì)列中
    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();
    }
}

在上面的代碼中,大家注意到了沒有,在startAnimation方法中調(diào)用到了ObjectAnimator的initAnimation()方法,這個(gè)initAnimation方法在上面已經(jīng)分析了,回過頭來看一下,這個(gè)initAnimation方法就是初始化動(dòng)畫執(zhí)行過程用到的參數(shù)值,如關(guān)鍵幀Keyframe、get和set方法。那么在上面ObjectAnimator.start()方法中已經(jīng)初始化了,為什么這里還要調(diào)用呢,這是為了使設(shè)置了mStartDelay延遲時(shí)間的Animator進(jìn)行初始化調(diào)用的。

回到AnimatationHandlers的doAnimationFrame(long frameTime)方法中,在遍歷mTmpAnimations隊(duì)列時(shí),調(diào)用了ValueAnimator的doAnimationFrame(long frameTime),接下來我們一起來看一下是怎么實(shí)現(xiàn)的

3.4.16 探究ValueAnimator.doAnimationFrame()方法

// ValueAnimator
final boolean doAnimationFrame(long frameTime) {
    // mPlayingState狀態(tài)改變
    if (mPlayingState == STOPPED) {
        mPlayingState = RUNNING;
        if (mSeekFraction < 0) {
            mStartTime = frameTime;
        } else {
            long seekTime = (long) (mDuration * mSeekFraction);
            mStartTime = frameTime - seekTime;
            mSeekFraction = -1;
        }
        mStartTimeCommitted = false; // allow start time to be compensated for jank
    }
    if (mPaused) {
        if (mPauseTime < 0) {
            mPauseTime = frameTime;
        }
        return false;
    } 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
        }
    }
    // 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);
    return animationFrame(currentTime);
}

在最后一行中調(diào)用到了ValueAnimator.animationFrame()方法

// ValueAnimator
boolean animationFrame(long currentTime) {
    boolean done = false;
    switch (mPlayingState) {
    case RUNNING:
    case SEEKED:
        // 計(jì)算fraction
        float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
        // 在上面的例子中,mDuration設(shè)置為5000ms
        if (mDuration == 0 && mRepeatCount != INFINITE) {
            // Skip to the end
            mCurrentIteration = mRepeatCount;
            if (!mReversing) {
                mPlayingBackwards = false;
            }
        }
        // 這里處理循環(huán)動(dòng)畫的情況,我們暫時(shí)可以略過
        if (fraction >= 1f) {
            if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
                // Time to repeat
                if (mListeners != null) {
                    int numListeners = mListeners.size();
                    for (int i = 0; i < numListeners; ++i) {
                        mListeners.get(i).onAnimationRepeat(this);
                    }
                }
                if (mRepeatMode == REVERSE) {
                    mPlayingBackwards = !mPlayingBackwards;
                }
                mCurrentIteration += (int) fraction;
                fraction = fraction % 1f;
                mStartTime += mDuration;
                // Note: We do not need to update the value of mStartTimeCommitted here
                // since we just added a duration offset.
            } else {
                done = true;
                fraction = Math.min(fraction, 1.0f);
            }
        }
        // 根據(jù)上面的例子,mPlayingBackwards為false
        if (mPlayingBackwards) {
            fraction = 1f - fraction;
        }
        // 注意啦,這里調(diào)用到ObjectAnimator的animateValue方法
        animateValue(fraction);
        break;
    }

    return done;
}

通過上面的分析,我們注意到,在animationFrame(long currentTime)方法中調(diào)用了ObjectAnimator的animateValue(float fraction),而這個(gè)方法我們?cè)谏厦嬉呀?jīng)分析過了,終于又回來了,這個(gè)animateValue方法主要就是根據(jù)fraction計(jì)算屬性值,然后通過反射的方式修改目標(biāo)mTarget的alpha屬性值,從而達(dá)到mTarget的alpha值從0.0到1.0的漸顯效果。

終于到了見證奇跡的地方,ObjectAnimator動(dòng)畫執(zhí)行就是不斷的通過mChoreographer的postCallback方法實(shí)現(xiàn)在幀渲染時(shí)執(zhí)行mAnimate這個(gè)Runnable,在animateValue方法中利用反射方式改變目標(biāo)target的屬性值,從而實(shí)現(xiàn)動(dòng)畫效果的。

至此,ObjectAnimator動(dòng)畫執(zhí)行的過程已經(jīng)全部完畢。

4.結(jié)尾

最后我們回到博客開頭我提到的想法,就是如果在ObjectAnimator動(dòng)畫結(jié)束回調(diào)函數(shù)onAnimationEnd中重新start此動(dòng)畫,是否能達(dá)到循環(huán)播放動(dòng)畫呢?我們仔細(xì)分析ValueAnimator的endAnimation(AnimationHandler handler)方法

// ValueAnimator
protected void endAnimation(AnimationHandler handler) {
    // 清空AnimationHandler用到的隊(duì)列
    handler.mAnimations.remove(this);
    handler.mPendingAnimations.remove(this);
    handler.mDelayedAnims.remove(this);
    mPlayingState = STOPPED;
    mPaused = false;
    if ((mStarted || mRunning) && mListeners != null) {
        if (!mRunning) {
            // If it's not yet running, then start listeners weren't called. Call them now.
            notifyStartListeners();
         }
        ArrayList<AnimatorListener> tmpListeners =
                (ArrayList<AnimatorListener>) mListeners.clone();
        int numListeners = tmpListeners.size();
        for (int i = 0; i < numListeners; ++i) {
            // 回調(diào)AnimatorListener.onAnimationEnd,在我的想法中就是在這里重新start動(dòng)畫
            tmpListeners.get(i).onAnimationEnd(this);
        }
    }
    // 這些標(biāo)志位都置為false
    mRunning = false;
    mStarted = false;
    mStartListenersCalled = false;
    mPlayingBackwards = false;
    mReversing = false;
    mCurrentIteration = 0;
    if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
        Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(),
                System.identityHashCode(this));
    }
}

通過分析,原來在AnimatorListener.onAnimationEnd回調(diào)中即使重新start動(dòng)畫,由于當(dāng)時(shí)mStartListenersCalled為true,所以再次start動(dòng)畫時(shí)不會(huì)在回調(diào)AnimatorListener.onAnimationStart方法,在后面又把mRunning和mStarted兩個(gè)標(biāo)志位改為false,導(dǎo)致在第二次start的動(dòng)畫結(jié)束時(shí)調(diào)用endAnimation方法中因?yàn)檫@兩個(gè)標(biāo)志位都是為false,所以不會(huì)再次回調(diào)AnimatorListener.onAnimationEnd方法,也就是說重新start起來的動(dòng)畫執(zhí)行完畢就不會(huì)再此觸發(fā)start動(dòng)作,所以驗(yàn)證博客開頭代碼的實(shí)驗(yàn)效果。

這篇博客已經(jīng)完畢,非常感謝您對(duì)本篇的關(guān)注,希望能對(duì)您了解ObjectAnimator屬性動(dòng)畫的實(shí)現(xiàn)有幫助,要是有不足之處歡迎指正,我們相互討論學(xué)習(xí)!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容