Android - Animation(一) 一文总结了android中的补间动画(View Animation/Tween Animation)和帧动画(Drawable Animation/Frame Animation)的使用
本篇文章主要解析属性动画(Property Animation,android3.0引入)的实现原理
下篇 属性动画的实现原理
先来看属性动画的最简单实现:
第一种方式:先在 /res/animator/目录下创建translate.xml文件定义动画,再在Java文件中引用并开启
//首先在/res/animator/目录下创建translate.xml文件,内容如下:
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator
xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="translationX" //设置动画需要改变的属性名称
android:duration="2000" //设置动画的持续时间
android:valueFrom="0.0" //设置动画的初始值(对应上边设置的属性)
android:valueTo="20.0"> //设置动画的结束值(对应上边设置的属性)
</objectAnimator>
//然后在Java文件中进行引用,代码如下:
mButton1 = (Button) findViewById(R.id.button1);
mButton1.setOnClickListener(this);
//加载xml文件中的动画资源
mObjectAnimator = AnimatorInflater.loadAnimator(this, R.animator.translate);
mObjectAnimator.setTarget(mButton1);//设置动画的作用对象
public void onClick(View v) {
switch(v.getId()){
case R.id.button1:
mObjectAnimator.start(); //开启动画
break;
}
}
第二种方式:直接在Java文件中创建并使用动画
mButton1 = (Button) findViewById(R.id.button1);
mButton1.setOnClickListener(this);
public void onClick(View v) {
//利用ofFloat()方法创建动画、指定需要改变的属性的名称等并开启动画
ObjectAnimator.ofFloat(mButton1, "translationX", 0.0f,20.0f).setDuration(3000).start();
}
简单地说,属性动画就是在指定的时间内改变对象的属性值。
上述的例子,从代码上看,设置了动画执行的时间、作用的目标对象mButton1及其属性translationX以及属性值的初始值和最终值,但从效果上看,mButton1在2秒钟之内在屏幕上匀速的移动了一段距离,于是我们可以猜想:
在start()方法执行之后,是不是会不断地计算出一个值并赋给目标对象的属性?
在属性动画中,是不是也有和补间动画里类似的插值器来改变动画的执行速率?
如果有这样的一个插值器的话,需要赋给目标对象的属性的那个值的计算是不是也和这个插值器有关?
... ...
带着这些猜想,我们就以 在Java文件中创建动画的方式 为例来梳理属性动画的实现原理。
首先是ObjectAnimator类的静态方法ofFloat
/*
Constructs and returns an ObjectAnimator that animates between float values. A single value implies that that value is the one being animated to. Two values imply a 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).
创建并返回 一个基于float 类型数值的ObjectAnimator 对象,一个value值代表动画的终点,两个value 值,则一个是起点,
另一个是终点,如果是多个值,则中间的值代表动画将要经过的点 ,并且这些点会均匀地分布在动画的执行过程中
*/
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = newObjectAnimator(target, propertyName);
❶ anim.setFloatValues(values);
return anim;
}
ObjectAnimator的构造函数:
private ObjectAnimator(Object target, String propertyName) {
//为ObjectAnimator的成员变量private Object mTarget赋值,mTarget的注释为:
//The target object on which the property exists, set in the constructor
mTarget = target;
setPropertyName(propertyName);
}
setPropertyName方法:
public void setPropertyName(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是ObjectAnimator的父类ValueAnimator的成员变量PropertyValuesHolder[] mValues;
//第一次执行为null
if (mValues != null) {
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getPropertyName();
valuesHolder.setPropertyName(propertyName);
mValuesMap.remove(oldName);
mValuesMap.put(propertyName, valuesHolder);
}
//为ObjectAnimator的成员变量private String mPropertyName赋值
mPropertyName = propertyName;
// New property/values/target should cause re-initialization prior to starting
//mInitialized 是ObjectAnimator的父类ValueAnimator的成员变量boolean mInitialized;
//它的注释为:Flag that denotes whether the animation is set up and ready to go.
//Used to set up animation that has not yet been started.
//一个Flag用于标示动画是否开始准备执行,它被用于动画还没有正式开始start之前。默认值为false
mInitialized = false;
}
所以,第一次执行ObjectAnimator anim = new ObjectAnimator(target, propertyName);只做了两件事情:
1、为ObjectAnimator 的成员变量mTarget和mPropertyName赋值
2、将mInitialized(定义在ObjectAnimator 的父类 ValueAnimator 中)的值设为false
接下来是上文中❶处的anim.setFloatValues(values)方法:
@Override
public void setFloatValues(float... values) {
if (mValues == null || mValues.length == 0) { //第一次执行mValues == null
// No values yet - this animator is being constructed piecemeal. Init the values with whatever the current propertyName is
// mValues目前尚未赋值——当前的animator 正在构建中,将通过传入的values初始化mValues
if (mProperty != null) {
//mProperty 为ObjectAnimator的成员变量 private Property mProperty,第一次执行时也为null
setValues(PropertyValuesHolder.ofFloat(mProperty, values));
} else {
// 第一次执行时,下列函数将被调用:
❷setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
}
} else {
super.setFloatValues(values);
}
}
在分析setValues()方法之前先来看PropertyValuesHolder的ofFloat()方法:
/**
* Constructs and returns a PropertyValuesHolder with a given property name and set of float values.
*/
// 通过给定的propertyName 和 values创建并返回一个PropertyValuesHolder 对象
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
❸ return new FloatPropertyValuesHolder(propertyName, values);
}
接着FloatPropertyValuesHolder的构造函数:
public FloatPropertyValuesHolder(String propertyName, float... values) {
super(propertyName);
❹ setFloatValues(values);
}
首先执行的是FloatPropertyValuesHolder 的父类 PropertyValuesHolder的构造函数:
private PropertyValuesHolder(String propertyName) {
// 为成员变量String mPropertyName(定义在PropertyValuesHolder类中)赋值
mPropertyName = propertyName;
}
我们注意到,FloatPropertyValuesHolder 、IntPropertyValuesHolder都是PropertyValuesHolder的静态内部类
来看一下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.
*/
//这个类维持着一个property 和它的 值,用以在动画中呈现出来,
//我们可以认为它是为实现属性动画而对property及其值做的一个包装
public class PropertyValuesHolder implements Cloneable { }
接下来是上文中❹处setFloatValues()方法:
@Override
public void setFloatValues(float... values) {
❺ super.setFloatValues(values);
mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
}
首先执行的又是父类的方法:
public void setFloatValues(float... values) {
// 为成员变量Class mValueType(定义在父类PropertyValuesHolder中)赋值
mValueType = float.class;
❻mKeyframeSet = KeyframeSet.ofFloat(values);
}
然后,mKeyframeSet = KeyframeSet.ofFloat(values),先来看KeyframeSet类的定义:
/**
* This class holds a collection of Keyframe objects and is called by ValueAnimator to calculate
* values between those keyframes for a given animation. The class internal to the animation
* package because it is an implementation detail of how Keyframes are stored and used.
*/
//这个类维持着一个Keyframe 对象的集合,
//ValueAnimator使用它来为一个动画计算那些 keyframe 之间的值 ... ...
class KeyframeSet { }
对KeyframeSet有一个大概了解之后,再来看一下Keyframe类的定义:
/**
* This class holds a time/value pair for an animation. The Keyframe class is used by {@link ValueAnimator} to define the values that
* the animation target will have over the course of the animation. As the time proceeds from one keyframe to the other, the value of
* the target object will animate between the value at the previous keyframe and the value at the next keyframe. Each keyframe also
* holds an optional {@link TimeInterpolator} object, which defines the time interpolation over the intervalue preceding the keyframe.
*/
//简单说,这就是个 对动画过程中的一帧的时间和属性的值进行封装的类
public abstract class Keyframe implements Cloneable { }
来看上文中❻处的KeyframeSet的ofFloat(values)方法:
public static KeyframeSet ofFloat(float... values) {
boolean badValue = false;//初始化一个标示,之后用于标示values[i]是不是一个数字
int numKeyframes = values.length;//获取传入的参数的个数
//初始化一个FloatKeyframe 类型的数组,数组的长度为numKeyframes和2之间的较大者,这个比较容易理解
FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
if (numKeyframes == 1) {
//如果我们只传入了一个参数,那么这个参数将用于构建keyframes[1],keyframes[0]的值则由ofFloat(0f)来构建,如下:
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
// Constructs a Keyframe object with the given time. The value at this time will be derived from the target object when the animation first starts ... ...
// 使用给定的time构造一个Keyframe 对象,在动画第一次执行时,这个给定的时间对应的value将利用动画的目标对象去获得 ... ...
public static Keyframe ofFloat(float fraction) {
//FloatKeyframe和IntKeyframe都是Keyframe 的静态内部类,这个和PropertyValuesHolder结构是类似的
return new FloatKeyframe(fraction);
FloatKeyframe(float fraction) {
// 为成员变量float mFraction(定义在Keyframe 中)赋值, 注意,在此时,成员变量mValue的值还没有设置
mFraction = fraction;
// 为成员变量Class mValueType(定义在Keyframe 中)赋值
mValueType = float.class;
}
}
keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
/**
* Constructs a Keyframe object with the given time and value. The time defines the time, as a proportion of an overall
* animation's duration, at which the value will hold true for the animation ... ...
// 使用给定的time和value构造一个Keyframe 对象,time作为整个动画执行过程中的一个时间比例,是一个0到1之间的值 ... ...
public static Keyframe ofFloat(float fraction, float value) {
return new FloatKeyframe(fraction, value);
FloatKeyframe(float fraction, float value) {
mFraction = fraction;
mValue = value;
mValueType = float.class;
mHasValue = true;
}
}
if (Float.isNaN(values[0])) {
badValue = true; // 如果传入的值不是一个数值,将标示badValue改为true
}
//从以上分析可以看到,当我们只传入一个值时,将用1(代表时间终点)和这个值构建出动画执行的最后一帧的Keyframe
// 对象,而动画的第一帧对应的Keyframe 对象,则默认由0(代表时间的起点)来构建,这一帧对应的值将在动画第一次
//执行时由动画作用的对象来获得,如果我们传入的参数大于1个,比如2个或者多个,则执行下边的逻辑:
} else {
//逻辑比较简单,如果传入的参数大于1,则,用0f和values[0]构建出动画的第一帧对应的Keyframe对象并赋值给keyframes[0],
//代表第一个value对应动画的起始值
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
// 然后,进行遍历,利用values[i] 和 其所对应的帧在整个动画执行过程中应该处于的时间比例——
// (float) i / (numKeyframes - 1) 来构建每一个Keyframe对象,一般我们传入的参数是两个,所以第二个参数就对应了
// 动画执行的最后一帧的属性值,其实,在这里,属性动画实现的原理已经开始有所体现了。
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;// 同上,如果传入的值不是一个数值,将标示badValue改为true
}
}
}
if (badValue) {
Log.w("Animator", "Bad value (NaN) in float animator");// 如果传入的值不是一个数值,执行此逻辑
}
//以上逻辑主要就是创建keyframes数组,该数组中放的是根据传入的value值创建出来的动画执行过程中的关键帧对象
//即(主要是)将一个mKeyframes 成员变量完成初始化的FloatKeyframeSet对象返回
return new FloatKeyframeSet(keyframes);
public FloatKeyframeSet(FloatKeyframe... keyframes) {
// 走的是父类的构造函数
super(keyframes);
public KeyframeSet(Keyframe... keyframes) {
//这个逻辑就比较简单了
mNumKeyframes = keyframes.length;//为成员变量int mNumKeyframes()赋值
mKeyframes = new ArrayList<Keyframe>();
//将接收到的keyframes数组中的元素添加到成员变量ArrayList<Keyframe> mKeyframes集合中
mKeyframes.addAll(Arrays.asList(keyframes));
mFirstKeyframe = mKeyframes.get(0);// 初始化第一帧对应的成员变量Keyframe mFirstKeyframe
mLastKeyframe = mKeyframes.get(mNumKeyframes - 1); // 初始化最后一帧对应的成员变量Keyframe mLastKeyframe
// 初始化插值器对应的成员变量TimeInterpolator mInterpolator,目前为null
mInterpolator = mLastKeyframe.getInterpolator();
private TimeInterpolator mInterpolator = null;
public TimeInterpolator getInterpolator() {
return mInterpolator;
}
}
}
}
至此,KeyframeSet的ofFloat(values)方法完成了,主要的逻辑是:
根据传入的value值创建出动画执行过程中的关键帧对象,将这些对象放在一个数组中,new一个FloatKeyframeSet对象,然后将数组中的这些元素,放在FloatKeyframeSet对象的成员变量ArrayList<Keyframe> mKeyframes中,并将FloatKeyframeSet对象返回。
上边第❻步将这个FloatKeyframeSet对象赋值给PropertyValuesHolder的成员变量KeyframeSet
mKeyframeSet,于是,第❺步也执行完了,接着,在setFloatValues()方法中,执行完第❺步后:
mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
//将mKeyframeSet向下转型为FloatKeyframeSet赋值给FloatPropertyValuesHolder
//的成员变量FloatKeyframeSet mFloatKeyframeSet
于是,第❹步也执行完了,第❸步把完成初始化的FloatPropertyValuesHolder对象返回,第❷步将利用第❸步返回的对象作为参数,执行setValues()方法,(该方法在ObjectAnimator的父类ValueAnimator类中定义),具体逻辑如下:
public void setValues(PropertyValuesHolder... values) {
int numValues = values.length;
// 为PropertyValuesHolder[] mValues(在ObjectAnimator父类ValueAnimator中定义)赋值
mValues = values;
// 为成员变量HashMap<String, PropertyValuesHolder> mValuesMap赋值,
//以下是关于mValuesMap的注释:
//A hashmap of the PropertyValuesHolder objects. This map is used to lookup
//animated values by property name during calls to getAnimatedValue(String).
//一个用于存储PropertyValuesHolder对象的集合,在调用getAnimatedValue(String)方法
//时通过property name来查找values
mValuesMap = new HashMap<String, PropertyValuesHolder>(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;
}
至此,用于创建属性动画对象的ObjectAnimator类的静态方法ofFloat的大体逻辑分析完成了。简单总结一下:
ObjectAnimator.ofFloat(Object target, String propertyName, float... values) 创建了一个ObjectAnimator对象,并且:
1、将 target 赋给 ObjectAnimator 的成员变量 private Object mTarget
2、将 propertyName 赋给 ObjectAnimator 的成员变量 private String mPropertyName
3、创建一个 FloatPropertyValuesHolder 对象
3.1、将 propertyName 赋给 FloatPropertyValuesHolder 的成员变量 String mPropertyName(在FloatPropertyValuesHolder 的父类 PropertyValuesHolder中定义)
3.2、将 float.class 赋给 FloatPropertyValuesHolder 的成员变量 Class mValueType(在FloatPropertyValuesHolder 的父类 PropertyValuesHolder中定义)
3.3、创建一个 FloatKeyframeSet对象
3.3.1、创建一个 FloatKeyframe 类型的数组 keyframes
3.3.2、根据传入的 values 构造出 keyframes 数组中的每一项(关键帧对象Keyframe)
3.3.3、将 keyframes 数组中的每一项添加到 FloatKeyframeSet 对象的成员变量
ArrayList<Keyframe>mKeyframes(在FloatKeyframeSet 的父类KeyframeSet中定义)中
3.4、将 FloatKeyframeSet 对象赋给 PropertyValuesHolder 的成员变量 KeyframeSet mKeyframeSet
3.5、将 mKeyframeSet 向下转型为 FloatKeyframeSet 类型赋给
FloatPropertyValuesHolder 的成员变量 FloatKeyframeSet mFloatKeyframeSet
4、将 FloatPropertyValuesHolder 对象 赋给ObjectAnimator 的成员变量 PropertyValuesHolder[] mValues
(在ObjectAnimator 的父类ValueAnimator中定义)
5、利用已完成初始化的FloatPropertyValuesHolder 对象及其mPropertyName属性,
完成成员变量HashMap<String, PropertyValuesHolder> mValuesMap(在ValueAnimator中定义)的初始化
动画开始执行之前,还有一个关键的方法 — setDuration(long)
public ObjectAnimator setDuration(long duration) {
// 执行的是父类 ValueAnimator 的setDuration()方法
super.setDuration(duration);
private static float sDurationScale = 1.0f;
// How long the animation should last in ms 默认时间是300毫秒
private long mDuration = (long)(300 * sDurationScale);
private long mUnscaledDuration = 300;
public ValueAnimator setDuration(long duration) {
if (duration < 0) {
// 若传入的值小于零,抛出异常
throw new IllegalArgumentException("Animators cannot have
negative duration: " + duration);
}
mUnscaledDuration = duration;
mDuration = (long)(duration * sDurationScale);
return this;
}
return this;
}
另外,在 setDuration( )方法中,我们可以看到 成员变量 mDuration 的值最终是由我们调用 setDuration( )方法时传入的 duration 乘以 sDurationScale 得出的,sDurationScale 默认值为 1.0f ; 并且 ValueAnimator 类中提供了静态方法 setDurationScale() 供我们使用
public static void setDurationScale(float durationScale) {
sDurationScale = durationScale;
}
我们可以利用这个方法改变动画的速度
下边来看属性动画的执行过程 —— start( )方法
从上文的分析,我们注意到,使用ObjectAnimator类的静态方法ofFloat来创建动画对象的过程中,ObjectAnimator类只是复写了父类ValueAnimator的一部分方法,同样也只拥有部分只属于自己的成员变量,事实上,我们在使用属性动画时,所涉及到的类的继承关系如下:
/**
* This is the superclass for classes which provide basic support for animations which can be
* started, ended, and have <code>AnimatorListeners</code> added to them.
*/
//这是那些为动画提供最基础的支持以让动画能被开启、结束和被增加监听的类的超类
public abstract class Animator implements Cloneable { }
Animator 是属性动画体系的超类,它定义了诸如 start()、cancel()、end()、setDuration(long duration)、setInterpolator(TimeInterpolator value)、isRunning()、addListener(AnimatorListener listener)、removeAllListeners() 等方法
AnimatorSet 在属性动画中的用法和在补间动画中类似,不细讲了
ValueAnimator 和 ObjectAnimator 是属性动画的实现类,它们的区别在哪里?分析完start( ) 方法,再结合上边的 ofFloat( ) 方法进行总结
ObjectAnimator 的 start() 方法:
public void start() {
// See if any of the current active/pending animators need to be canceled
AnimationHandler handler = sAnimationHandler.get();
if (handler != null) { ... ... } // 第一次进入这里 handler 应该是 null
super.start(); // 执行父类的start()方法
}
ValueAnimator 的 start() 方法:
public void start() {
start(false);
}
/**
* 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.
* <p>The animation started by calling this method will be run on the thread that called this method. This thread should have
* a Looperon 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>
*/
// 开始执行动画,这个start() 方法 有一个boolean型的参数用于标示该动画是否需要反转,该参数一般为false,但是也可能
// 被设置为true如果start() 方法是从 reverse() 方法中调用的,该动画将运行在调用 start() 方法的线程里,这个线程需要拥有
// 一个Looper 对象,否则会发生异常 ... ...
private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
mPlayingBackwards = playBackwards; // 动画是否反转的标示
// This variable tracks the current iteration that is playing. When mCurrentIteration exceeds the repeatCount
// (if repeatCount!=INFINITE), the animation ends 这个变量用于记录当前动画的循环次数,当mCurrentIteration 超过了
// repeatCount(如果repeatCount 不等于 -1),动画将被终止,该变量默认值为0
mCurrentIteration = 0;
mPlayingState = STOPPED;//标示动画的状态,以下是ValueAnimator中对动画状态的定义:
/**
* Values used with internal variable mPlayingState to indicate the current state of an animation.
*/
// 变量mPlayingState 使用这些值来标示动画的状态
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
mStarted = true;
// Tracks whether a startDelay'd animation has begun playing through the startDelay.
mStartedDelay = false;
// Whether this animator is currently in a paused state.
mPaused = false;
AnimationHandler animationHandler = getOrCreateAnimationHandler();
private static AnimationHandler getOrCreateAnimationHandler() {
AnimationHandler handler = sAnimationHandler.get();
if (handler == null) {// 第一次执行,走下边的逻辑
handler = new AnimationHandler();
sAnimationHandler.set(handler);// 此处涉及ThreadLocal的使用,暂不细说
}
return handler;
}
// 将此动画对象添加到AnimationHandler的mPendingAnimations集合中
animationHandler.mPendingAnimations.add(this);
if (mStartDelay == 0) {
// The amount of time in ms to delay starting the animation after start() is called 调用start()方法之后延迟多少时间播放动画
private long mStartDelay = 0;// 默认为零,执行下边的逻辑
// This sets the initial value of the animation, prior to actually starting it running
setCurrentPlayTime(0);
mPlayingState = STOPPED;
mRunning = true;
notifyStartListeners();
}
animationHandler.start();
}
在ValueAnimator的start( )方法中,需要重点分析的就是setCurrentPlayTime(0)和animationHandler.start()这两个方法
setCurrentPlayTime(0)方法:
public void setCurrentPlayTime(long playTime) {
❼ initAnimation();
long currentTime = AnimationUtils.currentAnimationTimeMillis();
if (mPlayingState != RUNNING) {
mSeekTime = playTime;
mPlayingState = SEEKED;
}
mStartTime = currentTime - playTime;
❽ doAnimationFrame(currentTime);
}
上边❼处的initAnimation()方法:
// ObjectAnimator 复写了父类的initAnimation()方法
void initAnimation() {
if (!mInitialized) { // 此时mInitialized的值为false,执行下边逻辑
// mValueType may change due to setter/getter setup; do this before calling super.init(),
// which uses mValueType to set up the default type evaluator.
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
❾ mValues[i].setupSetterAndGetter(mTarget);
}
❿ super.initAnimation();
}
}
上边第❾步执行的是PropertyValuesHolder的setupSetterAndGetter()方法,来看具体逻辑:
void setupSetterAndGetter(Object target) {
if (mProperty != null) {
// check to make sure that mProperty is on the class of target
try {
Object testValue = mProperty.get(target);
for (Keyframe kf : mKeyframeSet.mKeyframes) {
if (!kf.hasValue()) {
kf.setValue(mProperty.get(target));
}
}
return;
} catch (ClassCastException e) {
Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
") on target object " + target + ". Trying reflection instead");
mProperty = null;
}
}
// 从上文中属性动画的创建的分析来看,此时的mProperty 为null,从下边开始执行
Class targetClass = target.getClass();
if (mSetter == null) {
// PropertyValuesHolder 的成员变量 Method mSetter,默认为null
setupSetter(targetClass);
}
for (Keyframe kf : mKeyframeSet.mKeyframes) {
if (!kf.hasValue()) {
if (mGetter == null) {
setupGetter(targetClass);
if (mGetter == null) {
// PropertyValuesHolder 的成员变量 private Method mGetter,默认为null
// Already logged the error - just return to avoid NPE
return;
}
}
try {
kf.setValue(mGetter.invoke(target));
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
}
setupSetterAndGetter()方法中,重点分析setupSetter(targetClass)、setupGetter(targetClass)以及kf.setValue(mGetter.invoke(target))方法
setupSetter(targetClass)方法
void setupSetter(Class targetClass) {
// 可以看到,该方法的主要作用就是给PropertyValuesHolder 的成员变量 Method mSetter 赋值
mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType);
}
setupGetter(Class targetClass)方法
private void setupGetter(Class targetClass) {
// 可以看到,该方法的主要作用就是给 PropertyValuesHolder 的成员变量 Method mGetter 赋值
mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
}
setupSetter(targetClass) 和 setupGetter(targetClass) 方法都调用了setupSetterOrGetter 方法,只是参数有所不同,第一个和第三个好理解,第二个参数是一个集合,其定义如下:
//These maps hold all property entries for a particular class. This map is used to speed up property/setter/getter lookups for a given
//class/property combination. No need to use reflection on the combination more than once. 也比较简单明了
private static final HashMap<Class, HashMap<String, Method>> sSetterPropertyMap =
new HashMap<Class, HashMap<String, Method>>();
private static final HashMap<Class, HashMap<String, Method>> sGetterPropertyMap =
new HashMap<Class, HashMap<String, Method>>();
第四个参数,对于setupSetter()方法来讲,传入的是mValueType(我们在创建动画对象时已为其赋值),而对于setupGetter()方法来讲,传入的是null,来看setupSetterOrGetter 方法的主要逻辑:
/**
* Returns the setter or getter requested. This utility function checks whether the requested method exists in the propertyMapMap
* cache. If not, it calls another utility function to request the Method from the targetClass directly.
*/
// 首先看注释,检查propertyMapMap集合是否有所请求的 setter or getter 方法,如果有,返回,如果没有,通过targetClass
// 获得他们,将它们缓存起来并返回
private Method setupSetterOrGetter(Class targetClass,
HashMap<Class, HashMap<String, Method>> propertyMapMap,
String prefix, Class valueType) {
Method setterOrGetter = null; // 定义临时变量
try {
// 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
mPropertyMapLock.writeLock().lock();
HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
if (propertyMap != null) {// 第一次执行,此处propertyMap 为 null
setterOrGetter = propertyMap.get(mPropertyName);
}
if (setterOrGetter == null) {
// Determine the setter or getter function using the JavaBeans convention of setFoo or getFoo for a property
// named 'foo'. This function figures out what the name of the function should be and uses reflection to find the Method
// with that name on the target object.利用反射获取set和get方法
setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
if (propertyMap == null) {
propertyMap = new HashMap<String, Method>();
propertyMapMap.put(targetClass, propertyMap);
}
// 将获得的方法缓存起来
propertyMap.put(mPropertyName, setterOrGetter);
}
} finally {
mPropertyMapLock.writeLock().unlock();
}
return setterOrGetter;
}
setupSetterAndGetter()方法中,setupSetter(targetClass)、setupGetter(targetClass)方法大致分析完了,它完成了对mSetter和mGetter的初始化,接下来,对KeyframeSet的成员变量ArrayList<Keyframe> mKeyframes(上文分析过,在属性动画的对象创建时,就以完成对mKeyframes的初始化,mKeyframes里边放的是根据传入的value构造出的动画执行过程中的帧对象)进行遍历,具体逻辑是:
for (Keyframe kf : mKeyframeSet.mKeyframes) {
if (!kf.hasValue()) { // hasValue()方法定义如下:
public boolean hasValue() {
return mHasValue; // Keyframe的成员变量,boolean mHasValue ,默认是 false
// 上文我们在讲动画创建过程中根据传入的 values 构造出 keyframes 数组中的每一项(关键帧对象Keyframe)时,已经讲过,
// 如果我们只传入了一个参数,那么这个参数将用于构建keyframes[1],走下边的第一个构造函数
// keyframes[0]的值则由ofFloat(0f)来构建,走下边的第二个构造函数,即此关键帧对象的mHasValue为默认值false
FloatKeyframe(float fraction, float value) {
mFraction = fraction;
mValue = value;
mValueType = float.class;
mHasValue = true; // 标示一个帧对象是否已有value值
}
FloatKeyframe(float fraction) {
mFraction = fraction;
mValueType = float.class;
}
}
if (mGetter == null) {
setupGetter(targetClass); // 上文已分析过了
if (mGetter == null) {
// Already logged the error - just return to avoid NPE
return;
}
}
try {
kf.setValue(mGetter.invoke(target));
// 该方法利用通过反射获得的get方法为mKeyframes集合中还没有value值的帧对象赋值
// 上文中,讲到Keyframe 对象的创建时,构造函数 public static Keyframe ofFloat(float fraction)的注释为:
// Constructs a Keyframe object with the given time. The value at this time will be derived from the target object when
// the animation first starts ... ... 指的就是这个地方
public void setValue(Object value) {
if (value != null && value.getClass() == Float.class) {
mValue = ((Float)value).floatValue();
mHasValue = true;
}
}
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
至此,上边第❾步执行完了,它主要是对动画对象的成员变量PropertyValuesHolder[] mValues做更进一步的初始化,接下来执行上文中的第❿步,父类ValueAnimator中定义的initAnimation()方法
/**
* 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. ... ...
*/
// 这个方法在执行动画的第一帧之前被调用,如果有一个不为零的startDelay值,该方法将在相应的延迟时间执后被执行
// 这是一个动画最后的初始化步骤 ... ...
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].init();
/**
* Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used to calculate
animated values.
*/
// 逻辑很简单,就是根据mValueType的值设置成员变量TypeEvaluator mEvaluator 的值,用来calculate
animated values
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);
}
}
}
// 现在,动画最后的初始化已经完成,就将 mInitialized 的值设为 true 了
mInitialized = true;
}
}
到现在,上文中第❼步也完成了,我们可以看到,这一步是在做进一步的初始化,其中对set和get方法的初始化和为没有value值得帧对象赋值的操作是在ObjectAnimator中完成的,而对用来计算动画的value值的TypeEvaluator的初始化则是在ValueAnimator中完成的
稍后再来分析上文中第❽步的doAnimationFrame(currentTime)方法,因此,在ValueAnimator的start(
)方法中,需要重点分析的两个方法之一setCurrentPlayTime(0)就到此为止,接着看后边的animationHandler.start():
animationHandler.start()方法最终会导致AnimationHandler的run方法的执行(此处细节省略):
public void run() {
mAnimationScheduled = false;
// 这是AnimationHandler类中的doAnimationFrame()方法
doAnimationFrame(mChoreographer.getFrameTime());
}
doAnimationFrame( )方法:
private void doAnimationFrame(long 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<ValueAnimator> pendingCopy = (ArrayList<ValueAnimator>) 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);
// 其实,上述代码最主要的就是执行了一句 handler.mAnimations.add(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();
}
// 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();
}
}
可以看到,在AnimationHandler类中,有以下几个集合:
//The per-thread list of all active animations
protected final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>();
//Used in doAnimationFrame() to avoid concurrent modifications of mAnimations
private final ArrayList<ValueAnimator> mTmpAnimations = new ArrayList<ValueAnimator>();
//The per-thread set of animations to be started on the next animation frame
protected final ArrayList<ValueAnimator> mPendingAnimations = new ArrayList<ValueAnimator>();
protected final ArrayList<ValueAnimator> mDelayedAnims = new ArrayList<ValueAnimator>();
private final ArrayList<ValueAnimator> mEndingAnims = new ArrayList<ValueAnimator>();
private final ArrayList<ValueAnimator> mReadyAnims = new ArrayList<ValueAnimator>();
在AnimationHandler类的doAnimationFrame( )方法中,会根据动画的属性值的变化,用这些集合来管理动画对象,并且在这个过程中,会调用到最核心的ValueAnimator类的doAnimationFrame()方法(第 ⓫ 步),当mAnimations.contains(anim)并且doAnimationFrame()方法的返回值为true时,就会执行mEndingAnims.add(anim);将动画对象添加到mEndingAnims集合中,接着,遍历mEndingAnims集合,执行 mEndingAnims.get(i).endAnimation(this);主要是将mAnimations、mPendingAnimations、mDelayedAnims集合中的对象清空以及改变一些标示。标示着动画的结束。
如果doAnimationFrame()方法的返回值为false,
则在满足条件(!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) 时,执行scheduleAnimation(),即相当于调用animationHandler.start()继续循环。
那么doAnimationFrame()方法的逻辑是什么?
又回到了在上文中,当时我们暂时放下没有分析的第❽步中的doAnimationFrame(currentTime)方法上:
final boolean doAnimationFrame(long frameTime) {
if (mPlayingState == STOPPED) { }
if (mPaused) { }
else if (mResumed) { }
// 主要是下边的代码
final long currentTime = Math.max(frameTime, mStartTime);
return animationFrame(currentTime);
}
接着是animationFrame()方法:
boolean animationFrame(long currentTime) {
boolean done = false;
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;
}
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;
}
我们看到,在每一次调用该方法时,都会根据动画对象的一些和时间相关的属性的值来计算fraction的值,来判断要返回true还是false。
从代码中,可以看出,fraction代表的就是动画执行过程中的每一帧在整个动画执行过程中所处的时间的比率。
分析到此,整个属性动画的实现原理基本清楚了,还剩最后一点 ——
每一次调用animationFrame方法时,怎么利用计算出来的fraction来改变动画作用对象的属性值以达到动画的效果?答案是上文中⓬处的animateValue(fraction)方法,需要注意的是,ObjectAnimator类重写了父类的animateValue(fraction)方法,来看具体逻辑:
void animateValue(float fraction) {
super.animateValue(fraction); //
首先调用父类的方法
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setAnimatedValue(mTarget);
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());
}
}
}
}
}
父类ValueAnimator的animateValue(fraction)方法:
void animateValue(float fraction) {
// 此时,我们在文章的开头提到的插值器登场了
fraction = mInterpolator.getInterpolation(fraction);
private TimeInterpolator mInterpolator = sDefaultInterpolator;
private static final TimeInterpolator sDefaultInterpolator = new AccelerateDecelerateInterpolator();
// 可以看到,如果不进行设置的话,默认的插值器就是 AccelerateDecelerateInterpolator
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);
}
}
}
至此,属性动画实现原理基本清楚了。