Android--Property Animation介绍

来源:互联网 发布:软件项目工作量统计表 编辑:程序博客网 时间:2024/05/19 10:07

1、简介

属性动画是谷歌在Android3.0提出的动画框架,但我们使用以前的动画框架也能实现很丰富的动画效果,比如位移,旋转,缩放,透明度等等,那为什么谷歌还要提出属性动画。

补间动画(Tweened Animations)只能够实现移动、缩放、旋转和淡入淡出这四种动画操作,动画机制就是使用硬编码的方式来完成的,功能限定死就是这些,基本上没有任何扩展性可言。

并且补间动画只能作用于View,而且补间动画改变的只是View的绘制效果,View真正的属性并没有改变。比如,一个ImageView做平移的动画,虽然图片的确做了平移,但ImageView可点击的区域并没随着平移而改变,还是在原来的位置。

可以看到并没有改变 ImageView 的位置,我们还是要在原来的位置才能触发点击效果。所以补间动画并不适合做具有交互的动画效果,只用来做显示型的效果。

而属性动画则可以改变真正的属性,从而实现按钮平移时点击区域也跟着平移。通俗点说,属性动画其实就是在一定时间内,按照一定规律来改变对象的属性,从而使对象展现出动画效果。

属性动画和补间动画一样,可以通过xml文件定义,不同的是,补间动画的xml文件放于res/anim/目录下,而属性动画的xml文件则放于res/animator/目录下。同样的,在Java代码里引用属性动画的xml文件时,则用R.animator.filename,不同于补间动画用R.anim.filename引用。

2、相关API

Property Animation 就是通过动画方式来改变对象的属性来实现我们想要的效果。我们来了解这些相关的属性:

  • Duration:动画的持续时间,默认300ms。
  • Time interpolation:时间差值,就是Animation中的LinearInterpolator、AccelerateDecelerateInterpolator,是定义动画的变化率。
  • Repeat count and behavior:重复次数、以及重复模式;可以定义重复多少次;重复时从头开始,还是反向。
  • Animator sets:动画集合,你可以定义一组动画,一起执行或者顺序执行。
  • Frame refresh delay:帧刷新延迟,对于你的动画,多久刷新一次帧;默认为10ms,但最终依赖系统的当前状态;基本不用管。

和补间动画的属性设置是一样的,当然最重要的还是熟练掌握这些相关的类:

  • ObjectAnimator 动画的执行类
  • ValueAnimator 动画的执行类
  • AnimatorSet 用于控制一组动画的执行:线性,一起,每个动画的先后执行等。
  • AnimatorInflater 用户加载属性动画的xml文件
  • TypeEvaluator 类型估值,主要用于设置动画操作属性的值。
  • TimeInterpolator 时间插值,上面已经介绍。

总的来说,属性动画就是,动画的执行类来设置动画操作的对象的属性、持续时间,开始和结束的属性值,时间差值等,然后系统会根据设置的参数,动态的变化对象的属性。

3、ObjectAnimator

属性框架中最简单也是最常用的对象就是 ObjectAnimator,它可以直接对任意对象的任意属性进行动画操作。

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)

ofFloat 是我们经常使用的 ObjectAnimator 的静态方法,使用它我们可以轻松实现位移,旋转,缩放,透明度变化等多种动画效果。

target :就是我们所要去操纵的一个对象,以上图所展示的,这个对象如果用属性动画,就是 ImageView。

propertyName:顾名思义是属性名字,这个就是我们实现效果的关键,要写入的是我们想要实现的动画,以字符串的形式。常用属性名如下:

  • alpha:控制 View 对象的透明度变化
  • translationX 和 translationY:作为一种增量来控制View对象从它容器的左上角坐标偏移的位置
  • X 和 Y:它们与 translation不同的是,translation是偏移量,而X,Y则是到达的绝对值
  • rotation、rotationX 和 rotationY:控制View围绕支点进行2D和3D的旋转
  • scaleX 和 scaleY:控制View围绕它的中心点进行缩放
  • pivotX 和 pivotY:控制View对象的支点位置,围绕这个支点进行旋转和缩放的处理,默认情况下支点位置为对象的中心点
  • backgroundColor:可以对 View 对象的背景色做修改

因为属性动画操控的目标基本上是View对象,所以想要知道更多属性名,可以写个类继承 View,用 set,get看有多少可修改的属性,那些大都可以当propertyName使用。

values:既我们想要做的动画的属性值,这个符合要求下可以是任意个。

1、Alpha

imageView.setOnClickListener(new View.OnClickListener() {        @Override        public void onClick(View v) {            ObjectAnimator                   .ofFloat(imageView, "alpha", 0.1F, 1.0F)                   .setDuration(500)                   .start();        }});

用这样的语法一句代码就可以实现动画播放啦,是不是很方便。当然如果想要设置更多属性,也可以去实例对象。

ObjectAnimator animator = ObjectAnimator.ofFloat(imageView, "alpha", 0.1f, 1.0f);animator.setDuration(500);animator.setInterpolator(new DecelerateInterpolator());//设置动画插入器,减速animator.setRepeatCount(3);//设置动画重复次数,-1代表无限,这里则播放4次动画animator.setRepeatMode(ValueAnimator.REVERSE);//设置动画循环模式animator.start();

2、Translation

imageView.setOnClickListener(new View.OnClickListener() {     @Override     public void onClick(View v) {         ObjectAnimator               .ofFloat(imageView, "translationX", -300, 400)               .setDuration(500)               .start();     }});

因为使用的propertyName是translationX,所以只会做 X坐标上的移动,并且能够看到点击区域的变化,属性动画的确改变了 View对象的位置。

而如果用 X,Y,代码如下:

imageView.setOnClickListener(new View.OnClickListener() {    @Override    public void onClick(View v) {        ObjectAnimator.ofFloat(imageView,                        "X", 200).setDuration(1000).start();    }});

3、Rotation

propertyName 设置为rotation,则做普通的旋转。

imageView.setOnClickListener(new View.OnClickListener() {    @Override    public void onClick(View v) {        ObjectAnimator               .ofFloat(imageView, "rotation", 0, 360)               .setDuration(500)               .start();     }});

rotationX 则是将 View 对象绕 X轴旋转,实现3D的旋转,rotationY 则是绕 Y轴旋转。

imageView.setOnClickListener(new View.OnClickListener() {    @Override    public void onClick(View v) {        ObjectAnimator               .ofFloat(imageView, "rotationY", 0, 360)               .setDuration(500)               .start();     }});

4、Scale

scaleX,scaleY 也与translation 和 rotation一样,分别只在 X轴和 Y轴上起作用,scaleX,代表纵向的大小不变,在 X轴上进行缩放,scaleY就是相反的效果。

imageView.setOnClickListener(new View.OnClickListener() {   @Override   public void onClick(View v) {        ObjectAnimator               .ofFloat(imageView, "scaleY", 0, 1)               .setDuration(500)               .start();     }});

可以看到 View 对象只在Y轴上放大。

5、Pivot

这个属性与我们原来的是一样的,改变中心支点的位置,pivotX 就是只改变支点的 X轴上的坐标,pivotY 则是改变支点的 Y轴上的坐标,这里我先用一下 AnimatorSet 这个类,单单改变支点是没有动画效果的。

imageView.setOnClickListener(new View.OnClickListener() {    @Override    public void onClick(View v) {        ObjectAnimator                 .ofFloat(imageView, "rotation", 0, 360)                 .setDuration(2000).start();        ObjectAnimator.ofFloat(imageView, "pivotX", 0).start();   }});

可以看到现在的支点 Y轴并没有改变,只是X轴上的坐标变啦。上面我调用了两次ObjectAnimator的静态方法,从结果可以知道这个方法是异步的,所有动画可以同时进行。

PropertyValuesHolder

不过谷歌还给我们提供了一个更好的实现方法,也就是使用PropertyValuesHolder。谷歌在这个类中实现了许多优化,这些优化可以让我们在做多个属性的动画时更有效率,我们来看看代码:

PropertyValuesHolder p1 = PropertyValuesHolder.ofFloat("rotation", 0, 360F);PropertyValuesHolder p2 = PropertyValuesHolder.ofFloat("pivotX", 0);ObjectAnimator       .ofPropertyValuesHolder(imageView, p1, p2)       .setDuration(2000).start();

最后的结果当然是一样的,这里就不展示啦。

6、BackgroundColor

imageView.setOnClickListener(new View.OnClickListener() {   @Override   public void onClick(View v) {       ObjectAnimator.ofInt(imageView,                        "backgroundColor", Color.RED, Color.BLUE, Color.GRAY,                        Color.GREEN).start();    }});

这样就很轻松的改变了 View对象的背景色并实现的颜色变化的动画。当然我们的 View是ImageView,所以这时候不把那张图片设置为背景是不会有效果的。

4、AnimatorSet

AnimatorSet 的功能和 PropertyValuesHolder 类似,但 AnimatorSet 可以控制动画播放的顺序,PropertyValuesHolder 更多用于在特定时候为减少代码量而使用。

工欲善其事必先利其器,我们想要熟练地使用组合动画,当然先要了解谷歌给我们都提供了哪些方法。

AnimatorSet 中最重要的方法是play()方法,如果我们向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimator)将会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括以下四个方法:

  • after(Animator anim) 将现有动画插入到传入的动画之后执行
  • after(long delay) 将现有动画延迟指定毫秒后执行
  • before(Animator anim) 将现有动画插入到传入的动画之前执行
  • with(Animator anim) 将现有动画和传入的动画同时执行

除了play()方法,AnimatorSet 还有个play(Animator… items)方法,可实现任意个动画同时执行,playSequentially(Animator… items)方法按顺序播放动画。

imageView.setOnClickListener(new View.OnClickListener() {   @Override   public void onClick(View v) {        ObjectAnimator anim1 = ObjectAnimator.ofFloat(imageView, "scaleX", 0.1F, 1.0F);        ObjectAnimator anim2 = ObjectAnimator.ofFloat(imageView, "scaleY", 0.1F, 1.0F);        ObjectAnimator anim3 = ObjectAnimator.ofFloat(imageView, "translationX", 0, -300);        AnimatorSet animatorSet = new AnimatorSet();        animatorSet.play(anim1).with(anim2);        animatorSet.play(anim3).after(anim1);        animatorSet.setDuration(2000);        animatorSet.start();    }});

AnimatorSet 同样支持链式写法,但不能随便去连,before(),after()方法都只是对当前动画而言的,也就是play() 里的 Animator。

5、动画监听

对于动画,一般都是一些辅助效果,比如我们要删除个元素,我们可能希望是个淡出的效果,但是最终还是要删掉,并不是透明度没有了,但却还占着位置,所以我们需要知道动画如何结束。

Animator类当中提供了一个addListener()方法,这个方法接收一个AnimatorListener,我们只需要去实现这个AnimatorListener就可以监听动画的各种事件了。

ObjectAnimator是继承自ValueAnimator的,而ValueAnimator又是继承自Animator的,因此不管是ValueAnimator还是ObjectAnimator都可以使用addListener()这个方法。另外AnimatorSet也是继承自Animator的,因此addListener()这个方法算是个通用的方法。

anim.addListener(new AnimatorListener() {      @Override      public void onAnimationStart(Animator animation) {          // 会在动画开始的时候调用    }      @Override      public void onAnimationRepeat(Animator animation) {          // 会在动画重复执行的时候调用    }      @Override      public void onAnimationEnd(Animator animation) {          // 会在动画结束的时候调用    }      @Override      public void onAnimationCancel(Animator animation) {         // 会在动画被取消的时候调用     }  });

animator还有 cancel() 和 end() 方法,cancel动画立即停止,停在当前的位置;end动画直接到最终状态。

但是很多时候我们并不想要监听那么多个事件,可能我们只想要监听动画结束这一个事件,那么每次都要将四个接口全部实现一遍就显得非常繁琐。为此Android提供了一个适配器类,叫作AnimatorListenerAdapter,使用这个类就可以解决掉实现接口繁琐的问题了,如下所示:

anim.addListener(new AnimatorListenerAdapter() {  });  

我们向addListener()方法中传入这个适配器对象,由于AnimatorListenerAdapter中已经将每个接口都实现好了,所以这里不用实现任何一个方法也不会报错。那么如果我想监听动画结束这个事件,就只需要单独重写这一个方法就可以了,这个大家自己试试就行啦。

现在让我们做一个去除 ImageView 的动画:

imageView.setOnClickListener(new View.OnClickListener() {   @Override   public void onClick(View v) {       ObjectAnimator anim1 = ObjectAnimator.ofFloat(imageView, "scaleX", 1.0F, 0.1F);       ObjectAnimator anim2 = ObjectAnimator.ofFloat(imageView, "scaleY", 1.0F, 0.1F);       ObjectAnimator anim3 = ObjectAnimator.ofFloat(imageView, "alpha", 1.0f, 0.0f);       AnimatorSet animatorSet = new AnimatorSet();       animatorSet.playTogether(anim1, anim2, anim3);       animatorSet.setDuration(2000);       animatorSet.start();       animatorSet.addListener(new AnimatorListenerAdapter() {            @Override            public void onAnimationEnd(Animator animation) {                  ViewGroup parent = (ViewGroup) imageView.getParent();                  if (parent != null)                     parent.removeView(imageView);            }        });     }});

代码很简单,就不解释啦,直接看效果吧。

可以看到在做完动画后,怎么点击都没有反应,因为这个时候 ImageView 已经被移除啦。

除了addListener添加监听器外,还有一种 addUpdateListener 监听器。

anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {     @Override     public void onAnimationUpdate(ValueAnimator animation) {    }});

该方法用来监听动画绘制过程中的每一帧的改变,通过这个方法,我们可以在动画重绘的过程中,实现自己的逻辑。

6、ValueAnimator

ValueAnimation 是 ObjectAnimator 的父类,我们前面介绍的 ObjectAnimator 可以作用于某一个控件的某个属性,但 ValueAnimator 本身并不会作用于任何一个属性,也不会启动任何一个动画,它就相当于一个数值发生器,它可以产生我们想要的任何数值。

Android 系统给 ValueAnimator 提供了很多计算数值的方法,如 int,float,point等等,同时我们也可以根据自己的需要自定义计算数值的方法。

那我们产生这些数值有什么用呢?在属性动画中,实现每一步的动画效果,都是通过 ValueAnimator 计算出来的,比如我们要实现一个0 到 100的位移动画,随着动画的执行,它整个的值也会在0到100进行递增。我们有了这些值就可以作用于我们的属性,让它产生一个动画效果,那 ValueAnimator 是怎么去产生这些值的呢?

首先,ValueAnimator 会根据动画已进行的时间与它持续的总时间的比值产生一个0到1的时间因子,有了这样一个值经过相应的变换,就可以根据我们的 startValue 和 endValue(也就是初始值和结束值)来生成中间的相应值。同时,通过插值器的使用,我们还可以进一步的控制每个时间因子,它产生值的变化速度,如果我们使用 LinearInterpolator,那它生成值的时候就会是一个线性变化,只要时间相同,它的增量也相同。

由于 ValueAnimator 并不响应于任何一个动画,也不能控制任何一个属性,所以它并没有 ObjectAnimator 使用的广泛。

imageView.setOnClickListener(new View.OnClickListener() {    @Override    public void onClick(View v) {       ValueAnimator animator = ValueAnimator.ofFloat(-300, 300, 0, 200);       animator.setTarget(imageView);       animator.setDuration(1000).start();       animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                 imageView.setTranslationY((Float)animation.getAnimatedValue());            }       });    }});

这里就用到我们上面提到的 addUpdateAnimation,因为是监听动画绘制过程中的每一帧的改变,所以我们把 ValueAnimator 当前产生的值不断地传进去重绘,这样就实现了位移的效果,其它动画也是如此。

如果你把取得的值用 Log 日志打印出来,就会发现值在1000毫秒的时间内从-300平滑过渡到了300,再过渡到0,最后到200。

ValueAnimator 同样可以调用setStartDelay()方法来设置动画延迟播放的时间,还有setRepeatCount()和setRepeatMode()等方法,这些各种动画都是通用的。

7、Xml配置

Xml的配置和使用大都是标签上的应用,我就不在这里作介绍啦,有兴趣的朋友可以看我的博客Android–Animation标签介绍。

关于Property Animation的基本介绍就到这里啦,属性动画还有很多高级的使用,像TypeValue,Evaluator,这些我在其它的博客中会再介绍的。

结束语:本文仅用来学习记录,参考查阅。

4 0
原创粉丝点击