一、基础
二、属性动画源码分析
以 ObjectAnimator 为例来写一个简单的右移动画
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(iv,"translationX",0,100).setDuration(1 * 1000);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();
1.创建动画
从 ObjectAnimator.ofFloat()开始
/**
* 构建一个返回值为 float 的 ObjectAnimator 的实例
* @param target 作用于动画的对象。
* @param propertyName 属性名称,要求对象须有setXXX() 方法,且是 public 的。
* @param values,属性变化的值
*/
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
ofFloat()
中首先创建一个 ObjectAnimator
的对象,然后设置值。看一下ObjectAnimator
的构造方法:
private ObjectAnimator(Object target, String propertyName) {
setTarget(target);
setPropertyName(propertyName);
}
分别调用了setTarget
和setPropertyName
方法,再看一下setTarget
:
@Override
public void setTarget(@Nullable Object target) {
final Object oldTarget = getTarget();
if (oldTarget != target) {
if (isStarted()) {
cancel();
}
mTarget = target == null ? null : new WeakReference<Object>(target);
// 开始动画前需要重新初始化target
mInitialized = false;
}
}
先判断目标对象是否相同,不相同则判断是否动画在执行,执行就取消动画,将目标对象用弱引用保存。再看下setPropertyName
:
public void setPropertyName(@NonNull 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.
if (mValues != null) {
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getPropertyName();
valuesHolder.setPropertyName(propertyName);
mValuesMap.remove(oldName);
mValuesMap.put(propertyName, valuesHolder);
}
mPropertyName = propertyName;
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
大体是保存propertyName
,并且给PropertyValuesHolder
设置propertyName
并存入map中。对于属性动画来说,其属性相关的变量都被封装在了 PropertyValuesHolder
里。
再回到ofFloat
中,接下来执行了ObjectAnimator
的setFloatValues
方法:
@Override
public void setFloatValues(float... values) {
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);
}
}
当mValues
没有值的时候调用父类的setFloatValues
,父类是ValueAnimator
,setFloatValues
如下:
public void setFloatValues(float... values) {
if (values == null || values.length == 0) {
return;
}
if (mValues == null || mValues.length == 0) {
setValues(PropertyValuesHolder.ofFloat("", values));
} else {
PropertyValuesHolder valuesHolder = mValues[0];
valuesHolder.setFloatValues(values);
}
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
可以看出,不管是否调用父类的 setFloatValues()。最后都是要将 values 逐个构造成 PropertyValuesHolder,最后存放在前面所说的 HashMap 里面。继续来看看 PropertyValuesHolder#ofFloat()
方法:
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
return new FloatPropertyValuesHolder(propertyName, values);
}
创建 FloatPropertyValuesHolder
对象,相应的还有 IntPropertyValuesHolder
、MultiIntValuesHolder
以及MultiFloatValuesHolder
,都是 PropertyValuesHolder
的子类。
public FloatPropertyValuesHolder(String propertyName, float... values) {
super(propertyName);
setFloatValues(values);
}
调用父类的构造方法并传递了 propertyName
,调用 setFloatValues
方法的,其又进一步调用了父类的 setFloatValues
,在父类的 setFloatValues
方法里初始化了动画的关键帧。
public void setFloatValues(float... values) {
mValueType = float.class;
mKeyframes = KeyframeSet.ofFloat(values);
}
调用KeyframeSet.ofFloat(values)
方法,这个方法是将最初传进过来的可变参数,进行重新的封装,并且分为了两种情况,如果可变参数只传递了一个参数,那么就默认添加一个初始关键帧,并将传入的那个参数设置最后一个关键帧。如果已经传递了多个参数,那么就将各个参数设置成关键帧。
然后回到一开始的代码,调用setDuration
@Override
@NonNull
public ObjectAnimator setDuration(long duration) {
super.setDuration(duration);
return this;
}
内部调用了父类的方法,存储下 duration 的值:
@Override
public ValueAnimator setDuration(long duration) {
if (duration < 0) {
throw new IllegalArgumentException("Animators cannot have negative duration: " +
duration);
}
mDuration = duration;
return this;
}
然后执行到objectAnimator.setInterpolator(new LinearInterpolator());
,看下源码:
@Override
public void setInterpolator(TimeInterpolator value) {
if (value != null) {
mInterpolator = value;
} else {
mInterpolator = new LinearInterpolator();
}
}
如果传递的是 null
的话,则默认使用的便是 LinearInterpolator
,即线性插值器。插值器集成自TimeInterpolator
接口,来看下代码:
/**
* 插值器定义了动画变化的频率,其可以是线性的也可以是非线性的,如加速运动或者减速运动。
*/
public interface TimeInterpolator {
/**
* 这里传进来的 input 代表当前时间与总时间的比,根据这个时间占比返回当前的变化频率。其输出与输值都在 [0,1] 之间。
*/
float getInterpolation(float input);
}
来看看 LinearInterpolator
的 getInterpolation
实现:
public float getInterpolation(float input) {
return input;
}
对,就是返回原值,因为时间的变化肯定始终都是匀速的。
到此动画已经创建完毕,下一步就是执行动画了。
2.执行动画
执行动画从start
方法开始,具体代码:
@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();
}
最终调用了父类方法:
@Override
public void start() {
start(false);
}
private void start(boolean playBackwards) {
//需要在有looper的线程执行
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
...
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();
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);
}
}
}
看下addAnimationCallback
方法:
//1.
private void addAnimationCallback(long delay) {
if (!mSelfPulse) {
return;
}
getAnimationHandler().addAnimationFrameCallback(this, delay);
}
/**
* Register to get a callback on the next frame after the delay.
*/
//2.
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
if (mAnimationCallbacks.size() == 0) {
getProvider().postFrameCallback(mFrameCallback);
}
if (!mAnimationCallbacks.contains(callback)) {
mAnimationCallbacks.add(callback);
}
if (delay > 0) {
mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
}
}
主要是向 AnimationHander
添加一个回调接口AnimationHandler.AnimationFrameCallback
,而ValueAnimator
就实现了 AnimationFrameCallback
,传递的this
,具体实现的方法后面会用到,后面再说。
接下来回到start
方法,看下startAnimation
方法,
//在内部调用,通过将动画添加到动画列表来启动动画。必须在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();
}
}
看到内部调用了initAnimation
:
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].init();
}
mInitialized = true;
}
}
mValues
是 PropertyValuesHolder
数组,这里的目的是初始化 PropertyValuesHolder
,再看下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
mKeyframes.setEvaluator(mEvaluator);
}
}
init
方法的主要目的是就是给关键帧设置估值器。因为我们前面调用的是 ObjectAnimator#ofFloat()
方法,所以这里默认给的就是 FloatEvaluator
。这里也来分析一下估值器。
估值器继承自TypeEvaluator
接口:
public interface TypeEvaluator<T> {
public T evaluate(float fraction, T startValue, T endValue);
}
fraction
代表了startValue
到 endValue
之间的比例,startValue
与 endValue
是我们自己在 ofFloat()
调用时设定的那个。返回结果就是一个线性的结果,在 startValue
与 endValue
之间。那么来看看 FloatEvaluator
的实现:
public class FloatEvaluator implements TypeEvaluator<Number> {
public Float evaluate(float fraction, Number startValue, Number endValue) {
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
}
很简单,就是起点值加上当前的段值。
再回到最初的 start
方法里,经 startAnimation
设置了 KeyFrame
的估值器后,接下来就会进一步调用 setCurrentPlayTime
来开始动画:
public void setCurrentPlayTime(long playTime) {
float fraction = mDuration > 0 ? (float) playTime / mDuration : 1;
setCurrentFraction(fraction);
}
初始时调用的是setCurrentPlayTime(0)
,也就是 playTime
为 0,而 mDuration
是我们自己通过 setDuration()
来设置的。所以这里得到的 fraction
也是 0。进一步看 setCurrentFraction
() :
public void setCurrentFraction(float fraction) {
// 再次调用 initAnimation()
initAnimation();
// 校准 fraction 为 [0, mRepeatCount + 1]
fraction = clampFraction(fraction);
mStartTimeCommitted = true; // do not allow start time to be compensated for jank
if (isPulsingInternal()) {
long seekTime = (long) (getScaledDuration() * fraction);
// 获取动画的当前运行时间
long currentTime = AnimationUtils.currentAnimationTimeMillis();
// Only modify the start time when the animation is running. Seek fraction will ensure
// non-running animations skip to the correct start time.
// 得到开始时间
mStartTime = currentTime - seekTime;
} else {
// If the animation loop hasn't started, or during start delay, the startTime will be
// adjusted once the delay has passed based on seek fraction.
mSeekFraction = fraction;
}
mOverallFraction = fraction;
final float currentIterationFraction = getCurrentIterationFraction(fraction, mReversing);
// 执行动画,注意这里会先调用子类的 animateValue() 方法
animateValue(currentIterationFraction);
}
在看子类和父类的animateValue
:
//子类 ObjectAnimator#animateValue()
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;
}
// 调用父类的 animateValue() ,这个很关键,时间插值与估值器的计算都在父类的 animateValue() 方法中进行的。
super.animateValue(fraction);
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
// 这里的 mValues 的是PropertyValuesHolder[],也就是在 PropertyValuesHolder 里面来改变了目标 target 的属性值。
mValues[i].setAnimatedValue(target);
}
}
//父类 ValueAnimator#animateValue()
void animateValue(float fraction) {
// 获取时间插值
fraction = mInterpolator.getInterpolation(fraction);
mCurrentFraction = fraction;
int numValues = mValues.length;
// 将时间插值送给估值器,计算出 values
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);
}
}
}
在看setAnimatedValue
方法,通过反射来调用属性的get和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());
}
}
}
分析到这里,就完成了动画的一帧关键帧的执行。那么你一定会感到奇怪了,剩下的帧是怎么驱动的呢?还是得回到 start
方法里面,在这里最初分析的 addAnimationFrameCallback
方法。ValueAnimator
实现了 AnimationFrameCallback
,实现了doAnimationFrame
方法:
public final boolean doAnimationFrame(long frameTime) {
.....
boolean finished = animateBasedOnTime(currentTime);
if (finished) {
endAnimation();
}
return finished;
}
在看animateBasedOnTime
:
boolean animateBasedOnTime(long currentTime) {
boolean done = false;
if (mRunning) {
.....
float currentIterationFraction = getCurrentIterationFraction(
mOverallFraction, mReversing);
animateValue(currentIterationFraction);
}
return done;
}
这里主要的目的也还是计算出 currentIterationFraction
。然后再通过 animateValue
方法来执行动画。可以看到只要 doAnimationFrame
被不断的调用,就会产生动画的一个关键帧。如果关键帧是连续的,那么最后也就产生了我们所看到的动画
再来分析doAnimationFrame
是如何被不断调用的。这个需要回到 AnimationHandler
中来,在 AnimationHandler
中有一个非常重要的 callback 实现:Choreographer.FrameCallback
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
doAnimationFrame(getProvider().getFrameTime());
if (mAnimationCallbacks.size() > 0) {
getProvider().postFrameCallback(this);
}
}
};
这里监听的是Vsync信号,每 1 秒里收到 60 次回调,在这里就实现了不断地调用 doAnimationFrame() 来驱动动画了。
到这里,属性动画的整个过程就大体走了一遍。