整体架构
- Property:属性对象,主要定义属性的set和get方法
- PropertyValueHolder:持有目标属性Property,setter和getter方法以及关键帧集合的类
- KeyframeSet:存储一个动画的关键帧集合
流程分析
创建Animator相关的类
这里以我们经常用ObjectAnimator.ofFloat创建动画进行分析
流程
源码分析
- 使用ofFloat创建对象Animator对象
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
- 然后调用setFloatValues,其根据是否设置mProperty,创建PropertyValuesHolder
- 我们调用动画,一般传递属性名字
setFloatValues(..){
...
if(mVlaus == null || mValuse.length == 0){
setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
} else {
super.setFloatValues
}
...
}
//如果之前有,
- 创建PropertyValuesHolder,因为是float,所以创建其子类FloatPropertyValuesHolder
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
return new FloatPropertyValuesHolder(propertyName, values);
}
- 在FloatPropertyValuesHolder构造方法,会调用setFloatValues,
- 在其内部调用父类ValueAnimator的setFloatValues,会创建KeyframeSet
- 其调用KeyframeSet.ofFloat
public void setFloatValues(float... values) {
mValueType = float.class;
mKeyframes = KeyframeSet.ofFloat(values);
}
public static KeyframeSet ofFloat(float... values) {
...
new FloatKeyframeSet(keyframes);
...
}
总结
- 属性动画通过PropertyValuesHolder,保存动画执行过程中的属性和其值,和其setter和getter,
- ObjectAnimator会通过调用PropertyValuesHolder.setAnimatedValue更新响应的属性值
- KeyFromeSet用于保存对象的关键帧集合,然后ObjectAnimator会通过其计算动画的值
到这里,动画相关的核心类就创建完成了,接下来就是一些配置,插值器,执行时间,重复模式等。这里不做分析,下面分析动画启动过程中的一些核心逻辑
启动Animator
启动流程图
源码分析
我们直接从ValueAnimator.start开始分析
- 其进行一些检测和状态设置后,调用addAnimationCallback方法
- 然后判断是否延迟,调用startAnimation进行一些初始化配置
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
...
mStarted = true;
mPaused = false;
mRunning = false;
...
addAnimationCallback(0)
if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
startAnimation()
}
private void startAnimation(){
initAnimation();
mRunning = true;
}
接下里我们重点分析addAnimationCallback
- 其内部调用AnimationHanlder的addAnimationFrameCallback
getAnimationHandler().addAnimationFrameCallback(this, delay);
==AnimationHandler==不是Handler的子类,其内部通过Choreographer的hanlder去发送消息到UI线程
- 在addAnimationFrameCallback,首先会判断动画的回调数组的大小,0的化,调用postFrameCallback,发送AnimationHanler的内部mFrameCallback,初始动画的执行
//里面的参数,其为AnimationHandler的成员
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
doAnimationFrame(getProvider().getFrameTime());
if (mAnimationCallbacks.size() > 0) {
getProvider().postFrameCallback(this);
}
}
};
if (mAnimationCallbacks.size() == 0) {
getProvider().postFrameCallback(mFrameCallback);
}
- 然后不过是否为0,最后到会将其添加到mAnimationCallbacks中,如果是延时的,还会添加到mDelayedCallbackStartTime集合中
if (!mAnimationCallbacks.contains(callback)) {
mAnimationCallbacks.add(callback);
}
if (delay > 0) {
mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
}
-这是上面用到的集合的创建,我们添加的回调,根据情况,会添加到不同数组,其中mAnimationCallbacks要重点注意,后续我们分析动画如何重复计算值,其是关键
private final ArrayMap<AnimationFrameCallback, Long> mDelayedCallbackStartTime =
new ArrayMap<>();
private final ArrayList<AnimationFrameCallback> mAnimationCallbacks =
new ArrayList<>();
private final ArrayList<AnimationFrameCallback> mCommitCallbacks =
new ArrayList<>();
- 对于第一次,其会postFrameCallback,这里先说明其是通过handler发送消息,然后进行一些处理,会被回调执行,这了我们先不分析这个过程,直接看其回调方法的调用
- 我们看到如果mAnimationCallbacks大小不为0,其就会再次发送,然后再次被回调,这样动画就一直重复计算属性,当动画执行完,其大小为0,就不会在被发送,这就是其重复执行的逻辑
public void doFrame(long frameTimeNanos) {
doAnimationFrame(getProvider().getFrameTime());
if (mAnimationCallbacks.size() > 0) {
getProvider().postFrameCallback(this);
}
}
- 接下来我们看doAnimationFrame,做了什么,其是如何更新动画的属性值的
- 其内部会调用callback.doAnimationFrame(frameTime),
long currentTime = SystemClock.uptimeMillis();
final int size = mAnimationCallbacks.size();
for (int i = 0; i < size; i++) {
final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
if (callback == null) {
continue;
}
if (isCallbackDue(callback, currentTime)) {
callback.doAnimationFrame(frameTime);
}
- 这个CallBack是我们之前在ValueAnimator中添加的,我们回头看一下它,发现其就是ValueAnimator
getAnimationHandler().addAnimationFrameCallback(this, delay);
- 接下来我们看ValueAnimator的doAnimationFrame
- 其调用animateBasedOnTime根据时间计算值,然后会返回是否完成的标志finished
- 完成的化,调用endAnimation
...
boolean finished = animateBasedOnTime(currentTime);
if (finished) {
endAnimation();
}
...
我们看动画完成是做了什么处理
- endAnimation内部调用removeAnimationCallback
- removeAnimationCallback最终调用AnimationHandler的removeCallback
- 发现其并没有删除,只是将给位置置为null,那其在什么清除呢
public void removeCallback(AnimationFrameCallback callback) {
mCommitCallbacks.remove(callback);
mDelayedCallbackStartTime.remove(callback);
int id = mAnimationCallbacks.indexOf(callback);
if (id >= 0) {
mAnimationCallbacks.set(id, null);
mListDirty = true;
}
}
我们会找到一个cleanUpList方法,其会对null元素进行移除,而起在执行完我们的ValueAnimator.doAnimationFrame会调用
private void doAnimationFrame(long frameTime) {
...
cleanUpList()
}
private void cleanUpList() {
if (mListDirty) {
for (int i = mAnimationCallbacks.size() - 1; i >= 0; i--) {
if (mAnimationCallbacks.get(i) == null) {
mAnimationCallbacks.remove(i);
}
}
mListDirty = false;
}
}
通过动画执行完的处理,我们可以学习到,移除大量元素是的一种方法,同时也知道动画执行完后mAnimationCallbacks的大小会变为0,这样就不会重复执行了。
我们接着上面结束动画前的分析,即动画为执行完,其最终如何更新我们target的属性值,我们回到ValueAnimator.doAnimationFrame中,我们知道其通过animateBasedOnTime会返回是否结束标志,我们从此分析如何计算动画执行过程中的值
boolean animateBasedOnTime(long currentTime) {
boolean done = false;
//判断是否在允许中,进行一些处理
if (mRunning) {
//代码省略
mOverallFraction = clampFraction(fraction);
//当前遍历的比例,其会处理反向执行是的值,
float currentIterationFraction = getCurrentIterationFraction(
mOverallFraction, mReversing);
//在当前比例下的动画值
animateValue(currentIterationFraction);
}
return done;
}
- 在animateValue中,其会遍历调用所有设置得PropertyValueHolder,然后调用其caculateValue方法,然后调用动画更新监听器
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);
}
}
- PropertyValueHolder会调用FloatKeyframeSet的getFloatValue方法
void calculateValue(float fraction) {
mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
}
但是到这里,我们只是找到了如何计算动画执行过程的值,没有看到更新方法的调用,那是因为对于ValueAnimator,我们需要在更新监听其里自己设置,我们一般使用的是objectAnimator,其对animateValue进行了重写
- 首先判断目标是否存在,如果存在会调用父类的方法,即ValueAnimator的animateValue方法,计算值,
- 然后调用ProperValueHolder的setAnimatedValue
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);
}
- 在PropertyValueHolder中setAnimatedValue通过反射调用我们的设置方法
if (mProperty != null) {
mProperty.set(target, getAnimatedValue());
}
if (mSetter != null) {
try {
mTmpValueArray[0] = getAnimatedValue();
//这里进行调用set方法
mSetter.invoke(target, mTmpValueArray);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
总结
- 动画的不断循环通过判断动画回调数组大小是否大于零,不断发送消息,实现循环
- 动画执行完后,其会进行移除回调,回调是在全部动画回调执行完,统一移除
- 动画值的计算是最终是通过KeyFrameSet进行计算
- 动画对目标属性的改变通过PropertyValueHolder中setAnimatedValue方法,最终反射调用设置方法实现
- 属性动画没有触发重绘逻辑,所以需要我们在set方法里调用,才能更新View.
Hanlder发送消息
最后,我们看一下在AnimationHandler中,最后如何回调mFameCallback,这里只展示流程图,就不分析代码了,重点在流程。就是调用Choreographer中的FrameHandler,发送消息,对不同情况进行处理,最终调用我们的mFameCallback,细节方面后面有时间在分析。
流程图
属性动画的总结和优点
- 如果我们只是改变view的大小,平移,透明,旋转,可以使用ViewProperAnimator,系统对其进行了优化,比我们直接使用ObjectAnimator性能要好,具体细节可以看这篇官方博客
- 其真实改变了View的属性,
- 系统使用ProperyValueHolder和keyFrameSet计算和更新值,然后值更新后,我们手动重绘,比补间动画性能好且更灵活。