源码阅读_属性动画执行过程
在Android开发中有很多地方使用到了属性动画,阅读源码是熟悉API以及相关代码最直接的方式。
在平时开发中使用属性动画最多的就是这类的代码
val animator = ObjectAnimator.ofFloat(mView, "alpha", 0f, 100f, 50f)
animator.duration = 1000
animator.startDelay = 300
animator.start()
封装数据
代码中第一句ObjectAnimator.ofFloat(…)
,直接打开源码(ObjectAnimator.java)
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
在ofFloat方法中第一行代码是创建一个ObjectAnimator
对象,构造函数中传入执行动画的View,以及属性名。
private ObjectAnimator(Object target, String propertyName) {
setTarget(target);
setPropertyName(propertyName);
}
打开构造函数的代码,可以看到函数中又调用两个方法,看意思感觉挺明显一个是设置View,一个是设置属性名。
还是看一下setTarget
方法和setPropertyName
方法具体执行了什么逻辑
在setTarget
方法中,
- 先获取mTarget,判断获取的mTarget和目标View是否是同一个控件,很明显之前没有设置过mTarget,所以
if (oldTarget != target)
为true; - 判断动画状态,如果是开始执行动画了就取消;
- 将目标View保存到全局变量mTarget中。
private WeakReference<Object> mTarget;
public Object getTarget() {
return mTarget == null ? null : mTarget.get();
}
@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);
// New target should cause re-initialization prior to starting
mInitialized = false;
}
}
在setPropertyName
方法中
- 判断
mValues
是否为空;(第一次一定为空, 如果不为空,则用当前值替换数组中的第一个值,以及替换mValuesMap
) - 保存属性名至全局变量
mPropertyName
; - 设置初始化标识false
//用于保存属性名的数组
PropertyValuesHolder[] mValues;
//用于保存属性名的HashMap
HashMap<String, PropertyValuesHolder> mValuesMap;
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;
}
这样构造函数执行的内容都搞清楚了,继续看下面设置的代码
animator.duration = 1000
animator.startDelay = 300
这部分源码就很简单了,就是保存属性
// ObjectAnimator类中
@Override
@NonNull
public ObjectAnimator setDuration(long duration) {
super.setDuration(duration);
return this;
}
//父类ValueAnimator中
@Override
public ValueAnimator setDuration(long duration) {
if (duration < 0) {
throw new IllegalArgumentException("Animators cannot have negative duration: " +
duration);
}
mDuration = duration;
return this;
}
注册垂直同步信号
继续下一行代码animator.start()
;
这个方法里面主要操作一个是取消监听,一个是执行父类的start()
方法。
@Override
public void start() {
//AnimationHandler 并不是Android中的Handler,这里可以理解为一个单例工具类
//取消掉animator之前设置的回调
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()
方法中首先后去获取了一个AnimationHandler
对象的单例,再调用这个单例的autoCancelBasedOn()
方法。
其实AnimationHandler
对象并不是Android中的Handler,这里可以理解为一个工具类。
其中的autoCancelBasedOn()
方法中执行的内容大致为遍历取消动画下一帧回调监听。(至于哪里注册、使用到这个监听,下面会讲到)
public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
public static AnimationHandler getInstance() {
if (sAnimatorHandler.get() == null) {
sAnimatorHandler.set(new AnimationHandler());
}
return sAnimatorHandler.get();
}
void autoCancelBasedOn(ObjectAnimator objectAnimator) {
for (int i = mAnimationCallbacks.size() - 1; i >= 0; i--) {
AnimationFrameCallback cb = mAnimationCallbacks.get(i);
if (cb == null) {
continue;
}
if (objectAnimator.shouldAutoCancel(cb)) {
((Animator) mAnimationCallbacks.get(i)).cancel();
}
}
}
取消完监听之后调用了父类的start()
方法,父类的start()
方法中又调用了重载的start(false)
方法。
这个重载的start(false)
方法就比较重要了,一开始它检查了是否在UI线程执行,又设置动画是否反向播放(从来没有用过反向播放),反向播放的操作逻辑,以及一些状态设置等等。
接下来执行了它比较重要的三个方法addAnimationCallback(0)
、startAnimation();
和setCurrentPlayTime(0)
@Override
public void start() {
//重载start的方法 参数是是否反