Android - Animation(一) 一文总结了Android中的补间动画(View Animation/Tween
Animation)和帧动画(Drawable Animation/Frame Animation)的使用
本篇文章主要解析属性动画(Property Animation,android3.0引入)的实现原理
下篇 属性动画的实现原理
先来看属性动画的最简单实现:
第一种方式:先在 /res/animator/文件夹下创建translate.xml文件定义动画。再在Java文件里引用并开启
-
-
<?
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>
-
-
-
mButton1 = (Button) findViewById(R.id.button1);
-
mButton1.setOnClickListener(this);
-
-
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) {
-
-
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) {
-
-
-
mTarget = target;
-
setPropertyName(propertyName);
-
}
setPropertyName方法:
-
public void setPropertyName(String propertyName) {
-
-
-
-
-
if (mValues != null) {
-
PropertyValuesHolder valuesHolder = mValues[0];
-
String oldName = valuesHolder.getPropertyName();
-
valuesHolder.setPropertyName(propertyName);
-
mValuesMap.remove(oldName);
-
mValuesMap.put(propertyName, valuesHolder);
-
}
-
-
mPropertyName = propertyName;
-
-
-
-
-
-
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) {
-
-
mPropertyName = propertyName;
-
}
我们注意到,FloatPropertyValuesHolder 、IntPropertyValuesHolder都是PropertyValuesHolder的静态内部类
来看一下PropertyValuesHolder的类定义:
-
-
-
-
-
-
-
-
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类的定义:
对KeyframeSet有一个大概了解之后。再来看一下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之间的较大者,这个比較easy理解
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;
-
-
于是。第❹步也运行完了,第❸步把完毕初始化的FloatPropertyValuesHolder对象返回,第❷步将利用第❸步返回的对象作为參数。运行setValues()方法。(该方法在ObjectAnimator的父类ValueAnimator类中定义),详细逻辑例如以下:
-
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 = values[i];
-
mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
-
}
-
-
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的一部分方法,相同也仅仅拥有部分仅仅属于自己的成员变量,其实。我们在使用属性动画时。所涉及到的类的继承关系例如以下:
-
-
-
-
-
-
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() {
-
-
AnimationHandler handler = sAnimationHandler.get();
-
if (handler != null) { ... ... }
-
super.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) {
-
-
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;
-
}
-
}
-
-
Class targetClass = target.getClass();
-
if (mSetter == null) {
-
-
setupSetter(targetClass);
-
}
-
for (Keyframe kf : mKeyframeSet.mKeyframes) {
-
if (!kf.hasValue()) {
-
if (mGetter == null) {
-
setupGetter(targetClass);
-
if (mGetter == null) {
-
-
-
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) {
-
-
mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType);
-
}
setupGetter(Class targetClass)方法
-
private void setupGetter(Class targetClass) {
-
-
mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
-
}
setupSetter(targetClass) 和 setupGetter(targetClass) 方法都调用了setupSetterOrGetter 方法,仅仅是參数有所不同,第一个和第三个好理解,第二个參数是一个集合,其定义例如以下:
-
-
-
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
方法的主要逻辑:
-
-
-
-
-
-
-
private Method setupSetterOrGetter(Class targetClass,
-
HashMap<Class, HashMap<String, Method>> propertyMapMap,
-
String prefix, Class valueType) {
-
Method setterOrGetter = null;
-
try {
-
-
-
-
mPropertyMapLock.writeLock().lock();
-
HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
-
if (propertyMap != null) {
-
setterOrGetter = propertyMap.get(mPropertyName);
-
}
-
if (setterOrGetter == null) {
-
-
-
-
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.
*/
// 逻辑非常easy。就是依据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;
-
-
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类中。有下面几个集合:
-
-
protected final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>();
-
-
private final ArrayList<ValueAnimator> mTmpAnimations = new ArrayList<ValueAnimator>();
-
-
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);
}
}
}
至此,属性动画实现原理基本清楚了。