ViewPropertyAnimator源码解析
Android3.0引入了属性动画。在属性动画中有两个核心类,ValueAnimator和ObjectAnimator,这两个类可以针对任何对象做动画,功能十分强大,在日常开发中多数是对View对象做动画,直接使用ValueAnimator和ObjectAnimator略有些麻烦,Google在Android3.1中有带来了ViewPropertyAnimator,方便开发者做View相关的属性动画。
下面比较一下三个类的使用方式
-
ValueAnimator:
ValueAnimator valueAnimator = ValueAnimator.ofInt(0,90); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int value = (int) animation.getAnimatedValue(); mIvImage.setRotation(value); } }); valueAnimator.setDuration(300); valueAnimator.setInterpolator(new LinearInterpolator()); valueAnimator.start(); 复制代码
-
ObjectAnimator:
ObjectAnimator animator = ObjectAnimator.ofFloat(mIvImage, "rotationX", 0f, 180f); animator.setDuration(300); animator.setInterpolator(new AccelerateDecelerateInterpolator()); animator.start(); 复制代码
-
ViewPropertyAnimator:
mIvImage.animate().rotationY(180).setDuration(300).setInterpolator(new DecelerateInterpolator()).start(); 复制代码
可以看到封装层级越高代码越少,使用越方便,但适用范围也就越窄。
创建ViewPropertyAnimator
只能通过View的animate类创建,ViewPropertyAnimator的构造方法是默认访问权限,在其他包中无法直接访问。
public ViewPropertyAnimator animate() {
if (mAnimator == null) {
mAnimator = new ViewPropertyAnimator(this);
}
return mAnimator;
}
复制代码
使用ViewPropertyAnimator
ViewPropertyAnimator提供了一系列的方法来改变View的属性,可以通过链式调用来执行动画。比如:
mIvImage.animate().translationX(100).translationY(100).rotation(360).setDuration(300).start();
复制代码
每一个改变View属性的方法都提供了两个版本,一种是设置属性的最终值,另一种是设置属性的改变值。旋转View可以使用rotation(degree),也可以使用rotationBy(degree),区别就是rotation(degree)会把View旋转到degree设置的角度,而ratationBy(degree)则会使View在当前旋转角度上再旋转degree设置的角度。
源码分析
通过View的animate方法会创建一个ViewPropertyAnimator对象,在这个对象内会持有一个View引用
ViewPropertyAnimator(View view) {
mView = view;
view.ensureTransformationInfo();
}
复制代码
调用translation,rotationBy等方法时会调用内部的animateProperty或animatePropertyBy方法
public ViewPropertyAnimator rotation(float value) {
animateProperty(ROTATION, value);
return this;
}
public ViewPropertyAnimator rotationBy(float value) {
animatePropertyBy(ROTATION, value);
return this;
}
复制代码
animateProperty方法内部实际上也是调用的animatePropertyBy方法,首先获取当前的属性值,根据目标值计算出变化值,再调用animatePropertyBy方法:
private void animateProperty(int constantName, float toValue) {
float fromValue = getValue(constantName);
float deltaValue = toValue - fromValue;
animatePropertyBy(constantName, fromValue, deltaValue);
}
复制代码
改变View属性的方法最终都会执行到animatePropertyBy方法
private void animatePropertyBy(int constantName, float startValue, float byValue) {
// First, cancel any existing animations on this property
if (mAnimatorMap.size() > 0) {
Animator animatorToCancel = null;
Set<Animator> animatorSet = mAnimatorMap.keySet();
for (Animator runningAnim : animatorSet) {
PropertyBundle bundle = mAnimatorMap.get(runningAnim);
if (bundle.cancel(constantName)) {
// property was canceled - cancel the animation if it's now empty
// Note that it's safe to break out here because every new animation
// on a property will cancel a previous animation on that property, so
// there can only ever be one such animation running.
if (bundle.mPropertyMask == NONE) {
// the animation is no longer changing anything - cancel it
animatorToCancel = runningAnim;
break;
}
}
}
if (animatorToCancel != null) {
animatorToCancel.cancel();
}
}
NameValuesHolder nameValuePair = new NameValuesHolder(constantName, startValue, byValue);
mPendingAnimations.add(nameValuePair);
mView.removeCallbacks(mAnimationStarter);
mView.postOnAnimation(mAnimationStarter);
}
复制代码
这个方法逻辑也比较清晰,在mAnimatorMap中存储的是正在运行的动画,如果当前有正在运行的动画,会先取消动画的执行。然后会创建一个NameValuesHolder对象,这个对象存储动画的信息,包括动画改编的属性,属性的起始值和变化值,再将NameValuesHolder保存到mPendingAnimations中,等待执行。最后会调用 mView.removeCallbacks(mAnimationStarter)和 mView.postOnAnimation(mAnimationStarter)这两个方法,
mAnimationStarter是一个Runnable对象,run方法中会调用startAnimation()方法开始动画。连续调用这两个方法的效果是,当使用mIvImage.animate().translationX(100).translationY(100).rotation(360)这样的链式调用时,每次调用结束都会发起一个开始动画的请求(postOnAnimation(mAnimationStarter)),在请求之前会取消前一个请求,那么在调用最后一方法时会保留一个开始动画的请求,即使不调用start()方法,动画也会在链式结束后开始执行。
static class NameValuesHolder {
int mNameConstant;
float mFromValue;
float mDeltaValue;
NameValuesHolder(int nameConstant, float fromValue, float deltaValue) {
mNameConstant = nameConstant;
mFromValue = fromValue;
mDeltaValue = deltaValue;
}
}
private Runnable mAnimationStarter = new Runnable() {
@Override
public void run() {
startAnimation();
}
};
复制代码
动画真正的开始是在调用startAnimation之后,我们来看下startAnimation的代码
private void startAnimation() {
if (mRTBackend != null && mRTBackend.startAnimation(this)) {
return;
}
mView.setHasTransientState(true);
ValueAnimator animator = ValueAnimator.ofFloat(1.0f);
ArrayList<NameValuesHolder> nameValueList =
(ArrayList<NameValuesHolder>) mPendingAnimations.clone();
mPendingAnimations.clear();
int propertyMask = 0;
int propertyCount = nameValueList.size();
for (int i = 0; i < propertyCount; ++i) {
NameValuesHolder nameValuesHolder = nameValueList.get(i);
propertyMask |= nameValuesHolder.mNameConstant;
}
mAnimatorMap.put(animator, new PropertyBundle(propertyMask, nameValueList));
if (mPendingSetupAction != null) {
mAnimatorSetupMap.put(animator, mPendingSetupAction);
mPendingSetupAction = null;
}
if (mPendingCleanupAction != null) {
mAnimatorCleanupMap.put(animator, mPendingCleanupAction);
mPendingCleanupAction = null;
}
if (mPendingOnStartAction != null) {
mAnimatorOnStartMap.put(animator, mPendingOnStartAction);
mPendingOnStartAction = null;
}
if (mPendingOnEndAction != null) {
mAnimatorOnEndMap.put(animator, mPendingOnEndAction);
mPendingOnEndAction = null;
}
animator.addUpdateListener(mAnimatorEventListener);
animator.addListener(mAnimatorEventListener);
if (mStartDelaySet) {
animator.setStartDelay(mStartDelay);
}
if (mDurationSet) {
animator.setDuration(mDuration);
}
if (mInterpolatorSet) {
animator.setInterpolator(mInterpolator);
}
animator.start();
}
复制代码
在这个方法中首先会创建一个ValueAnimator对象,然后通过mPendingAnimations获取所有的即将改变的属性,通过propertyMask这个int类型保存所有的要改变化的属性,构建一个PropertyBundle对象,保存到mAnimatorMap这个map中。然后会设置一些动画的事件回调,为ValueAnimator设置更新回调,最后开始动画。我们知道ValueAnimator是在UpdateListener根据当前属性值来做动画的。
AnimatorEventlistener是一个内部类,实现了ValueAnimator.AnimatorUpdateListener和Animator.AnimatorListener。我们先看下AnimatorUpdateListener的onAnimationUpdate方法:
public void onAnimationUpdate(ValueAnimator animation) {
PropertyBundle propertyBundle = mAnimatorMap.get(animation);
if (propertyBundle == null) {
// Shouldn't happen, but just to play it safe
return;
}
boolean hardwareAccelerated = mView.isHardwareAccelerated();
// alpha requires slightly different treatment than the other (transform) properties.
// The logic in setAlpha() is not simply setting mAlpha, plus the invalidation
// logic is dependent on how the view handles an internal call to onSetAlpha().
// We track what kinds of properties are set, and how alpha is handled when it is
// set, and perform the invalidation steps appropriately.
boolean alphaHandled = false;
if (!hardwareAccelerated) {
mView.invalidateParentCaches();
}
float fraction = animation.getAnimatedFraction();
int propertyMask = propertyBundle.mPropertyMask;
if ((propertyMask & TRANSFORM_MASK) != 0) {
mView.invalidateViewProperty(hardwareAccelerated, false);
}
ArrayList<NameValuesHolder> valueList = propertyBundle.mNameValuesHolder;
if (valueList != null) {
int count = valueList.size();
for (int i = 0; i < count; ++i) {
NameValuesHolder values = valueList.get(i);
float value = values.mFromValue + fraction * values.mDeltaValue;
if (values.mNameConstant == ALPHA) {
alphaHandled = mView.setAlphaNoInvalidation(value);
} else {
setValue(values.mNameConstant, value);
}
}
}
if ((propertyMask & TRANSFORM_MASK) != 0) {
if (!hardwareAccelerated) {
mView.mPrivateFlags |= View.PFLAG_DRAWN; // force another invalidation
}
}
// invalidate(false) in all cases except if alphaHandled gets set to true
// via the call to setAlphaNoInvalidation(), above
if (alphaHandled) {
mView.invalidate(true);
} else {
mView.invalidateViewProperty(false, false);
}
if (mUpdateListener != null) {
mUpdateListener.onAnimationUpdate(animation);
}
}
复制代码
略过硬件加速的相关处理,会首先通过mAnimatorMap.get(animator)获取到PropertyBundle对象,再获取到mNameValuesHolder列表,这个列表中保存所有要改变的属性信息。使用一个循环取出每一个属性,通过
float value = values.mFromValue + fraction * values.mDeltaValue 这个公式计算出属性的当前的值,再调用
setValue(values.mNameConstant, value)方法,这个方法会根据不同的属性调用方法设置属性值,完成了属性值的变化。
最后梳理一下ViewPropertyAnimator的执行流程:
1.调用View的animate()方法创建ViewPropertyAnimator对象
2.translation(),alpha(),rotation()等方法内部会调用animatePropertyBy方法,将要变化的属性信息保存到NameValuesHolder中,并将NameValuesHolder保存到mPendingAnimations列表中,等待执行。
3.调用startAnimation()方法,这个方法中会创建一个ValueAnimator对象,并设置动画相关信息。
4.在ValueAnimator的onAnimationUpdate的方法中,获取当前动画的执行进度,计算出各个属性的当前值,并调用View的方法设置到View上,完成属性的改变。
除上面介绍的各个方法外,ViewPropertyAnimator还提供了其他辅助方法,如setStartDelay(),setInterpolator()setListener()等,这些方法比较简单,就不一一介绍了。