ViewPropertyAnimator的源码解析
最近对android中的动画特别感兴趣,可能是因为比较喜欢跟UI相关的东西吧。这篇文章将简单的介绍下ViewPropertyAnimator这个类的源码和一些使用。本文中的例子来自郭神的博客,在文章的最后会奉上三篇郭神的博客,关于属性动画的。非常感谢郭神的博客和书,在android的路上指引我们前进,特此感谢。
简述
ViewPropertyAnimator这个类出现的原因应该是android为了方便我们在使用对于View对象的属性动画的时候而封装的一个类,让我们能够更好的,更简单的使用属性动画。在看文章之前,需要对属性动画有一定的了解,才能更好的弄明白这个类的作用的方便之处。还是推荐看郭神的博客。
我们都知道,在android中动画可以分为这几种,补间动画,帧动画,属性动画,当然了,现在可能还有的就是SVG矢量动画,之后可能会写关于矢量动画的博客。先说前三种,优缺点什么的,怎么用什么的。请自行百度。百度之后可以去看郭神的博客,有三篇关于属性动画的高级使用。这篇文章就是接着郭神没说完的ViewPropertyAnimator来进行讲解的。推荐大家以后做属性动画的时候都采用这种方式,真的是好用。
源码
本人英文不是很好,对于android的理解也一般,如果有不对的地方,请不吝赐教。
先说下构造方法,因为这个类是专门用来处理View的属性动画的,所以它对象的创建必须跟View有关。
/**
* This method returns a ViewPropertyAnimator object, which can be used to animate
* specific properties on this View.
*
* @return ViewPropertyAnimator The ViewPropertyAnimator associated with this View.
*/
public ViewPropertyAnimator animate() {
if (mAnimator == null) {
mAnimator = new ViewPropertyAnimator(this);
}
return mAnimator;
}`
我们可以通过view.animate()方法来获得一个ViewPropertyAnimator对象,之后就可以使用ViewPropertyAnimator中的方法来实现我们想要的动画。注意ViewPropertyAnimator中的方法返回值都是ViewPropertyAnimator对象,也就是说我们可以通过连缀的方式来实现对动画的设置。
接下来让我们来看下ViewPropertyAnimator这个类里面都有什么,先看属性。
`
//关联的view对象
private final View mView;
//动画时长
private long mDuration;
//是否设置动画时长
private boolean mDurationSet = false;
//动画开始延迟
private long mStartDelay = 0;
//是否设置动画开始延迟
private boolean mStartDelaySet = false;
//差值器
private TimeInterpolator mInterpolator;
//是否设置差值器
private boolean mInterpolatorSet = false;
//动画监听器
private Animator.AnimatorListener mListener = null;
//动画监听器(私有内部类,之后会讲到)
private AnimatorEventListener mAnimatorEventListener = new AnimatorEventListener();
//NameValuesHolder的集合
ArrayList<NameValuesHolder> mPendingAnimations = new ArrayList<NameValuesHolder>();
//不同动作所执行的不同的任务类
private Runnable mPendingSetupAction;
private Runnable mPendingCleanupAction;
private Runnable mPendingOnStartAction;
private Runnable mPendingOnEndAction;
//View的常见属性
private static final int NONE = 0x0000;
private static final int TRANSLATION_X = 0x0001;
private static final int TRANSLATION_Y = 0x0002;
private static final int SCALE_X = 0x0004;
private static final int SCALE_Y = 0x0008;
private static final int ROTATION = 0x0010;
private static final int ROTATION_X = 0x0020;
private static final int ROTATION_Y = 0x0040;
private static final int X = 0x0080;
private static final int Y = 0x0100;
private static final int ALPHA = 0x0200;
private static final int TRANSFORM_MASK = TRANSLATION_X | TRANSLATION_Y | SCALE_X | SCALE_Y |
ROTATION | ROTATION_X | ROTATION_Y | X | Y;
//开启动画的任务
private Runnable mAnimationStarter = new Runnable() {
@Override
public void run() {
startAnimation();
}
};
/**
* This class holds information about the overall animation being run on the set of
* properties. The mask describes which properties are being animated and the
* values holder is the list of all property/value objects.
*这个类包含关于在集合中运行的全局动画的信息。该掩码描述了正在动画中的属性和值保持器是所有属性/值对象的列表。
*百度翻译。。。。。
*/
private static class PropertyBundle {
int mPropertyMask;
ArrayList<NameValuesHolder> mNameValuesHolder;
PropertyBundle(int propertyMask, ArrayList<NameValuesHolder> nameValuesHolder) {
mPropertyMask = propertyMask;
mNameValuesHolder = nameValuesHolder;
}
//是否成功取消动画
boolean cancel(int propertyConstant) {
if ((mPropertyMask & propertyConstant) != 0 && mNameValuesHolder != null) {
int count = mNameValuesHolder.size();
for (int i = 0; i < count; ++i) {
NameValuesHolder nameValuesHolder = mNameValuesHolder.get(i);
if (nameValuesHolder.mNameConstant == propertyConstant) {
mNameValuesHolder.remove(i);
mPropertyMask &= ~propertyConstant;
return true;
}
}
}
return false;
}
}
/**
* This list tracks the list of properties being animated by any particular animator.
* In most situations, there would only ever be one animator running at a time. But it is
* possible to request some properties to animate together, then while those properties
* are animating, to request some other properties to animate together. The way that
* works is by having this map associate the group of properties being animated with the
* animator handling the animation. On every update event for an Animator, we ask the
* map for the associated properties and set them accordingly.
*/
//这个大家自行翻译吧,我怕我翻译不好说错了影响大家,表面上就是五个Map分别保存着PropertyBundle
//和不同的任务对象
private HashMap<Animator, PropertyBundle> mAnimatorMap =
new HashMap<Animator, PropertyBundle>();
private HashMap<Animator, Runnable> mAnimatorSetupMap;
private HashMap<Animator, Runnable> mAnimatorCleanupMap;
private HashMap<Animator, Runnable> mAnimatorOnStartMap;
private HashMap<Animator, Runnable> mAnimatorOnEndMap;
//这个类持有着我们想要更改的属性和对应的值。可以这么理解为比如alpha 0f,1f
private static class NameValuesHolder {
int mNameConstant;
float mFromValue;
float mDeltaValue;
NameValuesHolder(int nameConstant, float fromValue, float deltaValue) {
mNameConstant = nameConstant;
mFromValue = fromValue;
mDeltaValue = deltaValue;
}
}
/**
* Constructor, called by View. This is private by design, as the user should only
* get a ViewPropertyAnimator by calling View.animate().
*构造方法,被View调用,设计成私有的是为了使用者只能通过View.animate()方法调用来获得一个ViewPropertyAnimator对象
* @param view The View associated with this ViewPropertyAnimator
*/
ViewPropertyAnimator(View view) {
mView = view;
view.ensureTransformationInfo();
}
`
好了,通过上面的代码。我们对ViewPropertyAnimator这个类有了一个初步的认识,基本的属性都有一个简单的认识(哎,英文不好见谅),接下来我们来看看它都有哪些方法。
大家可以看到在图片中有很多对View属性进行设置的方法,而且方法的返回值都是ViewpropertyAnimator对象,这样我能就可以使用连缀了。找其中一个看看。
public ViewPropertyAnimator alpha(float value) {
animateProperty(ALPHA, value);
return this;
}
可以看到其中执行了animateProperty这个方法,跟踪过去看看。
/**
* Utility function, called by animateProperty() and animatePropertyBy(), which handles the
* details of adding a pending animation and posting the request to start the animation.
*
* @param constantName The specifier for the property being animated
* @param startValue The starting value of the property
* @param byValue The amount by which the property will change
*/
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.post(mAnimationStarter);
}`
其中开始的部分是把之前在运行的动画取消掉,然后把需要对view那个属性进行修改和要修改的值封装到nameValuePair中,添加到mPendingAnimations,最后调用View的Post方法在主线程中执行。而动画的开始是在mAnimationStarter这个任务对象中的,可以看下之前的代码就会发现其中调用了startAnimation()这个方法,我们来看看是怎么执行的。
`
/**
* Starts the underlying Animator for a set of properties. We use a single animator that* simply runs from 0 to 1, and then use that fractional value to set each property
* value accordingly.
*/
private void startAnimation() {
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();
}`
可以看到其中对之前的属性进行了判断,如果不为null的话就添加到Map中进行统一的管理,最后加上监听,设置常用属性,这样这个动画就开始了。当然了,一个ViewPropertyAnimator肯定不止于此它还有许多能够丰富动画效果的方法比如withLayer,withStartAction,withEndAction等,不过我们日常需求所要用的方法上面你都有介绍了,而且这个动画会自己启动不需要我能去调用Start()方法来启动动画,当然也可以显示的调用来执行,也支持去掉,同样支持动画的实时监听。
textview.animate().x(500).y(500).setDuration(5000)
.setInterpolator(new BounceInterpolator());
常用的调用方式,直接点就可以,不需要更多的操作。
总结
ViewPropertyAnimator这个类多与我们平时写属性的动画真的帮助很大,而且更加方便使用,易于理解,,代码看起来也更加的美观。但它只是针对于view的,不是之前使用的valueAnimator那样可以针对所有的对象,所以如果在之后的工作中如果想要更改View的属性的话推荐用它,如果是其他的不是view的就需要使用ValueAnimator来实现控制。总体来说还是很好用的 ^_^.
参看资料
关于属性动画,也许你只需要弄明白三篇博客就可以随心所欲了。
http://blog.csdn.net/guolin_blog/article/details/43536355
http://blog.csdn.net/guolin_blog/article/details/43816093
http://blog.csdn.net/guolin_blog/article/details/44171115
感觉高级属性动画还有一个东西至关重要,那就是会写差值器。好了这篇博客就到这里了,有不对的地方欢迎指出。