先来看一段代码,如下:
ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animator) {
//获得当前动画的进度值,整型,1-100之间
int currentValue = (Integer)animator.getAnimatedValue();
}
});
valueAnimator.setDuration(450).start();
当时写这段代码是为了慢慢的从屏幕的底部展示一个不断上升的View,当然这只是其中部分代码,当时不知道整个流程是怎样的,getAnimatedValue 是怎么获取值的,今天有时间就把源码撸了遍,然后记录下来。
上面的代码,我们通过ValueAnimator.ofInt方法来设置动画的起始和停止值,如果只设置一个那么默认的起始值为0(这个等后面的源码来分析),
通过设置更新进度的监听来获取当前的动画进行的进度(1~100);
valueAnimator.start()来执行动画,如果不设置动画的执行时间,那么默认的是300ms,见源码分析。
先来看看ofInt的代码:
public static ValueAnimator ofInt(int... values) {
ValueAnimator anim = new ValueAnimator();
anim.setIntValues(values);
return anim;
}
很简单,就是new 一个ValueAnimator,并把我们设置的值传递进去,然后返回一个ValueAnimator对象,setIntValues代码如下:
public void setIntValues(int... values) {
if (values == null || values.length == 0) {
return;
}
if (mValues == null || mValues.length == 0) {
setValues(new PropertyValuesHolder[]{PropertyValuesHolder.ofInt("", values)});
} else {
PropertyValuesHolder valuesHolder = mValues[0];
valuesHolder.setIntValues(values);
}
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
第一条判断条件很简单,传递进来的值为空或者数据为空则return;
第二个条件中mValues 指的是啥呢? PropertyValuesHolder[] mValues; //这个类表示传递数据的属性和值,即是int型、float还是其他什么类型的值;
一开始 mValues 是null,所以条件成立,执行 setValues 方法,传入 setValues 中的参数 由 PropertyValuesHolder.ofInt 方法获取,
public static PropertyValuesHolder ofInt(String propertyName, int... values) {
return new IntPropertyValuesHolder(propertyName, values);
}
该方法返回IntPropertyValuesHolder 对象,IntPropertyValuesHolder 是 PropertyValuesHolder 的静态内部类并且继承自 PropertyValuesHolder ,
public IntPropertyValuesHolder(Property property, int... values) {
super(property);
setIntValues(values);
if (property instanceof IntProperty) {
mIntProperty = (IntProperty) mProperty;
}
}
这个我们只要看看setIntValues(values);这个我们只要看看setIntValues(values);
@Override
public void setIntValues(int… values) {
super.setIntValues(values);
mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
}
父类的 setIntValues 都做了哪些事呢?
public void setIntValues(int... values) {
mValueType = int.class;
mKeyframeSet = KeyframeSet.ofInt(values);
}
为变量 mValueType 赋值为 int.class; KeyframeSet.ofInt(values)这个方法就是执行动画赋值操作,
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);
}
numKeyframes 表示传递给动画值的个数,keyframes 的最小个数是2;
看看Keyframe.ofInt的两个方法:
IntKeyframe(float fraction, int value) {
mFraction = fraction;
mValue = value;
mValueType = int.class;
mHasValue = true;
}
IntKeyframe(float fraction) {
mFraction = fraction;
mValueType = int.class;
}
可以看出,如果传递过来的数值个数是1的话,keyframes[0] 只设置了 mFraction 而没有设置 mValue;
如果个数大于等于2的话,都会设置值进去,然后返回一个 IntKeyframeSet 对象:
public IntKeyframeSet(IntKeyframe... keyframes) {
super(keyframes);
}
IntKeyframeSet 是 KeyframeSet 的子类,看看 KeyframeSet 中的构造方法:
public KeyframeSet(Keyframe... keyframes) {
mNumKeyframes = keyframes.length;
mKeyframes = new ArrayList<Keyframe>();
mKeyframes.addAll(Arrays.asList(keyframes));
mFirstKeyframe = mKeyframes.get(0);
mLastKeyframe = mKeyframes.get(mNumKeyframes - 1);
mInterpolator = mLastKeyframe.getInterpolator();
}
mNumKeyframes 表示 Keyframe的个数,也就是传递的int型数据的个数,我们这里传递的是(1,100),所以 mNumKeyframes =2;
mKeyframes将之前设置数据的 Keyframe 放入集合,mFirstKeyframe表示第一个数据, mLastKeyframe 表示最后一个数据;
mInterpolator 表示最后一个数据所传递的插值器;
再回到 IntPropertyValuesHolder 的 setIntValues方法
@Override
public void setIntValues(int... values) {
super.setIntValues(values);
mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
}
最后 将获取的数据 赋值给 mIntKeyframeSet;
再回过头来看看 ValueAnimator 的 setValues方法:
public void setValues(PropertyValuesHolder... values) {
int numValues = values.length;
mValues = values;
mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
for (int i = 0; i < numValues; ++i) {
PropertyValuesHolder valuesHolder = (PropertyValuesHolder) values[i];
mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
}
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
参数就是之前通过一系列的步骤获取的;这个方法很简单,就是 将值 赋给 mValues,然后通过键值对方式放入map中。
好啦,到现在,设置动画值的操作终于走完啦,下面来看看start方法啦:
@Override
public void start() {
start(false);
}
// playBackwards 表示是否需要反过来执行动画,默认为false,但也可以 reverse 方法来设置;
//这个方法是运行在调用这个方法的线程中。
private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
mPlayingBackwards = playBackwards;
mCurrentIteration = 0;
mPlayingState = STOPPED;
mStarted = true;
mStartedDelay = false;
sPendingAnimations.get().add(this);
if (mStartDelay == 0) {
// This sets the initial value of the animation, prior to actually starting it running
setCurrentPlayTime(getCurrentPlayTime());
mPlayingState = STOPPED;
mRunning = true;
if (mListeners != null) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
int numListeners = tmpListeners.size();
for (int i = 0; i < numListeners; ++i) {
tmpListeners.get(i).onAnimationStart(this);
}
}
}
AnimationHandler animationHandler = sAnimationHandler.get();
if (animationHandler == null) {
animationHandler = new AnimationHandler();
sAnimationHandler.set(animationHandler);
}
animationHandler.sendEmptyMessage(ANIMATION_START);
}
开始是设置几个状态值:
mPlayingState = STOPPED; //暂停状态
mStarted = true;
mStartedDelay = false;
sPendingAnimations.get().add(this);
//mPlayingState 有以下3种取值
static final int STOPPED = 0; // Not yet playing
static final int RUNNING = 1; // Playing normally
static final int SEEKED = 2; // Seeked to some time value
mStartDelay 如果不设置延迟的话,默认值是0,然后走到 setCurrentPlayTime(getCurrentPlayTime()),getCurrentPlayTime就是获取当前的系统时间
public long getCurrentPlayTime() {
if (!mInitialized || mPlayingState == STOPPED) {
return 0;
}
return AnimationUtils.currentAnimationTimeMillis() - mStartTime;
}
上面代码的逻辑很简单,就是 未初始化或者 mPlayingState 为STOPPED的时候返回0,否则返回 系统当前时间减去动画开始时间;
public void setCurrentPlayTime(long playTime) {
initAnimation();
long currentTime = AnimationUtils.currentAnimationTimeMillis();
if (mPlayingState != RUNNING) {
mSeekTime = playTime;
mPlayingState = SEEKED;
}
mStartTime = currentTime - playTime;
animationFrame(currentTime);
}
首先 初始化动画,
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].init();
}
mInitialized = true;
}
}
由于开始是 mInitialized 是false且mValues之前也赋值过,所以走到 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
mKeyframeSet.setEvaluator(mEvaluator);
}
}
这个方法就是为了设置 Evaluator的,由于我们传进来的数据类型是 int.class,所以 mEvaluator = null;
在 initAnimation 方法的最后 mInitialized = true; 回到 setCurrentPlayTime 方法中,由于 开始 mPlayingState = STOPPED, 满足条件,
执行 mSeekTime = playTime; mPlayingState = SEEKED; mStartTime = currentTime - playTime; 最后执行 animationFrame方法:
boolean animationFrame(long currentTime) {
boolean done = false;
if (mPlayingState == STOPPED) {
mPlayingState = RUNNING;
if (mSeekTime < 0) {
mStartTime = currentTime;
} else {
mStartTime = currentTime - mSeekTime;
// Now that we're playing, reset the seek time
mSeekTime = -1;
}
}
switch (mPlayingState) {
case RUNNING:
case SEEKED:
float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
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 ? false : true;
}
mCurrentIteration += (int)fraction;
fraction = fraction % 1f;
mStartTime += mDuration;
} else {
done = true;
fraction = Math.min(fraction, 1.0f);
}
}
if (mPlayingBackwards) {
fraction = 1f - fraction;
}
animateValue(fraction);
break;
}
return done;
}
这就是执行动画的方法, 开始 done = false;由于 mPlayingState 之前赋值为 SEEKED,所以 走到 case 为 SEEKED 的代码块中,第一句中,由于我们设置了 动画的持续时间
所以 mDuration 大于0,如果不设置, mDuration默认为 300ms, //private long mDuration = 300;
从上面的 setCurrentPlayTime 方法中,我们可以知道 currentTime = mStartTime, 所以 执行到 if (mPlayingBackwards) ,而 mPlayingBackwards=false,直接执行 animateValue(fraction)
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);
}
}
}
首先通过插值器获取 fraction, 默认的插值器是 AccelerateDecelerateInterpolator,这个getInterpolation代码很简单,就是通过余弦函数来取值,不说,跳过去;
看 mValues 的calculateValue 方法:
void calculateValue(float fraction) {
mAnimatedValue = mKeyframeSet.getValue(fraction);
}
getValue调用的是 IntKeyframeSet中的 getIntValue方法:
public int getIntValue(float fraction) {
if (mNumKeyframes == 2) {
if (firstTime) {
firstTime = false;
firstValue = ((IntKeyframe) mKeyframes.get(0)).getIntValue();
lastValue = ((IntKeyframe) mKeyframes.get(1)).getIntValue();
deltaValue = lastValue - firstValue;
}
if (mInterpolator != null) {
fraction = mInterpolator.getInterpolation(fraction);
}
if (mEvaluator == null) {
return firstValue + (int)(fraction * deltaValue);
} else {
return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).intValue();
}
}
......
}
这个代码很长,截取部分代码, firstValue就是我们之前传入的1, lastValue是 100;mEvaluator之前也判断了是null,然后return 返回值;
回到 animateValue 方法,设置了 mAnimatedValue 值后,通过我们 add的 UpdateListeners 的 onAnimationUpdate 回调中 就可以通过 animator.getAnimatedValue() 来获取当前的进度值啦,
这样就完啦?。。。。没有的时,这才执行了部分进度,还没完成呢,start(boolean )方法还没执行完
该代码还剩下面的 :
......
AnimationHandler animationHandler = sAnimationHandler.get();
if (animationHandler == null) {
animationHandler = new AnimationHandler();
sAnimationHandler.set(animationHandler);
}
animationHandler.sendEmptyMessage(ANIMATION_START);
就是通过handler来循环执行动画,
public void handleMessage(Message msg) {
boolean callAgain = true;
ArrayList<ValueAnimator> animations = sAnimations.get();
ArrayList<ValueAnimator> delayedAnims = sDelayedAnims.get();
switch (msg.what) {
// TODO: should we avoid sending frame message when starting if we
// were already running?
case ANIMATION_START:
ArrayList<ValueAnimator> pendingAnimations = sPendingAnimations.get();
if (animations.size() > 0 || delayedAnims.size() > 0) {
callAgain = false;
}
while (pendingAnimations.size() > 0) {
ArrayList<ValueAnimator> pendingCopy =
(ArrayList<ValueAnimator>) pendingAnimations.clone();
pendingAnimations.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();
} else {
delayedAnims.add(anim);
}
}
}
// fall through to process first frame of new animations
case ANIMATION_FRAME:
......
......
int numAnims = animations.size();
int i = 0;
while (i < numAnims) {
ValueAnimator anim = animations.get(i);
if (anim.animationFrame(currentTime)) {
endingAnims.add(anim);
}
if (animations.size() == numAnims) {
++i;
} else {
--numAnims;
endingAnims.remove(anim);
}
}
if (endingAnims.size() > 0) {
for (i = 0; i < endingAnims.size(); ++i) {
endingAnims.get(i).endAnimation();
}
endingAnims.clear();
}
// If there are still active or delayed animations, call the handler again
// after the frameDelay
if (callAgain && (!animations.isEmpty() || !delayedAnims.isEmpty())) {
sendEmptyMessageDelayed(ANIMATION_FRAME, Math.max(0, sFrameDelay -
(AnimationUtils.currentAnimationTimeMillis() - currentTime)));
}
break;
}
}
传过来的 msg.what = ANIMATION_START, pendingAnimations 之前对 sPendingAnimations 赋值了, 所以pendingAnimations size >0; 执行到 anim.startAnimation()
private void startAnimation() {
initAnimation();
sAnimations.get().add(this);
if (mStartDelay > 0 && mListeners != null) {
// Listeners were already notified in start() if startDelay is 0; this is
// just for delayed animations
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
int numListeners = tmpListeners.size();
for (int i = 0; i < numListeners; ++i) {
tmpListeners.get(i).onAnimationStart(this);
}
}
}
initAnimation()方法之前讲过,因为 mInitialized 之前被赋值为true,所以 直接退出该方法;sAnimations add当前对象即 ValueAnimator;mStartDelay = 0,该方法执行完;
因为 pendingAnimations.clear(); 所以 while 循环结束;
执行到 case ANIMATION_FRAME: int numAnims = animations.size(); 之前 sAnimations add 一个ValueAnimator,所以 animationFrame,这个方法之前讲过,知道 时间执行完后
把该方法的部分代码拿过来,如下,
case SEEKED:
float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
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 ? false : true;
}
mCurrentIteration += (int)fraction;
fraction = fraction % 1f;
mStartTime += mDuration;
} else {
done = true;
fraction = Math.min(fraction, 1.0f);
}
}
if (mPlayingBackwards) {
fraction = 1f - fraction;
}
animateValue(fraction);
break;
}
只有 动画执行时间 大于 mDuration, fraction才会>=1,因为不需要循环,所以 done = true; animateValue 之前说过就是设置当前进度,然后通过回调获取该进度;
animationFrame 方法返回ture, 所以会调用 刚才handler中的handMessage 方法 ndingAnims.add(anim); 最后 执行 endAnimation来结束动画,并资源释放。
如果动画执行时间还没到,会 sendEmptyMessageDelayed 来循环执行。
好啦,整个流程讲完啦。