属性动画动画系列从开始到现在,其他类例如ValueAnimator、ObjectAnimator还有AnimatorSet等的介绍挺多的,但是关于ViewPropertyAnimator这个类的介绍似乎比较少,所以本文详细的介绍一下这个类(主要是这个类的内部实现)。
ViewPropertyAnimator这个类能够对View对象进行自动添加和优化属性动画(用来便捷方式处理一系列View对象的属性动画的类)。
这个类提供了链式调用进行多个属性同时变化的动画,更加简洁方便我们操作组合动画(多个属性同时进行变化)。
这个类可以为同时动画提供更好的性能。这个类在动画进行的时候只对多个属性的变化进行一次invalidate调用,而不是对变化每个属性进行调用(n个ObjectAnimator就会进行n次属性变化,就有n次invalidate)。当然这个类的调用方式更方便,调用对应属性方法传一个属性值就可以自动实现动画。每个属性方法都有两种调用形式,例如 alpha(float value) 和alphaBy(float value),前者是变化到多少,后者是变化多少。
这个类没用提供公开的构造方法,通过调用view的animate()获取引用。
上面说的比较拗口, 总结 起来就是:
imageView.animate()
.setDuration(4000)
.rotationY(45f)
.translationX(imageView.getWidth())
.alpha(0f);
如上,使用很方便,对imageView设置三个属性的变化,同时进行动画。
等价的ObjectAnimator形式是这样的:
new AnimatorSet().playTogether(
ObjectAnimator.ofFloat(imageView, "translationX", imageView.getWidth()),
ObjectAnimator.ofFloat(imageView, "alpha", 0),
ObjectAnimator.ofFloat(imageView, "rotationY", 45f)
);
前者明显 更简洁 。
ViewPropertyAnimator使用很简单,那么来看看它是怎么实现的。
ArrayList
Runnable mAnimationStarter: 用来执行动画的Runnable。它会执行startAnimation方法,启动动画。
HashMap
private void animatePropertyBy(int constantName, float startValue, float byValue) {
// 移除正在进行动画的对应的这个属性
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)) {// 移除对应属性
if (bundle.mPropertyMask == NONE) {// 判断还有其他属性没
animatorToCancel = runningAnim;
break;
}
}
}
if (animatorToCancel != null) {
animatorToCancel.cancel();
}
}
// 创建这个属性的NameValuesHolder对象,放到mPendingAnimations列表中
NameValuesHolder nameValuePair = new NameValuesHolder(constantName, startValue, byValue);
mPendingAnimations.add(nameValuePair);
// 执行动画
mView.removeCallbacks(mAnimationStarter);
mView.postOnAnimation(mAnimationStarter);
}
这个方法 主要完成 三件事:
如果当前已经存在正在运行的Animator的话,并且这个Animator包含这个属性的话,将这个Animator的这个属性移除,移除完之后如果这个Animator没有属性了就结束它。这个操作是保证View对象的一个属性只有一个动画在操作它。
将属性和属性值变化装到NameValuesHolder类中,然后放到mPendingAnimations列表中。以便在之后放到mAnimatorMap中。
取消并且重新启动mAnimationStarter就是启动动画,并且保证链式设置的属性是同时进行的。
private void startAnimation() {
if (mRTBackend != null && mRTBackend.startAnimation(this)) {
return;
}
mView.setHasTransientState(true);
// 创建简单的animator,变化值从0到1
ValueAnimator animator = ValueAnimator.ofFloat(1.0f);
// 拷贝mPendingAnimations并且清除原有的mPendingAnimations
ArrayList<NameValuesHolder> nameValueList =
(ArrayList<NameValuesHolder>) mPendingAnimations.clone();
mPendingAnimations.clear();
// 迭代拷贝之后的mPendingAnimations获取属性值标记propertyMask
int propertyMask = 0;
int propertyCount = nameValueList.size();
for (int i = 0; i < propertyCount; ++i) {
NameValuesHolder nameValuesHolder = nameValueList.get(i);
propertyMask |= nameValuesHolder.mNameConstant;
}
// 将这个animator放入到mAnimatorMap。
mAnimatorMap.put(animator, new PropertyBundle(propertyMask, nameValueList));
...
// 设置监听器器
animator.addUpdateListener(mAnimatorEventListener);
animator.addListener(mAnimatorEventListener);
if (mStartDelaySet) {
animator.setStartDelay(mStartDelay);
}
if (mDurationSet) {
animator.setDuration(mDuration);
}
if (mInterpolatorSet) {
animator.setInterpolator(mInterpolator);
}
// 启动动画
animator.start();
}
在animatePropertyBy方法中执行mView.postOnAnimation(mAnimationStarter)会触发startAnimation方法,startAnimation方法 主要 做了这些事情:
public void onAnimationUpdate(ValueAnimator animation) {
PropertyBundle propertyBundle = mAnimatorMap.get(animation);
...
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 (alphaHandled) {
mView.invalidate(true);
} else {
mView.invalidateViewProperty(false, false);
}
...
}
这个方法完成了属性值的设置。通过mAnimatorMap获取对应的所有属性,计算变化之后的属性,然后设置给对应的属性,再进行更行UI。
我们再次回过来看看我们前面是怎么使用的。
imageView.animate()
.setDuration(4000)
.rotationY(45f)
.translationX(imageView.getWidth())
.alpha(0f);
通过imageView.animate()获取ViewPropertyAnimator类对象。调用rotationY、translationX、alpha设置属性动画。
我们看看alpha方法的 实现 :
public ViewPropertyAnimator alpha(float value) {
animateProperty(ALPHA, value);
return this;
}
private void animateProperty(int constantName, float toValue) {
float fromValue = getValue(constantName);
float deltaValue = toValue - fromValue;
animatePropertyBy(constantName, fromValue, deltaValue);
}
最终调用了 animatePropertyBy(int constantName, float startValue, float byValue)方法
这样我们就可以将这个ViewPropertyAnimator类的使用 串起来了 :