Android3.0之前,Android提供了几种动画类型:View Animation、Drawable Animation、Property Animation。
View Animation:比较简单。不过只能支持简单的缩放、平移、旋转、透明度几个基本的动画,且具有一定的局限性。
例如:希望View有一个颜色的切换动画,比如希望使用3D旋转动画,希望动画停止时,View的位置就是当前位置。这些View Animation都无法做到。
Google在Android3.0提供了属性动画。
兼容android3.0以下
上图是属性动画的整体设计,Animator通过PropertyValuesHolder来更新对象的目标属性,如果用户没有设置目标属性的Property对象,那么会通过反射的形式调用目标属性的setter方法来更新属性值。否则,通过Property的set方法来设置属性。这个属性值则通过KeyFrameSet的计算得到,而KeyFrameSet又是时间插值器和类型估值器来计算,在动画执行过程中不断计算当前时刻目标属性的值,然后更新属性值来达到动画效果。
先了解一下属性动画中一些核心的类以及它们的作用。
ValueAnimator:该类是Animator的子类,实现了动画的整个处理逻辑,也是属性动画最为核心的类。
ObjectAnimator:对象属性动画的操作类,继承自ValueAnimator,通过该类使用动画的形式操作对象的属性。
TimeInterpolator: 时间插值器,它的作用是根据时间流逝的百分比来计算出当前属性值改变的百分比,系统预设的有线性插值器(LinearInterpolator)、加速减速插值器(AccelerateDecelerateInterpolator)和减速插值器(DecelerateInterpolator)等。
TypeEvaluator:TypeEvaluator的中文翻译为类型估值算法,它的作用是根据当前属性改变的百分比来计算改变后的属性值。系统预置的有针对整型属性(IntEvaluator)、针对浮点型属性(FloatEvaluator)和针对Color属性(ArgbEvaluator)。
Property:属性对象,主要定义了属性的set和get方法。
PropertyValuesHolder:PropertyValuesHolder是持有目标属性Property、setter和getter方法、以及关键帧集合的类。
KeyframeSet:存储一个动画的关键帧 集合。
AnimationProxy:在Android3.0以下使用View的属性动画的辅助类。
示例1:改变一个对象(myObject)的translationY属性,让其沿着y轴向上平移一段距离,该动画在默认时间内完成,动画的完成时间是可以定义的,想要更灵活的效果还可以定义插值器和估值算法,但是一般来说我们不需要自定义,系统已经预置了一些,能够满足常用动画。
ObjectAnimator.ofFloat(myObject, "translationY", -myobject.getHeight()),start();
示例2:改变一个对象的背景色属性,典型的情形是改变View的背景色,下面的动画可以让背景色在3s内实现从0xFFF8080到0xFF8080FF的渐变,并且动画会无限循环而且会有反转效果。
ValueAnimator colorAnim = ObjectAnimator.ofInt(this, "backgroudColor", Color.RED, Color.BLUE);
colorAnim.setDuration(3000);
colorAnim.setEvaluator(new ArgbEvaluator());
colorAnim.setRepeatCount(ValueAnimator.INFINITE);
colorAnim.setRepeatMode(ValueAnimator.REVERSE);
colorAnim.start();
示例3:动画集合,5s内对View的旋转、平移、缩放和透明度都进行改变。
AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(view, "rotationX", 0, 360),
ObjectAnimator.ofFloat(view, "rotationX", 0, 180),
ObjectAnimator.ofFloat(view, "rotation", 0, -90),
ObjectAnimator.ofFloat(view, "translationX", 0, 90),
ObjectAnimator.ofFloat(view, "translationY", 0, 90),
ObjectAnimator.ofFloat(view, "scaleX", 1, 1.5f),
ObjectAnimator.ofFloat(view, "scaleY", 1, 0.5f),
ObjectAnimator.ofFloat(view, "alpha", 1, 0.25f, 1)
);
set.setDuration(5*1000).start();
1.ValueAnimator流程图
2.ObjectAnimator流程图
核心原理分析:
以一个具体的示例作为分析入口:
例如,要对某一个View的x轴缩放到原来的0.3倍,动画执行时间为1s,具体代码如下:
ValueAnimator colorAnimator = ObjectAnimator.ofFloat(this, "scaleX", 0.3f);
colorAnimator.setDuration(3000);
colorAnimator.start();
/**
* Constructs and returns an ObjectAnimator that animates between float values. A single
* value implies that that value is the one being animated to, in which case the start value
* will be derived from the property being animated and the target object when {@link #start()}
* is called for the first time. Two values imply starting and ending values. More than two
* values imply a starting value, values to animate through along the way, and an ending value
* (these values will be distributed evenly across the duration of the animation).
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called <code>setName()</code>, where <code>name</code> is
* the value of the <code>propertyName</code> parameter.
* @param propertyName The name of the property being animated.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
//1.构建动画对象
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
//2. 设置属性值
anim.setFloatValues(values);
return anim;
}
在ObjectAnimator的ofFloat函数中会首先构建属性动画对象,然后根据属性值来初始化各个时间段对应的属性值,这个属性值就是values参数,它是一个可变参数,如果是一个参数,那么改参数是目标值;如果是两个参数,那么一个是起始值,另一个是目标值。我们先看看setFloatValues函数的实现。
/**
* The property/value sets being animated.
*/
#ValueAnimator
PropertyValuesHolder[] mValues;
#ObjectAnimator
@Override
public void setFloatValues(float... values) {
//这个mValues是动画的各个数值的集合,mValues在其父类ValueAnimator中声明。
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
if (mProperty != null) {
setValues(PropertyValuesHolder.ofFloat(mProperty, values));
} else {
setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
}
} else {
super.setFloatValues(values);
}
}
我们看到,setFloatValues出现了一个类PropertyValuesHolder,这个类是该动画库的一个核心类之一,它的作用就是保存属性的名称和它的setter和getter方法,以及它的目标值。我们看看它的一些相关函数。
#PropertyValuesHolder
/**
* This class holds information about a property and the values that that property
* should take on during an animation. PropertyValuesHolder objects can be used to create
* animations with ValueAnimator or ObjectAnimator that operate on several different properties
* in parallel.
*/
public class PropertyValuesHolder implements Cloneable {
//属性名称
String mPropertyName;
//属性对象
protected Property mProperty;
//属性的setter方法
Method mSetter = null;
//属性的getter方法
private Method mGetter = null;
/**
* The type of values supplied. This information is used both in deriving the setter/getter
* functions and in deriving the type of TypeEvaluator.
*/
//属性值的类类型,如float,int等。
Class mValueType;
/**
* The set of keyframes (time/value pairs) that define this animation.
*/
//这里是动画关键帧集合,即在duration时间内的动画帧集合,它保存的是每个时刻该属性对应的值
Keyframes mKeyframes = null;
#PropertyValuesHolder
/**
* Constructs and returns a PropertyValuesHolder with a given property name and
* set of float values.
* @param propertyName The name of the property being animated.
* @param values The values that the named property will animate between.
* @return PropertyValuesHolder The constructed PropertyValuesHolder object.
*/
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
//1.构建的是 FloatPropertyValuesHolder
return new FloatPropertyValuesHolder(propertyName, values);
}
#PropertyValuesHolder
/**
* Constructs and returns a PropertyValuesHolder with a given property and
* set of float values.
* @param property The property being animated. Should not be null.
* @param values The values that the property will animate between.
* @return PropertyValuesHolder The constructed PropertyValuesHolder object.
*/
public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) {
//构建的是 FloatPropertyValuesHolder
return new FloatPropertyValuesHolder(property, values);
}
//内部类, Float类型的PropertyValuesHolder
static class FloatPropertyValuesHolder extends PropertyValuesHolder {
long mJniSetter;
private FloatProperty mFloatProperty;//float属性值
Keyframes.FloatKeyframes mFloatKeyframes;//动画的关键帧,这个是重点
float mFloatAnimatedValue;
... ...
//2.构造函数
public FloatPropertyValuesHolder(String propertyName, float... values) {
super(propertyName);
//设置目标的属性值
setFloatValues(values);
}
public FloatPropertyValuesHolder(Property property, float... values) {
super(property);
//设置目标的属性值
setFloatValues(values);
if (property instanceof FloatProperty) {
mFloatProperty = (FloatProperty) mProperty;
}
}
... ...
//3.设置动画的目标值
@Override
public void setFloatValues(float... values) {
//4.调用父类的方法
super.setFloatValues(values);
//5.获取动画的关键帧
mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
}
//计算当前的动画值
@Override
void calculateValue(float fraction) {
mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
}
//代码省略
}
}
该类是属性和属性值的辅助类,它保存了属性的名称、setter、getter,以及该属性在duration时间段内各个时刻对应的属性数值(mKeyframeSet)。这样,当动画执行时,动画库只需要根据动画的执行时间,到mKeyFrameSet中查询这个时刻对应的属性值,然后修改执行动画的对象的目标属性值,连续这个过程即可达到动画效果。在这个例子中,我们的属性值是scaleX,目标属性值是0.3f。因此,对应的属性类是FloatPropertyValuesHolder ,还有一种是IntPropertyValuesHolder,这个比较好理解。
在这个例子中,我们会调用注释2中的构造函数,该函数会调用setFloatValues来设置动画的目标值,然后会达到注释3处的函数,在这个函数中调用了父类中对应的方法,然后就获取到了动画的关键帧,这有可能计算各个时刻属性值的操作放在了父类(即PropertyValuesHolder)的setFloatValues函数中。setFloatValues实现如下:
/**
* Set the animated values for this object to this set of floats.
* If there is only one value, it is assumed to be the end value of an animation,
* and an initial value will be derived, if possible, by calling a getter function
* on the object. Also, if any value is null, the value will be filled in when the animation
* starts in the same way. This mechanism of automatically getting null values only works
* if the PropertyValuesHolder object is used in conjunction
* {@link ObjectAnimator}, and with a getter function
* derived automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has
* no way of determining what the value should be.
*
* @param values One or more values that the animation will animate between.
*/
#PropertyValuesHolder
public void setFloatValues(float... values) {
mValueType = float.class;
mKeyframes = KeyframeSet.ofFloat(values);
}
static class FloatPropertyValuesHolder extends PropertyValuesHolder {
... ...
int mNumKeyframes;
Keyframe mFirstKeyframe;
Keyframe mLastKeyframe;
TimeInterpolator mInterpolator; // only used in the 2-keyframe case
List<Keyframe> mKeyframes; // only used when there are not 2 keyframes
TypeEvaluator mEvaluator;
public KeyframeSet(Keyframe... keyframes) {
mNumKeyframes = keyframes.length;
// immutable list
mKeyframes = Arrays.asList(keyframes);
mFirstKeyframe = keyframes[0];
mLastKeyframe = keyframes[mNumKeyframes - 1];
mInterpolator = mLastKeyframe.getInterpolator();
}
... ...
//我们看到关键帧的计算就在ofFloat函数中。如果用户设置了一个目标值,那么这个值就是最终的值,它的起始值就会被默认设置为0;如果用户设置了大于等于1个目标值,这些关键帧都会被存储到KetFrameSet对象中。设置完关键帧之后,我们就会调用start()方法启动动画了。我们看看ObjectAnimator的start()方法。
public static KeyframeSet ofFloat(float... values) {
boolean badValue = false;
int numKeyframes = values.length;
FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
//如果用户设置了一个目标值
if (numKeyframes == 1) {
//它的起始值就会被默认设置为0
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
//那么这个值就是最终的值
keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
if (Float.isNaN(values[0])) {
badValue = true;
}
} else {
//它的起始值就会被默认设置为0,这里的起始值是 values[0]
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);
}
}
#ObjectAnimator
@Override
public void start() {
AnimationHandler.getInstance().autoCancelBasedOn(this);
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));
}
}
super.start();
}
这里支持调用父类的start()方法,它的父类是ValueAnimator,该类的start()方法如下:
#ValueAnimator
@Override
public void start() {
start(false);
}
#ValueAnimator
/**
*开始播放动画。boolean参数 playBackwards 指示动画是否应该反向播放
* Start the animation playing. This version of start() takes a boolean flag that indicates
* whether the animation should play in reverse. The flag is usually false, but may be set
* to true if called from the reverse() method.
*该标志通常为false,但如果从reverse()方法调用,则可以设置为true。
*
*通过调用此方法启动的动画将在调用此方法的线程上运行。
*该线程上应该有一个Looper(如果不是这样的话,将抛出运行时异常)
*此外,如果动画将为视图层次结构中的对象的属性设置动画,则调用线程应该是该视图层次结构的UI线程。
* <p>The animation started by calling this method will be run on the thread that called
* this method. This thread should have a Looper on it (a runtime exception will be thrown if
* this is not the case). Also, if the animation will animate
* properties of objects in the view hierarchy, then the calling thread should be the UI
* thread for that view hierarchy.</p>
*
* @param playBackwards Whether the ValueAnimator should start playing in reverse.
*/
private void start(boolean playBackwards) {
//判断Looper是否为空,这里的Looper是UI线程的Looper。
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
//设置一些基本状态
mReversing = playBackwards;
mSelfPulse = !mSuppressSelfPulseRequested;
// Special case: reversing from seek-to-0 should act as if not seeked at all.
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;
mAnimationEndRequested = 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 = -1;
mFirstFrameTime = -1;
mStartTime = -1;
addAnimationCallback(0);
//是否延时
if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
//如果没有启动延迟,则启动动画并立即通知启动侦听器
// 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();
//Fraction 是动画执行的百分比
//这里是判断是否(到达某个动画播放进度)
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.
setCurrentPlayTime(0);
} else {
setCurrentFraction(mSeekFraction);
}
}
}
#ValueAnimator
/**
* Called internally to start an animation by adding it to the active animations list. Must be
* called on the UI thread.
*在内部调用以通过将动画添加到活动动画列表来启动动画。 必须在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();
}
}
#ValueAnimator
private void notifyStartListeners() {
if (mListeners != null && !mStartListenersCalled) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
int numListeners = tmpListeners.size();
for (int i = 0; i < numListeners; ++i) {
//3.回调动画开始的hook
tmpListeners.get(i).onAnimationStart(this, mReversing);
}
}
mStartListenersCalled = true;
}
#ValueAnimator
/**
* This function is called immediately before processing the first animation
* frame of an animation. If there is a nonzero <code>startDelay</code>, the
* function is called after that delay ends.
* It takes care of the final initialization steps for the
* animation.
*
* <p>Overrides of this method should call the superclass method to ensure
* that internal mechanisms for the animation are set up correctly.</p>
*/
@CallSuper
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].init();
}
mInitialized = true;
}
}
#ValueAnimator
private void addAnimationCallback(long delay) {
if (!mSelfPulse) {
return;
}
getAnimationHandler().addAnimationFrameCallback(this, delay);
}
#ValueAnimator
/**
*用于动画更新的Handler
* @return The {@link AnimationHandler} that will be used to schedule updates for this animator.
* @hide
*/
public AnimationHandler getAnimationHandler() {
return AnimationHandler.getInstance();
}
#Animator
public abstract class Animator implements Cloneable {
#Animator
/**
* The set of listeners to be sent events through the life of an animation.
* 动画监听器
*/
ArrayList<AnimatorListener> mListeners = null;
/**
* A pause listener receives notifications from an animation when the
* animation is {@link #pause() paused} or {@link #resume() resumed}.
*当动画为{@link #pause()paused}或{@link #resume()恢复时,暂停侦听器接收来自动画的通知
* @see #addPauseListener(AnimatorPauseListener)
*/
public static interface AnimatorPauseListener {
/**
* <p>Notifies that the animation was paused.</p>
*通知动画已暂停。
* @param animation The animaton being paused.
* @see #pause()
*/
void onAnimationPause(Animator animation);
/**
* <p>Notifies that the animation was resumed, after being
* previously paused.</p>
*在先前暂停之后通知动画已恢复。
* @param animation The animation being resumed.
* @see #resume()
*/
void onAnimationResume(Animator animation);
}
}
#Animator
/**
* Gets the set of {@link android.animation.Animator.AnimatorListener} objects that are currently
* listening for events on this <code>Animator</code> object.
*
* @return ArrayList<AnimatorListener> The set of listeners.
*/
public ArrayList<AnimatorListener> getListeners() {
return mListeners;
}
#Animator
/**
* <p>An animation listener receives notifications from an animation.
* Notifications indicate animation related events, such as the end or the
* repetition of the animation.</p>
*/
//动画侦听器接收来自动画的通知。通知指示动画相关事件,例如动画的结束或重复。
public static interface AnimatorListener {
}
//分析以下clone方法
@Override
public Animator clone() {
try {
final Animator anim = (Animator) super.clone();
if (mListeners != null) {
//浅拷贝
anim.mListeners = new ArrayList<AnimatorListener>(mListeners);
}
if (mPauseListeners != null) {
//浅拷贝
anim.mPauseListeners = new ArrayList<AnimatorPauseListener>(mPauseListeners);
}
return anim;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
/**
*在处理动画的第一个动画帧之前立即调用此函数。
* This function is called immediately before processing the first animation
* frame of an animation. If there is a nonzero <code>startDelay</code>, the
* function is called after that delay ends.
* It takes care of the final initialization steps for the
* animation.它负责动画的最终初始化步骤
*此方法的覆盖应调用超类方法以确保正确设置动画的内部机制。
* <p>Overrides of this method should call the superclass method to ensure
* that internal mechanisms for the animation are set up correctly.</p>
*/
@CallSuper
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].init();
}
mInitialized = true;
}
}
#ObjectAnimator
/**
* This function is called immediately before processing the first animation
* frame of an animation. If there is a nonzero <code>startDelay</code>, the
* function is called after that delay ends.
* It takes care of the final initialization steps for the
* animation. This includes setting mEvaluator, if the user has not yet
* set it up, and the setter/getter methods, if the user did not supply
* them.
*
* <p>Overriders of this method should call the superclass method to cause
* internal mechanisms to be set up correctly.</p>
*/
@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
/**
* Internal function to set the value on the target object, using the setter set up
* earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
* to handle turning the value calculated by ValueAnimator into a value set on the object
* according to the name of the property.
* @param target The target object on which the value is set
*/
void setAnimatedValue(Object target) {
if (mProperty != null) {
mProperty.set(target, getAnimatedValue());
}
if (mSetter != null) {
try {
mTmpValueArray[0] = getAnimatedValue();
mSetter.invoke(target, mTmpValueArray);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
#PropertyValuesHolder.IntPropertyValuesHolder
static class IntPropertyValuesHolder extends PropertyValuesHolder {
/**
* Internal function to set the value on the target object, using the setter set up
* earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
* to handle turning the value calculated by ValueAnimator into a value set on the object
* according to the name of the property.
* @param target The target object on which the value is set
*/
@Override
void setAnimatedValue(Object target) {
if (mIntProperty != null) {
mIntProperty.setValue(target, mIntAnimatedValue);
return;
}
if (mProperty != null) {
mProperty.set(target, mIntAnimatedValue);
return;
}
if (mJniSetter != 0) {
nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
return;
}
if (mSetter != null) {
try {
mTmpValueArray[0] = mIntAnimatedValue;
mSetter.invoke(target, mTmpValueArray);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
}
#ValueAnimator
/**
* This method is called with the elapsed fraction of the animation during every
* animation frame. This function turns the elapsed fraction into an interpolated fraction
* and then into an animated value (from the evaluator. The function is called mostly during
* animation updates, but it is also called when the <code>end()</code>
* function is called, to set the final value on the property.
*
* <p>Overrides of this method must call the superclass to perform the calculation
* of the animated value.</p>
*
* @param fraction The elapsed fraction of the animation.
*/
@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为FloatPropertyValuesHolder 类型,因此,mValues也就是FloatPropertyValuesHolder 类型,继续深入到FloatPropertyValuesHolder 中的setAnimatedValue 方法。
static class FloatPropertyValuesHolder extends PropertyValuesHolder {
/**
* Internal function to set the value on the target object, using the setter set up
* earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
* to handle turning the value calculated by ValueAnimator into a value set on the object
* according to the name of the property.
* @param target The target object on which the value is set
*/
@Override
void setAnimatedValue(Object target) {
//1.如果有float property,则通过 setValue 来更新属性值
if (mFloatProperty != null) {
mFloatProperty.setValue(target, mFloatAnimatedValue);
return;
}
if (mProperty != null) {//2.如果有属性,则通过setValue来更新
mProperty.set(target, mFloatAnimatedValue);
return;
}
if (mJniSetter != 0) {
nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
return;
}
if (mSetter != null) { //3.否则通过反射调用属性的setter函数来更新属性值
try {
mTmpValueArray[0] = mFloatAnimatedValue;
//通过setter函数更新属性值
mSetter.invoke(target, mTmpValueArray);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
@Override
void calculateValue(float fraction) {
mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
}
}
#ObjectAnimator
/**
* This function is called immediately before processing the first animation
* frame of an animation. If there is a nonzero <code>startDelay</code>, the
* function is called after that delay ends.
* It takes care of the final initialization steps for the
* animation. This includes setting mEvaluator, if the user has not yet
* set it up, and the setter/getter methods, if the user did not supply
* them.
*
* <p>Overriders of this method should call the superclass method to cause
* internal mechanisms to be set up correctly.</p>
*/
@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) {
//1.设置setter 和 getter方法
mValues[i].setupSetterAndGetter(target);
}
}
super.initAnimation();
}
}
#PropertyValuesHolder
/**
* Internal function (called from ObjectAnimator) to set up the setter and getter
* prior to running the animation. If the setter has not been manually set for this
* object, it will be derived automatically given the property name, target object, and
* types of values supplied. If no getter has been set, it will be supplied iff any of the
* supplied values was null. If there is a null value, then the getter (supplied or derived)
* will be called to set those null values to the current value of the property
* on the target object.
* @param target The object on which the setter (and possibly getter) exist.
*/
//这里会初始化属性的setter和getter方法。通过反射调用这些函数就可以修改其属性值了。
void setupSetterAndGetter(Object target) {
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.
if (mProperty == null) {
Class targetClass = target.getClass();
if (mSetter == null) {
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) {
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());
}
}
}
}
}
通过时间百分比计算得到了fraction之后调用了animateValue函数,我们看看ObjectAnimator中的animateValue函数的实现。
#ObjectAnimator
ObjectAnimator也重写了animateValue方法,(ObjectAnimator是其ValueAnimator的子类)
/**
* This method is called with the elapsed fraction of the animation during every
* animation frame. This function turns the elapsed fraction into an interpolated fraction
* and then into an animated value (from the evaluator. The function is called mostly during
* animation updates, but it is also called when the <code>end()</code>
* function is called, to set the final value on the property.
*
* <p>Overrides of this method must call the superclass to perform the calculation
* of the animated value.</p>
*
* @param fraction The elapsed fraction of the animation.
*/
@CallSuper
@Override
void animateValue(float fraction) {
final Object target = getTarget();
if (mTarget != null && target == null) {
// We lost the target reference, cancel and clean up. Note: we allow null target if the
/// target has never been set.
cancel();
return;
}
super.animateValue(fraction);//计算属性值
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setAnimatedValue(target);//更新属性值
}
}
#Animator
// Pulse an animation frame into the animation.将动画帧脉冲到动画中
boolean pulseAnimationFrame(long frameTime) {
// TODO: Need to find a better signal than this. There's a bug in SystemUI that's preventin
// returning !isStarted() from working.
return false;
}
#ValueAnimator
@Override
boolean pulseAnimationFrame(long frameTime) {
if (mSelfPulse) {
// Pulse animation frame will *always* be after calling start(). If mSelfPulse isn't
// set to false at this point, that means child animators did not call super's start().
// This can happen when the Animator is just a non-animating wrapper around a real
// functional animation. In this case, we can't really pulse a frame into the animation,
// because the animation cannot necessarily be properly initialized (i.e. no start/end
// values set).
//脉冲动画帧将*总是*在调用start()之后
//如果此时mSelfPulse未设置为false,则表示儿 child animators 未调用super的start()。
//当Animator只是一个围绕真实功能动画的非动画包装时,就会发生这种情况。
return false;
}
return doAnimationFrame(frameTime);
}
#ValueAnimator
/**
* Processes a frame of the animation, adjusting the start time if needed.
*处理动画的帧,根据需要调整开始时间。
* @param frameTime The frame time.
* @return true if the animation has ended.
* @hide
*/
public final boolean doAnimationFrame(long frameTime) {
if (mStartTime < 0) {
// First frame. If there is start delay, start delay count down will happen *after* this
// frame.
mStartTime = mReversing ? frameTime : frameTime + (long) (mStartDelay * sDurationScale);
}
// Handle pause/resume
if (mPaused) {
mPauseTime = frameTime;
removeAnimationCallback();
return false;
} else if (mResumed) {
mResumed = false;
if (mPauseTime > 0) {
// Offset by the duration that the animation was paused
mStartTime += (frameTime - mPauseTime);
}
}
if (!mRunning) {
// If not running, that means the animation is in the start delay phase of a forward
// running animation. In the case of reversing, we want to run start delay in the end.
if (mStartTime > frameTime && mSeekFraction == -1) {
// This is when no seek fraction is set during start delay. If developers change the
// seek fraction during the delay, animation will start from the seeked position
// right away.
return false;
} else {
// If mRunning is not set by now, that means non-zero start delay,
// no seeking, not reversing. At this point, start delay has passed.
mRunning = true;
startAnimation();
}
}
if (mLastFrameTime < 0) {
if (mSeekFraction >= 0) {
long seekTime = (long) (getScaledDuration() * mSeekFraction);
mStartTime = frameTime - seekTime;
mSeekFraction = -1;
}
mStartTimeCommitted = false; // allow start time to be compensated for jank
}
mLastFrameTime = frameTime;
// 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();
}
return finished;
}
参考《Android源码设计模式》