android属性动画

来源:互联网 发布:智慧树网络课程答案doc 编辑:程序博客网 时间:2024/06/05 22:32

因此Android系统在一开始的时候就给我们提供了两种实现动画效果的方式,逐帧动画(frame-by-frame animation)和补间动画(tweened animation)。逐帧动画的工作原理很简单,其实就是将一个完整的动画拆分成一张张单独的图片,然后再将它们连贯起来进行播放,类似于动画片的工作原理。补间动画则是可以对View进行一系列的动画操作,包括淡入淡出、缩放、平移、旋转四种。

然而自Android 3.0版本开始,系统给我们提供了一种全新的动画模式,属性动画(property animation),它的功能非常强大,弥补了之前补间动画的一些缺陷,几乎是可以完全替代掉补间动画了。

补间动画有一个缺陷,就是它只能够实现移动、缩放、旋转和淡入淡出这四种动画操作,那如果我们希望可以对View的背景色进行动态地改变呢?很遗憾,我们只能靠自己去实现了。说白了,之前的补间动画机制就是使用硬编码的方式来完成的,功能限定死就是这些,基本上没有任何扩展性可言。

补间动画还有一个致命的缺陷,就是它只是改变了View的显示效果而已,而不会真正去改变View的属性。什么意思呢?比如说,现在屏幕的左上角有一个按钮,然后我们通过补间动画将它移动到了屏幕的右下角,现在你可以去尝试点击一下这个按钮,点击事件是绝对不会触发的,因为实际上这个按钮还是停留在屏幕的左上角,只不过补间动画将这个按钮绘制到了屏幕的右下角而已,属性动画的实现机制是通过对目标对象进行赋值并修改其属性来实现的,那么之前所说的按钮显示的问题也就不复存在了,如果我们通过属性动画来移动一个按钮,那么这个按钮就是真正的移动了,而不再是仅仅在另外一个位置绘制了而已。


ValueAnimator

valueanimator动态改变一个值,利用这个值可以对view的任何属性设置动画:

protected void testValueAnimtor(View view){    ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);    anim.setDuration(3000);    anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {        @Override        public void onAnimationUpdate(ValueAnimator animation) {            float fValue = (float) animation.getAnimatedValue();            mtvTest.setAlpha(fValue);        }    });    anim.start();}

int值可以用ofInt来改变


ObjectAnimator

继承ValueAnimator,不用写监听,直接在参数里指定要执行动画的view和view的属性,但是它不是对任何属性都可以执行的,必须要相关属性有get/set方法才行,比如alpha,有get和setAlpha,当然,大部分属性都有get和set方法

alpha从1到0再到1:

  1. ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);  
  2. animator.setDuration(5000);  
  3. animator.start();  

旋转360度


  1. ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);  
  2. animator.setDuration(5000);  
  3. animator.start();
向左移出屏幕再移回

  1. float curTranslationX = textview.getTranslationX();  
  2. ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "translationX", curTranslationX, -500f, curTranslationX);  
  3. animator.setDuration(5000);  
  4. animator.start();  
垂直放大3倍再恢复

  1. ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "scaleY", 1f, 3f, 1f);  
  2. animator.setDuration(5000);  
  3. animator.start();
无限旋转:

RepeatCount传入-1表示无限旋转,如果不设置Interpolator,有加速效果,两次旋转之间速度要重新从0开始,所以有停顿,不连续,要设置线性匀速加速才行

protected void testRepeatRotation(View view){    ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotation", 0f, 360f);    animator.setDuration(5000);    animator.setInterpolator(new LinearInterpolator());    animator.setRepeatCount(-1);    animator.start();}

组合动画

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

protected void testObjectAnimtorSet(View view){    ObjectAnimator moveIn = ObjectAnimator.ofFloat(view, "translationX", -500f, 0f);    ObjectAnimator rotate = ObjectAnimator.ofFloat(view, "rotation", 0f, 360f);    ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f, 1f);    AnimatorSet animSet = new AnimatorSet();    animSet.play(rotate).with(fadeInOut).after(moveIn);    animSet.setDuration(5000);    animSet.start();}


Animator监听器

在很多时候,我们希望可以监听到动画的各种事件,比如动画何时开始,何时结束,然后在开始或者结束的时候去执行一些逻辑处理。这个功能是完全可以实现的,Animator类当中提供了一个addListener()方法,这个方法接收一个AnimatorListener,我们只需要去实现这个AnimatorListener就可以监听动画的各种事件了。

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

添加一个监听器的代码如下所示:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. anim.addListener(new AnimatorListener() {  
  2.     @Override  
  3.     public void onAnimationStart(Animator animation) {  
  4.     }  
  5.   
  6.     @Override  
  7.     public void onAnimationRepeat(Animator animation) {  
  8.     }  
  9.   
  10.     @Override  
  11.     public void onAnimationEnd(Animator animation) {  
  12.     }  
  13.   
  14.     @Override  
  15.     public void onAnimationCancel(Animator animation) {  
  16.     }  
  17. });  
可以看到,我们需要实现接口中的四个方法,onAnimationStart()方法会在动画开始的时候调用,onAnimationRepeat()方法会在动画重复执行的时候调用,onAnimationEnd()方法会在动画结束的时候调用,onAnimationCancel()方法会在动画被取消的时候调用。

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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. anim.addListener(new AnimatorListenerAdapter() {  
  2. });  
这里我们向addListener()方法中传入这个适配器对象,由于AnimatorListenerAdapter中已经将每个接口都实现好了,所以这里不用实现任何一个方法也不会报错。那么如果我想监听动画结束这个事件,就只需要单独重写这一个方法就可以了,如下所示:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. anim.addListener(new AnimatorListenerAdapter() {  
  2.     @Override  
  3.     public void onAnimationEnd(Animator animation) {  
  4.     }  
  5. });  

使用XML编写动画

我们可以使用代码来编写所有的动画功能,这也是最常用的一种做法。不过,过去的补间动画除了使用代码编写之外也是可以使用XML编写的,因此属性动画也提供了这一功能,即通过XML来完成和代码一样的属性动画功能。

通过XML来编写动画可能会比通过代码来编写动画要慢一些,但是在重用方面将会变得非常轻松,比如某个将通用的动画编写到XML里面,我们就可以在各个界面当中轻松去重用它。

如果想要使用XML来编写动画,首先要在res目录下面新建一个animator文件夹,所有属性动画的XML文件都应该存放在这个文件夹当中。然后在XML文件中我们一共可以使用如下三种标签:

  • <animator>  对应代码中的ValueAnimator
  • <objectAnimator>  对应代码中的ObjectAnimator
  • <set>  对应代码中的AnimatorSet

那么比如说我们想要实现一个从0到100平滑过渡的动画,在XML当中就可以这样写:

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <animator xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:valueFrom="0"  
  3.     android:valueTo="100"  
  4.     android:valueType="intType"/>  
而如果我们想将一个视图的alpha属性从1变成0,就可以这样写:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:valueFrom="1"  
  3.     android:valueTo="0"  
  4.     android:valueType="floatType"  
  5.     android:propertyName="alpha"/>  
其实XML编写动画在可读性方面还是挺高的,上面的内容相信不用我做解释大家也都看得懂吧。

另外,我们也可以使用XML来完成复杂的组合动画操作,比如将一个视图先从屏幕外移动进屏幕,然后开始旋转360度,旋转的同时进行淡入淡出操作,就可以这样写:

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <set xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:ordering="sequentially" >  
  3.   
  4.     <objectAnimator  
  5.         android:duration="2000"  
  6.         android:propertyName="translationX"  
  7.         android:valueFrom="-500"  
  8.         android:valueTo="0"  
  9.         android:valueType="floatType" >  
  10.     </objectAnimator>  
  11.   
  12.     <set android:ordering="together" >  
  13.         <objectAnimator  
  14.             android:duration="3000"  
  15.             android:propertyName="rotation"  
  16.             android:valueFrom="0"  
  17.             android:valueTo="360"  
  18.             android:valueType="floatType" >  
  19.         </objectAnimator>  
  20.   
  21.         <set android:ordering="sequentially" >  
  22.             <objectAnimator  
  23.                 android:duration="1500"  
  24.                 android:propertyName="alpha"  
  25.                 android:valueFrom="1"  
  26.                 android:valueTo="0"  
  27.                 android:valueType="floatType" >  
  28.             </objectAnimator>  
  29.             <objectAnimator  
  30.                 android:duration="1500"  
  31.                 android:propertyName="alpha"  
  32.                 android:valueFrom="0"  
  33.                 android:valueTo="1"  
  34.                 android:valueType="floatType" >  
  35.             </objectAnimator>  
  36.         </set>  
  37.     </set>  
  38.   
  39. </set>  
这段XML实现的效果和我们刚才通过代码来实现的组合动画的效果是一模一样的,每个参数的含义都非常清楚,相信大家都是一看就懂,我就不再一一解释了。

最后XML文件是编写好了,那么我们如何在代码中把文件加载进来并将动画启动呢?只需调用如下代码即可:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file);  
  2. animator.setTarget(view);  
  3. animator.start();  

调用AnimatorInflater的loadAnimator来将XML动画文件加载进来,然后再调用setTarget()方法将这个动画设置到某一个对象上面,最后再调用start()方法启动动画就可以了,就是这么简单。


ViewPropertyAnimator的用法

ViewPropertyAnimator其实算不上什么高级技巧,它的用法格外的简单,只不过和前面所学的所有属性动画的知识不同,它并不是在3.0系统当中引入的,而是在3.1系统当中附增的一个新的功能,在低版本的模拟器里看不到动画效果(比如4.2.2)

我们都知道,属性动画的机制已经不是再针对于View而进行设计的了,而是一种不断地对值进行操作的机制,它可以将值赋值到指定对象的指定属性上。但是,在绝大多数情况下,我相信大家主要都还是对View进行动画操作的。Android开发团队也是意识到了这一点,没有为View的动画操作提供一种更加便捷的用法确实是有点太不人性化了,于是在Android 3.1系统当中补充了ViewPropertyAnimator这个机制。

那我们先来回顾一下之前的用法吧,比如我们想要让一个TextView从常规状态变成透明状态,就可以这样写:

view sourceprint?
1.ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 0f);
2.animator.start();
看上去复杂吗?好像也不怎么复杂,但确实也不怎么容易理解。我们要将操作的view、属性、变化的值都一起传入到ObjectAnimator.ofFloat()方法当中,虽然看上去也没写几行代码,但这不太像是我们平时使用的面向对象的思维。

 

那么下面我们就来看一下如何使用ViewPropertyAnimator来实现同样的效果,ViewPropertyAnimator提供了更加易懂、更加面向对象的API,如下所示:

view sourceprint?
1.textview.animate().alpha(0f);

果然非常简单!不过textview.animate()这个方法是怎么回事呢?animate()方法就是在Android 3.1系统上新增的一个方法,这个方法的返回值是一个ViewPropertyAnimator对象,也就是说拿到这个对象之后我们就可以调用它的各种方法来实现动画效果了,这里我们调用了alpha()方法并转入0,表示将当前的textview变成透明状态。

怎么样?比起使用ObjectAnimator,ViewPropertyAnimator的用法明显更加简单易懂吧。除此之外,ViewPropertyAnimator还可以很轻松地将多个动画组合到一起,比如我们想要让textview运动到500,500这个坐标点上,就可以这样写:

view sourceprint?
1.textview.animate().x(500).y(500);
可以看出,ViewPropertyAnimator是支持连缀用法的,我们想让textview移动到横坐标500这个位置上时调用了x(500)这个方法,然后让textview移动到纵坐标500这个位置上时调用了y(500)这个方法,将所有想要组合的动画通过这种连缀的方式拼接起来,这样全部动画就都会一起被执行。

如果要顺序执行,可以这样:

protected void testViewPropertyAnimator(final View view){    view.animate().setDuration(10000).alpha(0.5f).setListener(new Animator.AnimatorListener() {        @Override        public void onAnimationStart(Animator animation) {        }        @Override        public void onAnimationEnd(Animator animation) {            view.animate().x(500).y(500).setDuration(10000);        }        @Override        public void onAnimationCancel(Animator animation) {        }        @Override        public void onAnimationRepeat(Animator animation) {        }    });}

0 0