Android开发中,总是需要一些动画,在UI中达到某种美观的效果,进而提高用户的使用体验。因此,Android为我们提供了一些动画的框架。Android的动画框架分为两类。
既然有了传统动画框架,Google为什么还要创造一个属性动画框架呢?
我们举个例子来说明一下传统动画的局限性。
在布局中加入一个ImageView和一个Button,设置点击ImageView后弹出一个Toast,点击Button后使ImageView开始一个向右平移的动画。
传统动画的动画部分代码:
TranslateAnimation animation = new TranslateAnimation(0,200,0,0); //平移动画x轴移动200,y轴不动
animation.setDuration(1000); //动画时长
animation.setFillAfter(true); //使动画结束后停留在结束的位置
mIvPicture.startAnimation(animation);
运行Demo可以发现,ImageView确实进行了平移的效果。可是这时,当我们点击ImageView现在的位置时,却没有Toast弹出。而点击ImageView原来(现在是空白)的位置,却发现弹出了Toast。
这就是传统动画很大的一个局限性
因此,Google为我们提供了属性动画框架,来让我们实现更丰富,更美观的动画效果。
ObjectAnimator是属性动画中,最简单也最常用的一个对象。
平移
前文提到的使ImageVIew向右平移200像素的动画效果,我们只需要很简单的几句代码,即可实现:
ObjectAnimator.ofFloat(mIvPicture,"translationX",0F,200F)
.setDuration(1000)
.start();
我们来分析一下这一句代码。我们调用了ofFloat代码,并传入三个参数。
第一个参数是我们需要操纵的东西,在这里是我们的ImageView。
第二个参数是我们所需要操纵的对象的属性。
第三个参数是这个动画所变化的取值范围。
最后设置一下它的动画的属性,便可以start了。
这回我们再次点击ImageView目前的位置,成功弹出了Toast。证实了属性动画是通过改变物体的属性来达到动画效果的理论。
当我们需要改变y坐标时,只需要把"translationX"变为"translationY"即可。
其实 ,只要Google对一个对象的某个属性提供了get和set方法,我们就可以这个属性
对象属性中 X Y 与 translationX translationY 有什么区别呢?
旋转
旋转属性使用的是"rotation"属性,后面的变换范围是角度的变换。
比如想让ImageView旋转90度,只需要
ObjectAnimator.ofFloat(mIvPicture,"rotation",0F,90F)
.setDuration(1000)
.start();
其他
其实属性动画能操纵的属性,只要具有set get方法,都可以进行操纵。如scaleX scaleY等等…
插值器
Android为我们内置了插值器,使我们的动画更为自然。比如可以让我们的平移动画像物体的重力加速度由快到慢的Accelerate等等
Android中内置了七种插值器,分别是
要应用插值器,可以调用ObjectAnimator的setInterpolator方法, new出对应的插值器作为参数(xxxInterpolator)。如:
animator.setInterpolator(new AccelerateInterpolator());
这样就可以达到更自然的动画效果
当我们把几种动画按顺序写下时,运行程序,会发现效果是三种属性动画的叠加。由此可以发现,属性动画在调用start方法后,实际上是一个异步的过程。因此我们就可以看到三个属性动画同时作用的效果。通过这样的方法,其实就可以实现多种属性动画同时作用的效果。
ObjectAnimator.ofFloat(mIvPicture,"translationX",0F,200F).setDuration(1000).start();
ObjectAnimator.ofFloat(mIvPicture,"rotationX",0F,360F).setDuration(1000).start();
ObjectAnimator.ofFloat(mIvPicture,"translationY",0F,200F).setDuration(1000).start();
其实Google为我们提供了更好的方法,来实现这样的效果。
我们可以使用PropertyValuesHolder来实现。方法是传入和ObjectAnimator除作用对象之外同样的参数创建PropertyValuesHolder,之后通过ObjectAnimator的ofPropertyValuesHolder方法,传入作用对象以及要同时作用的PropertyValuesHolder即可。
PropertyValuesHolder p1 = PropertyValuesHolder.ofFloat("translationX",0F,200F);
PropertyValuesHolder p2 = PropertyValuesHolder.ofFloat("rotationX",0F,360F);
PropertyValuesHolder p3 = PropertyValuesHolder.ofFloat("translationY",0F,200F);
ObjectAnimator.ofPropertyValuesHolder(mIvPicture,p1,p2,p3).setDuration(1000).start();
运行后可以发现,与之前的效果一样。
既然两种方法效果一样,这样相比之前的好处是怎样呢?
我们可以通过AnimatorSet,来实现同样的效果。这里我们调用了set的playTogether方法,使得这些方法同时执行。
AnimatorSet set = new AnimatorSet();
ObjectAnimator animator1 = ObjectAnimator.ofFloat(mIvPicture, "translationX", 0F, 200F);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(mIvPicture, "rotationX", 0F, 360F);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(mIvPicture, "translationY", 0F, 200F);
set.playTogether(animator1,animator2,animator3);
set.setDuration(1000);
set.start();
除了playTogether方法外,我们还有playSequentially方法,它可以使得动画顺序执行。具体顺序则是调用时的参数的顺序。
AnimatorSet set = new AnimatorSet();
ObjectAnimator animator1 = ObjectAnimator.ofFloat(mIvPicture, "translationX", 0F, 200F);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(mIvPicture, "rotationX", 0F, 360F);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(mIvPicture, "translationY", 0F, 200F);
set.playSequentially(animator1,animator2,animator3);
set.setDuration(1000);
set.start();
我们除了可以用上述方法来让他们按一定顺序执行外,也可以通过AnimatorSet的play、with、after、before等方法的组合来控制动画播放的关系。
比如如下的代码就可以实现先平移,再旋转的效果。
set.play(animator1).with(animator3);
set.play(animator2).after(animator1);
我们通过这样的方法,可以为按钮设计一个按下后渐变出现的效果。
mBtnPress.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
ObjectAnimator animator = ObjectAnimator.ofFloat(mBtnPress,"alpha",1F,0F);
animator.setDuration(1000);
animator.start();
}
});
如果我们想要当动画播放完成后进行某些操作的话,又该如何去做呢?
里面传入的参数应该是一个AnimatorListener。
我们创建一个AnimatorListener,可以发现,需要实现四个方法,分别是
它们的回调时机根据字面意思便可以理解。大部分时候,我们需要实现的是onAnimationEnd方法。
animator.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
Toast.makeText(MainActivity.this,"Animation End",Toast.LENGTH_SHORT).show();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
如果每次都监听这么多事件,未免太麻烦了一点。因此Android为我们提供了另一种方法来添加动画的监听事件。也就是在添加AnimatorListener的时候,添加AnimatorListenerAdapter的对象即可。我们实现它,可以发现,可以只用实现自己需要的方法就可以了,比如在此处只用实现onAnimationEnd即可。
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
Toast.makeText(MainActivity.this,"Animation End",Toast.LENGTH_SHORT).show();
}
});
ValueAnimator本身不作用于任何一个属性,也不提供任何一种动画。简单来说,它就是一个数值发生器,可以产生想要的各种数值。Android系统为它提供了很多计算数值的方法,如int、float等等。你也可以自己实现计算数值的方法。其实,在属性动画中,如何产生每一步的动画效果,都是通过ValueAnimator计算出来的。
比如我们要实现一个从0-100的位移动画。随着动画时间的持续,它产生的值也会从0-100递增。我们有了这些值,就可以作用于这些属性,让它产生动画效果。
那么ValueAnimator,究竟是如何产生这些值的呢?
由于我们的ValueAnimator不作用于任何一个属性,也不提供任何一种动画。因此并没有ObjectAnimator使用得广泛。
我们可以查看一下ObjectAnimator的源码,会发现它继承自ValueAnimator,是它的一个子类。正是ValueAnimator产生的变化值,才使得ObjectAnimator可以将它应用于各个属性。因此,实际上,ObjectAnimator就是对ValueAnimator进行的一次封装。
我们可以通过ValueAnimator的ofXXX产生一个XXX类型的值(如ofInt),然后为valueAnimator添加一个更新的回调事件。在回调事件中,通过参数animation的getAnimationValue()方法,来获取对应的val;ue。有了这个值,我们就可以实现我们所有想要的动画效果。
比如此处就通过ValueAnimator实现了一个计时器的动画效果。
ValueAnimator animator = ValueAnimator.ofInt(0,100);
animator.setDuration(5000);
animator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = (Integer) animation.getAnimatedValue();
mButton.setText(""+value);
}
});
animator.start();
前面提到,ValueAnimator可以创建自定义的数值生成器,做法就是调用ValueAnimator的ofObject方法,创建一个TypeEvaluator作为参数。
通过重写TypeEvaluator的evaluate方法,我们可以返回各种各样的值。
ValueAnimator animator = ValueAnimator.ofObject(new TypeEvaluator() {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
//计算
return null; //返回值
}
});
我们来研究一下evaluate方法的几个参数
float fraction:前面提到的时间因子
Object startValue:起始值
Object endValue:结束值
通过这三个值,我们就可以经过各种计算,产生所有我们想要的值。
其实,通过TypeEvaluator,我们不光能产生普通的数据。通过泛型的用法,我们还能定义更加复杂的数据。
泛型的用法,可以在创建TypeEvaluator时指定为泛型。这样,我们就能通过更多各种类型的数据,来达到更丰富的效果。比如这里就用到了一个名为PointF的数据类型。
ValueAnimator animator = ValueAnimator.ofObject(new TypeEvaluator<PointF>() {
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
//计算
return null; //返回值
}
});
广告时间
我是N0tExpectErr0r,一名广东工业大学的大二学生
欢迎来到我的个人博客,所有文章均在个人博客中同步更新哦
http://blog.N0tExpectErr0r.cn