一、Property动画简介:
Property动画系统是一个很强大的框架,几乎可以让任何东西产生动画效果。可以定义一个动画随着时间的推移来改变任何对象的属性,无论它被绘制到屏幕上与否。其主要的Animator有:ValueAnimator、ObjectAnimator和AnimatorSet。
Property动画系统允许定义一个动画的下列属性:
1)Duration:动画持续时间,默认值为300ms。
2)Time interpolation:时间插值,你可以把属性的值如何被计算指定为动画当前逝去时间的函数。
3)Repeat count and behavior:重复次数和行为。你可以指定是否让一个动画重复当它到达持续时间的结束处时,以及重复动画多少次。你还可以指定你是否希望动画反向回放。设置它以向前然后向后地重复反向播放动画,直至到达重复次数。
4)Animator sets:动画集,你可以分组动画进逻辑集合,它们同时地或依次地或在指定延迟后播放。
5)Frame refresh delay:帧刷新延迟,可以指定多久刷新动画的帧,默认是设置每隔10ms刷新,但你的应用程序刷新帧的速度最终依赖于系统整体有多繁忙,以及系统可以多快地提供底层定时器。
二、Property动画如何工作:
这里用一个简单的列子来说明,假如有一个对象在X轴上利用动画使其匀速移动,那么X就相当于此对象的一个property,设定每隔10ms刷新一次,总共移动40ms。那么到移动动画结束时,此对象在此过程中是如何表现的?如图:
当然也可以让此动画过程非匀速移动,就如View动画里面的匀速或者先匀速再加速然后再匀速一样(匀速对应linearInterpolator,后者对应accelerateDecelerateInterpolator):
那么对于上面的列子,属性动画的实现有哪些重要组成部分,下图就很好描述了这点,并展示了主要类如何与另一个类工作的。
ValueAnimator跟踪动画的时间,比如:动画运行了多长时间。ValueAnimator封装了TimeInterpolator,它定义动画的插值(interpolation)和TypeEvaluator,TypeEvaluator定义了如何计算被实现动画的属性的值。这部分可以查看源码:
...
// The time interpolator to be used if none is set on the animation
private static final TimeInterpolator sDefaultInterpolator =
new AccelerateDecelerateInterpolator();
...
private TimeInterpolator mInterpolator = sDefaultInterpolator;
...
/**
* The time interpolator used in calculating the elapsed fraction of this animation. The
* interpolator determines whether the animation runs with linear or non-linear motion,
* such as acceleration and deceleration. The default value is
* {@link android.view.animation.AccelerateDecelerateInterpolator}
*
* @param value the interpolator to be used by this animation. A value of <code>null</code>
* will result in linear interpolation.
*/
@Override
public void setInterpolator(TimeInterpolator value) {
if (value != null) {
mInterpolator = value;
} else {
mInterpolator = new LinearInterpolator();
}
}
三、属性动画和View动画有什么不同:
1)View动画仅支持View对象,而让View对象实现动画的同时并不能让view对象的背景颜色等也发生变换。
2)View动画的另一个缺点是:它只改变视图被绘制的部分,而不是实际的视图本身。举个例子,如果你实现一个动画,让一个Button在屏幕上移动,Button是被正确的绘制,但是你能点击Button的地方却没有变化。为此,你还必须实现自己的逻辑来解决这个问题。
3)对于属性动画,上面提到的那些限制就没有了,可以为属性动画设置任何对象(包括视图和非视图对象)并且对象的任何属性也随着变换。属性动画在其执行动画的方式上也更加强大。在一个较高的水平,指定动画给你想要实现动画的属性,如:颜色、位置和大小,并可以定义动画的各个方面,如插值和多动画同步。
4)属性动画需要较少的时间来建立动画,并且需要更少的代码来实现。如果视图动画完成了你需要做的任何事,或者你现有的代码已经能按照你想要的方式工作,那么就没必要使用属性动画,当然,可以根据不同的条件使用这两种动画。
四、API概要:
1)Animators: ValueAnimator、ObjectAnimator和AnimatorSet。
2)Evaluators:
Class/Interface | Description |
---|---|
IntEvaluator | 计算int属性值的默认evaluator |
FloatEvaluator | 计算float属性值的默认evaluator |
ArgbEvaluator | 计算color属性值的默认evaluator,为16进制。 |
TypeEvaluator | 一个接口,允许你创建自己的Evaluator,如果你实现对象动画的属性不是int,float或者color,你必须实现此接口,用来指定如何计算对象属性的动画值,当然,你也可以为int,float和color自定义TypeEvaluator,如果你想进行的不同于这些types的默认行为。 |
3)Interpolators:可以参考Android-View Animation(视图动画)。相对于View动画,多了一个TimeInterpolator插值器(一个接口,允许你实现自己的Interpolator)。
五、属性动画实现体验:
1)使用ValueAnimator实现动画:
ValueAnimator提供了3个方法来获得一个ValueAnimator对象:ofInt()、ofFloat()、
ofObject()。以ofFloat为例来实现一个属性动画:
private void startValueAnimation(){ if(mValueAnimator == null){ mValueAnimator = ValueAnimator.ofFloat(0, 500); } mValueAnimator.setInterpolator(new AnticipateInterpolator()); mValueAnimator.setTarget(mImageView); mValueAnimator.setDuration(3000); mValueAnimator.setRepeatCount(1); mValueAnimator.start(); mValueAnimator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mImageView.setTranslationX((Float) animation.getAnimatedValue()); mImageView.setTranslationY((Float) animation.getAnimatedValue()); } }); }
![]()
![](https://img-blog.csdn.net/20151201233051658)
注意:setRepeatCount()方法,设置为1的时候,动画重复是2次,因为默认是0,表示只重复一次。那么设置1的时候就是重复2次,具体逻辑可看源码。
2)使用ObjectAnimator实现动画:
private void startObjectAnimation() { ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mImageView, "translate", 0, 500); objectAnimator.setInterpolator(new AnticipateInterpolator()); objectAnimator.setDuration(3000); objectAnimator.setRepeatCount(1); objectAnimator.start(); objectAnimator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mImageView.setTranslationX((Float) animation.getAnimatedValue()); mImageView.setTranslationY((Float) animation.getAnimatedValue()); } }); }
从代码上看实现同样的动画,ObjectAnimator和ValueAnimator相差不大,而事实上ObjectAnimator是继承于ValueAnimator的。
3)使用AnimatorSet:
AnimatorSet大多数情况下都是被用来处理多个动画,比如:要播放一个动画,但是这个动画必须在另外一个动画播放结束后才开始,这个时候就需要AnimatorSet登场了。
举个例子来说明一下:假如要让一个对象移动轨迹呈正方形,就可以先水平向右移动,再垂直向下移动,再水平向左移动,最后垂直向上移动到起始位置,那么这个过程中的先后顺序就可以使用AnimatorSet来控制,如实现代码:
private void startAnimatorSet(){ AnimatorSet animatorSet = new AnimatorSet(); animatorSet.play(gethValueAnimatorOne()).before(getvValueAnimatorOne()); animatorSet.play(getvValueAnimatorOne()).before(gethValueAnimatorTwo()); animatorSet.play(gethValueAnimatorTwo()).before(getvValueAnimatorTwo()); animatorSet.start(); } private ValueAnimator gethValueAnimatorOne(){ if(hValueAnimatorOne == null) { hValueAnimatorOne = ValueAnimator.ofFloat(0, 500); } hValueAnimatorOne.setInterpolator(new LinearInterpolator()); hValueAnimatorOne.setTarget(mImageView); hValueAnimatorOne.setDuration(2000); hValueAnimatorOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mImageView.setTranslationX((Float) animation.getAnimatedValue()); } }); return hValueAnimatorOne; } private ValueAnimator getvValueAnimatorOne(){ if(vValueAnimatorOne == null){ vValueAnimatorOne = ValueAnimator.ofFloat(0, 500); } vValueAnimatorOne.setInterpolator(new LinearInterpolator()); vValueAnimatorOne.setTarget(mImageView); vValueAnimatorOne.setDuration(2000); vValueAnimatorOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mImageView.setTranslationY((Float) animation.getAnimatedValue()); } }); return vValueAnimatorOne; } private ValueAnimator gethValueAnimatorTwo(){ if(hValueAnimatorTwo == null){ hValueAnimatorTwo = ValueAnimator.ofFloat(500, 0); } hValueAnimatorTwo.setInterpolator(new LinearInterpolator()); hValueAnimatorTwo.setTarget(mImageView); hValueAnimatorTwo.setDuration(2000); hValueAnimatorTwo.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mImageView.setTranslationX((Float) animation.getAnimatedValue()); } }); return hValueAnimatorTwo; } private ValueAnimator getvValueAnimatorTwo(){ if(vValueAnimatorTwo == null){ vValueAnimatorTwo = ValueAnimator.ofFloat(500, 0); } vValueAnimatorTwo.setInterpolator(new LinearInterpolator()); vValueAnimatorTwo.setTarget(mImageView); vValueAnimatorTwo.setDuration(2000); vValueAnimatorTwo.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mImageView.setTranslationY((Float) animation.getAnimatedValue()); } }); return vValueAnimatorTwo; }
效果如图:
![](https://img-blog.csdn.net/20151201234154212)
![]()
当然AnimatorSet不仅可以控制先后,还可以让两个动画同时展现出来,依然使用上面例子,让对象向右移动的时候同时旋转,如实现代码:
private void startAnimatorSet(){ AnimatorSet animatorSet = new AnimatorSet(); animatorSet.play(gethValueAnimatorOne()).with(getmObjectAnimator()); animatorSet.play(gethValueAnimatorOne()).before(getvValueAnimatorOne()); animatorSet.play(getvValueAnimatorOne()).before(gethValueAnimatorTwo()); animatorSet.play(gethValueAnimatorTwo()).before(getvValueAnimatorTwo()); animatorSet.start(); } ... private ObjectAnimator getmObjectAnimator(){ if(mObjectAnimator == null){ mObjectAnimator = ObjectAnimator.ofFloat(mImageView, "rotate", 0, 360); } mObjectAnimator.setDuration(2000); mObjectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mImageView.setRotation((Float) animation.getAnimatedValue("rotate")); } }); return mObjectAnimator; }
![]()
(图1)
上面配置动画的属性都是在code中配置的,当然也可以使用xml,这个就不再多说了。如:
<setandroid:ordering="sequentially">
<set>
<objectAnimator
android:propertyName="x"
android:duration="500"
android:valueTo="400"
android:valueType="intType"/>
<objectAnimator
android:propertyName="y"
android:duration="500"
android:valueTo="300"
android:valueType="intType"/>
</set>
<objectAnimator
android:propertyName="alpha"
android:duration="500"
android:valueTo="1f"/>
</set>
AnimatorSetset=(AnimatorSet)AnimatorInflater.loadAnimator(myContext,
R.anim.property_animator);
set.setTarget(myObject);
set.start();
六、动画的监听-Animator.AnimatorListener():
回调方法主要有:onAnimationStart、onAnimationEnd、onAnimationCancel、onAnimationRepeat。通过实现这些方法,可以在动画不同的状态下做一些事情。依旧举个例子来测试下。就如上面图1中的效果。上面使用AnimatorSet来实现的,如果不用AnimatorSet是否可以实现?此时就需要这些接口来实现了。在监听到当前动画结束时,在对应的回调中去开始下一个动画。具体代码如下:
private void startAnimatorSet(){ gethValueAnimatorOne().start(); } private ValueAnimator gethValueAnimatorOne(){ if(hValueAnimatorOne == null) { hValueAnimatorOne = ValueAnimator.ofFloat(0, 500); } hValueAnimatorOne.setInterpolator(new LinearInterpolator()); hValueAnimatorOne.setTarget(mImageView); hValueAnimatorOne.setDuration(2000); hValueAnimatorOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mImageView.setTranslationX((Float) animation.getAnimatedValue()); } }); hValueAnimatorOne.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { getmObjectAnimator().start(); } @Override public void onAnimationEnd(Animator animation) { getvValueAnimatorOne().start(); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); return hValueAnimatorOne; } private ValueAnimator getvValueAnimatorOne(){ if(vValueAnimatorOne == null){ vValueAnimatorOne = ValueAnimator.ofFloat(0, 500); } vValueAnimatorOne.setInterpolator(new LinearInterpolator()); vValueAnimatorOne.setTarget(mImageView); vValueAnimatorOne.setDuration(2000); vValueAnimatorOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mImageView.setTranslationY((Float) animation.getAnimatedValue()); } }); vValueAnimatorOne.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { gethValueAnimatorTwo().start(); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); return vValueAnimatorOne; } private ValueAnimator gethValueAnimatorTwo(){ if(hValueAnimatorTwo == null){ hValueAnimatorTwo = ValueAnimator.ofFloat(500, 0); } hValueAnimatorTwo.setInterpolator(new LinearInterpolator()); hValueAnimatorTwo.setTarget(mImageView); hValueAnimatorTwo.setDuration(2000); hValueAnimatorTwo.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mImageView.setTranslationX((Float) animation.getAnimatedValue()); } }); hValueAnimatorTwo.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { getvValueAnimatorTwo().start(); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); return hValueAnimatorTwo; } private ValueAnimator getvValueAnimatorTwo(){ if(vValueAnimatorTwo == null){ vValueAnimatorTwo = ValueAnimator.ofFloat(500, 0); } vValueAnimatorTwo.setInterpolator(new LinearInterpolator()); vValueAnimatorTwo.setTarget(mImageView); vValueAnimatorTwo.setDuration(2000); vValueAnimatorTwo.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mImageView.setTranslationY((Float) animation.getAnimatedValue()); } }); return vValueAnimatorTwo; } private ObjectAnimator getmObjectAnimator(){ if(mObjectAnimator == null){ mObjectAnimator = ObjectAnimator.ofFloat(mImageView, "rotate", 0, 360); } mObjectAnimator.setDuration(2000); mObjectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mImageView.setRotation((Float) animation.getAnimatedValue("rotate")); } }); return mObjectAnimator; }
最后,这里使用的动画例子比较简单,如果需要更多的练习,可以看看ApiDemo中列子,再结合Genymotion模拟上的ApiDemo看效果。