編輯:關於Android編程
Android開發的過程中經常要用到屬性動畫,經常都是網上扒下來看下怎麼用,但是經常不知道為什麼要這麼用,手一哆嗦一不小心就點到源碼裡面去了。我們就來看看Android屬性動畫ValueAnimator類源碼的簡單實現,從而對ValueAnimator類有個大概的了解。
在Android開發過程中做動畫效果的時候用到ValueAnimator的時候最簡單的方法我們是這麼干的
// ValueAnimator
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 200, 100);
valueAnimator.setDuration(3000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int currentValue = (Integer) animation.getAnimatedValue();
setX(currentValue);
}
});
valueAnimator.start();
看到了ValueAnimator的簡單使用那就得看下裡面是怎麼實現的了。ValueAnimator的源碼准備從兩個地方入手。
1. ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 200, 100);這裡干了些啥。
2. valueAnimator.start();裡面都干了些啥子東西。在start的分析過程中同時可以看到onAnimationUpdate回調函數的調用時機。
為了分析ValueAnimator.ofInt()函數裡面的實現過程,得先簡單的知道下面的類圖關系。
准備分兩個部分來看這個類圖。
1. PropertyValuesHolder部分:PropertyValuesHolder有好幾個子類,這裡只是列出了四個子類IntPropertyValuesHolder,FloatPropertyValuesHolder,MultiFloatValuesHolder,MultiIntValuesHolder。從類圖中還可以看到PropertyValuesHolder 到 Keyframes的關聯關系。從源碼上面也可以看到PropertyValuesHolder類裡面有Keyframes類型的成員變量mKeyframes(IntPropertyValuesHolderd裡面對應的是Keyframes.IntKeyframes類型的mIntKeyframes變量),這裡PropertyValuesHolder裡面對於mKeyframes的成員變量只是關心setEvaluator(TypeEvaluator evaluator); getType(); getValue(float fraction); invalidateCache(); getKeyframes();這幾個方法。
2. Keyframes部分:先看類圖的右下部分Keyframe類是一個抽象類有三個子類ObjectKeyframe,IntKeyframe,FloatKeyframe。Keyframe類表示動畫的關鍵時刻包括關鍵時間和該時間點對應的值以及插值器等。再來看Keyframes部分類圖中KeyframeSet實現了Keyframes,所以重心點在KeyframeSet,從上面的類圖間的關系看到KeyframeSet關聯了Keyframe,源碼上面也可以看到KeyframeSet裡面有一個Keyframe的成員變量mKeyframes 的List。
從整體上來看這個類圖PropertyValuesHolder是最外層的類他關心的方法只是setEvaluator(TypeEvaluator evaluator); getType(); getValue(float fraction); invalidateCache(); getKeyframes(); 那就得來看每個方法的具體實現了,從類圖可以看出這些方法的具體實現都是在KeyframeSet類以及KeyframeSet類的子類中實現的。這裡我們就挑一個函數來分析看KeyframeSet類裡面的getValue(float fraction);的具體實現。
在看這個函數的具體實現之前我們先得知道兩個東西
1. Interpolator 時間插值器,根據時間流逝的百分比計算出當前屬性值改變的百分比。定義動畫變換的速度。能夠實現alpha/scale/translate/rotate動畫的加速、減速和重復等。Interpolator類其實是一個空接口,繼承自TimeInterpolator,TimeInterpolator時間插值器允許動畫進行非線性運動變換,如加速和限速等,該接口中只有接口中有一個方法 float getInterpolation(float input)這個方法。傳入的值是一個0.0~1.0的值,返回值可以小於0.0也可以大於1.0。
2. TypeEvaluator 估值器,根據當前屬性改變的百分比來計算改變後的屬性值。系統給我們提供三種插值器IntEvaluator:針對整型屬性,FloatEvaluator:針對浮點型屬性,ArgbEvaluator:針對Color屬性。如果還需要其他類型的插值器就得自己去自定義了。看看IntEvaluator的源碼是怎麼實現的。
/**
* This evaluator can be used to perform type interpolation between int
values.
*/
public class IntEvaluator implements TypeEvaluator {
/**
* This function returns the result of linearly interpolating the start and end values, with
* fraction
representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: result = x0 + t * (v1 - v0)
,
* where x0
is startValue
, x1
is endValue
,
* and t
is fraction
.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue The start value; should be of type int
or
* Integer
* @param endValue The end value; should be of type int
or Integer
* @return A linear interpolation between the start and end values, given the
* fraction
parameter.
*/
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}
好像也沒什麼東西樣的就一個函數evaluate,我們關注返回值得類型是int型的數據。同時我們還得關注函數的三個參數startValue,endValue一個開始的值一個結束的值這個好說,第一個參數fraction是開始值和結束值變化的百分比。
這下可以安心的來看KeyframeSet 類的 getValue(float fraction)了,源碼如下
/**
* Gets the animated value, given the elapsed fraction of the animation (interpolated by the
* animation's interpolator) and the evaluator used to calculate in-between values. This
* function maps the input fraction to the appropriate keyframe interval and a fraction
* between them and returns the interpolated value. Note that the input fraction may fall
* outside the [0-1] bounds, if the animation's interpolator made that happen (e.g., a
* spring interpolation that might send the fraction past 1.0). We handle this situation by
* just using the two keyframes at the appropriate end when the value is outside those bounds.
*
* @param fraction The elapsed fraction of the animation
* @return The animated value.
*/
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());
}
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;
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();
}
從函數的介紹我們也可以看到這個函數的參數float fraction已經是通過插值器轉換之後的屬性百分比的值了可能是小於0的也可能是大於1的。這個函數的作用簡單來說就是通過傳入經過插值器轉換之後的值(您也可以把他理解成屬性變化的百分比)得到具體屬性的值,最後給到onAnimationUpdate回調函數。
15-21行,如果動畫過程中我們只關注兩個關鍵時間點(動畫的開始點,動畫的結束點)直接通過估值器去計算得到屬性值。
22-33行,如果fraction<=0,只有在第一個時間點和第二個時間點的時候才會有fraction<=0的情況這個好理解哦。第29行intervalFraction表示兩個點之間的百分比這個好說吧,我們知道了兩個點的值,以及這兩個點過程中的百分比 把這三個參數給估值器得到屬性的具體值。
33-44行,如果fraction>=1,只有在倒數第二個時間點和倒數第一個時間才有這樣的情況,同樣得到intervalFraction然後通過估值器去計算。
46-61行,有多個時間點,先判斷fraction落在哪個時間段。然後得到intervalFraction,給估值器去計算。
總的來說這個函數就是你給我一個插值器變化之後的值,我就給你這個時間點的屬性值。
IntKeyframes裡面對應的是public int getIntValue(float fraction)具體的實現也是一樣的。
終於到了ValueAnimator.ofInt()的過程分析了,這裡就 以int類型來分析,float,rgb的類型都是一樣的道理得到的。
public static ValueAnimator ofInt(int... values) {
ValueAnimator anim = new ValueAnimator();
anim.setIntValues(values);
return anim;
}
是個static函數 參數是可變的可以傳多個int型的數據。會通過出入的int數據去確定動畫變化過程中的關鍵時間點和該時間點對應的數據。這個函數做的事情就是new了一個ValueAnimator對象anim 繼續調用了anim.setIntValues。然後把這個對象返回了。繼續看setIntValues()函數。
public void setIntValues(int... values) {
if (values == null || values.length == 0) {
return;
}
if (mValues == null || mValues.length == 0) {
setValues(PropertyValuesHolder.ofInt("", values));
} else {
PropertyValuesHolder valuesHolder = mValues[0];
valuesHolder.setIntValues(values);
}
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
5-7行, 如果mValues(PropertyValuesHolder[] mValues)之前沒有被設置過則調用setValues函數這個函數的參數我們等下再分析。如果之前已經設置了則改變第一個PropertyValuesHolder的值。這裡我們先看setValues函數的內容然後在看setValues函數的參數。
public void setValues(PropertyValuesHolder... values) {
int numValues = values.length;
mValues = values;
mValuesMap = new HashMap(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;
}
就賦了三個變量的值了,這個好理解mValuesMap key對應的是屬性的字符串value對應的是PropertyValuesHolder對象。
接下來該看下setValues函數的參數是怎麼得到的了哦。跟蹤PropertyValuesHolder.ofInt(“”, values)函數。
public static PropertyValuesHolder ofInt(String propertyName, int... values) {
return new IntPropertyValuesHolder(propertyName, values);
}
看的出來構造的是IntPropertyValuesHolder對於(PropertyValuesHolder的子類,因為這裡我們分析的是int型的數據這條主線)
IntPropertyValuesHolder類的構造函數
public IntPropertyValuesHolder(String propertyName, int... values) {
super(propertyName);
setIntValues(values);
}
IntPropertyValuesHolder類的setIntValues函數。
@Override
public void setIntValues(int... values) {
super.setIntValues(values);
mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes;
}
直接看super.setIntValues(values) PropertyValuesHolder類中setIntValues函數。
public void setIntValues(int... values) {
mValueType = int.class;
mKeyframes = KeyframeSet.ofInt(values);
}
設置了mValueType的類型是int.class。給mKeyframes賦值了。分析類圖的時候我們也知道mKeyframes變量是PropertyValuesHolder重要的部分。我們是要通過mKeyframes去獲取動畫變化過程中各個時間點的屬性值的。
繼續跟蹤KeyframeSet類中ofInt函數。
public static KeyframeSet ofInt(int... values) {
int numKeyframes = values.length;
IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];
if (numKeyframes == 1) {
keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
} else {
keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);
for (int i = 1; i < numKeyframes; ++i) {
keyframes[i] =
(IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
}
}
return new IntKeyframeSet(keyframes);
}
第3行,new了一個IntKeyframe數組(IntKeyframe是Keyframe的子類,在分析類圖的時候說過Keyframe裡面保存的是關鍵時間點的時間和該時間對應的數據)
第4-7行,如果參數只有一個那麼有兩個關鍵時間點一個是(0, 0)一個是(1, value)。這個也好理解就是一個開始點一個結束點。
第7-12行,如果參數的個數不止一個的時候,根據參數的個數去平分時間點,比如ofInt的初始值是 ofInt(100, 200, 300)那麼我們得到的時間點就是(0, 100), (1/2, 200),(1, 300)。三個關鍵的時間點。這個也好理解哦。
14行,返回了IntKeyframeSet的對象並且把keyframes的數據傳進去了,裡面做的事情還是挺簡單的我們就不進去了看了。這樣整個ValueAnimator.ofInt()部分就走到頭了。
總結ValueAnimator.ofInt()做的事情。就是得到了PropertyValuesHolder的對象(不管具體的是IntPropertyValuesHolder還是FloatPropertyValuesHolder得對象都是他的子類)。並且把這個對象賦值給了ValueAnimator裡面的mValues,和mValuesMap(key是屬性字符串,value才是PropertyValuesHolder的對象)。PropertyValuesHolder裡面有一個Keyframes mKeyframes的對象,同樣Keyframes裡面有 mKeyframes的List(時間點和該時間點對應的屬性值的List)。PropertyValuesHolder裡面mKeyframes對象的每個操作都是在mKeyframes的List基礎之上進行的。為了更好的理解這一部分我們先提前看下是怎麼通過PropertyValuesHolder去獲取動畫過程中各個時間點對應的屬性值的,直接跳到看ValueAnimator animateValue()函數,這個函數在下文講ValueAnimator start()函數的會調用到,會把整個串起來的,這裡先提前看下。
@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);
}
}
}
第一行先通過插值器吧時間進度按照某種規律(勻速,加速等等)轉換了一下。然後去遍歷mValues,通過前面的分析我們知道mValues是PropertyValuesHolder的數組我們分析過的哦,遍歷去調用每個PropertyValuesHolder的calculateValue函數,進去看下了。
PropertyValuesHolder類裡面的calculateValue函數。
void calculateValue(float fraction) {
Object value = mKeyframes.getValue(fraction);
mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
}
用到了PropertyValuesHolder裡面的Keyframes mKeyframes了吧,又調用了Keyframes的getValue函數。在跟進去比如我們調用的是ValueAnimator.ofInt()構造的ValueAnimator,那麼這裡會走到IntKeyframeSet類的getValue函數
@Override
public Object getValue(float fraction) {
return getIntValue(fraction);
}
直接調用的是getIntValue,就是我們在分析類圖的時候分析過的函數把(KeyframeSet getValue()一樣的意思),這樣就得到了動畫過程中時間點對應的值了。這個值就直接給了onAnimationUpdate回調函數了。
其實吧,前面干的事情都還沒進入到動畫的過程,還在是在賦值做一些動畫的准備,下面開始動畫過程的分析。入口函數ValueAnimator 的start函數。
ValueAnimator類裡面的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;
mPlayingBackwards = playBackwards;
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;
mStarted = true;
mStartedDelay = false;
mPaused = false;
updateScaledDuration(); // in case the scale factor has changed since creation time
AnimationHandler animationHandler = getOrCreateAnimationHandler();
animationHandler.mPendingAnimations.add(this);
if (mStartDelay == 0) {
// This sets the initial value of the animation, prior to actually starting it running
if (prevPlayingState != SEEKED) {
setCurrentPlayTime(0);
}
mPlayingState = STOPPED;
mRunning = true;
notifyStartListeners();
}
animationHandler.start();
}
參數表示動畫是否反向播放。
mReversing:表示當前是否是從後面開始播放的。
mPlayingBackwards:表示當前動畫的這次播放是否是在反向播放。有這樣的情況比如動畫的count是2,第一次正向播放第二次方向播放。
mSeekFraction:表示動畫要跳到的播放時間點(0~1)setCurrentFraction()函數裡面設置。
mCurrentIteration:表示當前動畫是第幾次播放。
7-18行,如果動畫是要反向播放的並且我們調用了setCurrentFraction(0函數設置了mSeekFraction,這個if裡面做的事情就是去設置動畫是從哪個點去播放的計算出mSeekFraction和mCurrentIteration的值,舉個例子,我們設置了動畫的播放次數是5次(setRepeatCount(5)),並且調用了setCurrentFraction(1.3),。那麼經過setCurrentFraction(1.3)函數之後mSeekFraction = 0.3,mCurrentIteration = 1。這段代碼會做的事情就是因為我們的動畫是要反向播放的計算之後mSeekFraction = 0.7,mCurrentIteration = 4。因為要反向播放。
19-28行,如果當前的播放模式是REVERSE的模式,進一步去確定動畫這次播放是順序播放還是反向播放。
35,36行,獲取了AnimationHandler的實例,並且把當前動畫加入到了pending animator裡面去了。
37-45行,動畫的播放是否要延時的處理。
46行,調用了animationHandler的start函數。
那就直接去看AnimationHandler類中start函數
/**
* Start animating on the next frame.
*/
public void start() {
scheduleAnimation();
}
private void scheduleAnimation() {
if (!mAnimationScheduled) {
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
mAnimationScheduled = true;
}
}
mAnimate的Runable
// Called by the Choreographer.
final Runnable mAnimate = new Runnable() {
@Override
public void run() {
mAnimationScheduled = false;
doAnimationFrame(mChoreographer.getFrameTime());
}
};
AnimationHandler類中的doAnimationFrame函數
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.
while (mPendingAnimations.size() > 0) {
ArrayList pendingCopy =
(ArrayList) mPendingAnimations.clone();
mPendingAnimations.clear();
int count = pendingCopy.size();
for (int i = 0; i < count; ++i) {
ValueAnimator anim = pendingCopy.get(i);
// If the animation has a startDelay, place it on the delayed list
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();
for (int i = 0; i < numDelayedAnims; ++i) {
ValueAnimator anim = mDelayedAnims.get(i);
if (anim.delayedAnimationFrame(frameTime)) {
mReadyAnims.add(anim);
}
}
int numReadyAnims = mReadyAnims.size();
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.clear();
}
// Now process all active animations. The return value from animationFrame()
// tells the handler whether it should now be ended
int numAnims = mAnimations.size();
for (int i = 0; i < numAnims; ++i) {
mTmpAnimations.add(mAnimations.get(i));
}
for (int i = 0; i < numAnims; ++i) {
ValueAnimator anim = mTmpAnimations.get(i);
if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) {
mEndingAnims.add(anim);
}
}
mTmpAnimations.clear();
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.
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.
if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
scheduleAnimation();
}
}
9-23行,循環去遍歷mPendingAnimations的list,拿到mPendingAnimations裡面的每個動畫實例ValueAnimator。如果這個動畫不用延時播放直接調用ValueAnimator裡面的anim.startAnimation(this);並且把當前的對象傳遞進去了,如果要延時播放就把這個動畫加入到延時的List mDelayedAnims當中去。 回過頭看下anim.startAnimation(this);做的事情
ValueAnimator類中的startAnimation函數。
private void startAnimation(AnimationHandler handler) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
System.identityHashCode(this));
}
initAnimation();
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();
}
}
先調用了initAnimation函數,給ValueAnimator裡面的每個PropertyValuesHolder的Keyframes設置了估值器。PropertyValuesHolder,Keyframes前面都有提到過。然後又把這個動畫的對象加入到了AnimationHandler裡面的mAnimations list裡面去了。
在回頭繼續AnimationHandler類中的doAnimationFrame函數
27-33行,遍歷有延時的動畫,如果延時時間到了就把動畫加入到mReadyAnims裡面去。
34-43行,去遍歷mReadyAnims裡面的動畫,調用anim.startAnimation(this);加入到mAnimations裡面去,並且從mDelayedAnims裡面移除掉。
47-56行,遍歷mAnimations裡面的動畫,調用anim.doAnimationFrame(frameTime)函數開始具體的動畫邏輯了,如果動畫結束了就把該動畫加入到mEndingAnims當中去,進去看下doAnimationFrame函數的具體動作了
ValueAnimator類中的doAnimationFrame函數。
final boolean doAnimationFrame(long frameTime) {
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);
}
設置了狀態,記錄了mStartTime的時間。接著調用了animationFrame函數。
boolean animationFrame(long currentTime) {
boolean done = false;
switch (mPlayingState) {
case RUNNING:
case SEEKED:
float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
if (mDuration == 0 && mRepeatCount != INFINITE) {
// Skip to the end
mCurrentIteration = mRepeatCount;
if (!mReversing) {
mPlayingBackwards = false;
}
}
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);
}
}
if (mPlayingBackwards) {
fraction = 1f - fraction;
}
animateValue(fraction);
break;
}
return done;
}
如果設置了duration,計算得到時間的進度,比如fraction = 1.3;說明動畫是在第二次播放了。如果設置了播放模式是REVERSE。fraction = 1 - 0.3 = 0.7。這個好理解吧。 接著調用animateValue函數。done返回值表示動畫是否完成了。
接著看下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計算得到當前的屬性值,這個上文當中有提到過,第一部分ValueAnimator.ofInt()最後講到的正好和這裡接上了
繼續回到AnimationHandler類中的doAnimationFrame函數
58-63行,遍歷mEndingAnims,那個動畫結束了就設置一些值,並且回調動畫結束的函數。
70-72行,如果還有動畫有調用了scheduleAnimation函數,又重復了上面的邏輯了。知道所有的動畫都播放完。
總結第二部分,動畫的具體實現是靠AnimationHandler來驅動,AnimationHandler裡面也是各種list的記錄各個狀態的動畫。
流水賬終於記完了,下一篇准備看看ObjectAnimator源碼的簡單實現。
1、本地html與本地html裡的js交互2、本地html與本地js交互3、網絡html與網絡js交互4、網絡html與本地js交互5、各個情況動態添加js以上5點都可以
好了,跟隨潮流,還是先看下效果,不然可能都沒人想看下去了(不會看到效果後不想看了吧O(∩_∩)O~)嗯,就是讓左面板在主面板的下面,所以我們自定義的控件S
1.Device tree設備樹概述設備樹包含DTC(device treecompiler),DTS(device treesource和DTB(device tree
public class TvControlActivity extends Activity { private TvControlActivity tvCont