Android 动画详解

Android动画初步

        动画(Animation)在我们日常的Android开发工作当中使用得较为频繁,尤其对于Android游戏这个动画的集合体,掌握动画开发的重要性毋庸置疑。同样的,在Android应用开发中我们也经常使用动画效果来提升APP用户体验,比如应用中的图片的旋转,页面和页面之间的淡入淡出、左右渐入渐出切换效果等等。那从今天开始我们将进入Android 动画的学习阶段,我将带领大家学习并实现一些比较炫的动画效果。

动画的分类

        Android FrameWork向开发人员提供了丰富的API用于实现各种各样的动画效果,而若要对动画分类,一般可将Android动画分为以下两类的动画系统:

View Animation

       View Animation动画系统又可以分类成Tween Animation 和Frame Animation:
     Tween Animation
         Tween Animation是Android系统比较老的一种动画系统,它的特点是通过对场景里的对象不断做图像变换(渐变、平移、缩放、旋转)产生动画效果,且这种动画只适用于View对象。
      Frame Animation
          Frame Animation也是常用到的动画,它的原理比较简单,就是将一系列准备好的图片按照顺序播放,形成动画效果。

Property Animation

       Property Animation(属性动画)是在Android3.0(API 11)之后引入的一种动画系统,该动画提供了比View Animation更加丰富、强大和灵活的功能,Android官方推荐开发人员使用该动画系统来实现动画。Property Animation的特点是动态地改变对象的属性从而达到动画效果。该动画实现使用于包括View在内的任何对象。

------------------------------------------------------------------------------------------------------------------
Tween Animation 和Frame Animation
------------------------------------------------------------------------------------------------------------------
初步了解了Android各种动画的分类及特点后,下面我们将一一对这些不同的动画系统进行深入地学习和分析。

Tween Animation

        上面我们了解到Tween Animation是Android中比较老的一种动画系统,且其只能实现对View对象动画设置,不过其虽然没有Property Aniamtion功能那么强大和灵活,但是使用Tween Animation依然能完成我们日常大部分的开发需求。灵活地掌握Tween Animation的使用方法,十分有必要。那Tween Animation实现View的动画,一般分为以下几种效果:
       Alpha:渐变透明动画效果;
       Scale:渐变尺寸伸缩动画效果;
       Translate:位置位移动画效果;
        Rotate:位置旋转动画效果。
接下来我们通过实例来一一学习如何实现以上几种动画效果,实现Tween Animation分为xml实现和代码实现,下面我们都会分别使用这两种方式来实现上面的的几种动画效果。首先我们使用xml方式来实现View的Alpha渐变透明动画效果,在项目目录结构中创建res/anim文件夹,然后在文件夹中创建view_animation_alpha.xml文件,文件中的代码如下:
[html]  view plain copy
  1. <!--   
  2.   * android:interpolator:修饰动画效果,定义动画的变化率,可以使存在的动画效果accelerated(加速),  
  3.   *           decelerated(减速),repeated(重复),bounced(弹跳)等。  
  4.   * android:fromAlpha="0.0":设置动画开始时控件的透明度,0.0为透明,控件不显示,1.0为不透明,控件全部显示  
  5.   * android:toAlpha="1.0":设置动画结束时的控件的透明度  
  6.   * android:duration="2500":设置每一次动画持续的时间值  
  7.   * android:repeatCount="5":设置动画重复的次数  
  8.   * android:repeatMode="reverse":设置动画重复的模式,reverse为0.0 -> 1.0 -> 0.0,动画反复执行;  
  9.   * restart为0.0 -> 1.0, 0.0 -> 1.0,动画每次都重新开始执行      
  10.  -->  
  11. <alpha xmlns:android="http://schemas.android.com/apk/res/android"  
  12.     android:interpolator="@android:anim/accelerate_decelerate_interpolator"  
  13.     android:fromAlpha="0.0"   
  14.     android:toAlpha="1.0"  
  15.     android:duration="2500"  
  16.     android:repeatCount="5"  
  17.     android:repeatMode="reverse"  
  18.    />  
xml文件中的属性在代码中都有详细的解释,这里就不重复说明了。定义完XML文件后,然后在代码中使用AnimationUtils类调用loadAnimation(context, id)方法来加载xml文件,具体代码如下:
[java]  view plain copy
  1. /** 
  2. * xml文件加载图片渐变(Alpha)动画 
  3. */  
  4. Animation mAnimation = AnimationUtils.loadAnimation(this, R.anim.view_animation_alpha);  
上面是使用xml实现Alpha的动画效果,下面我们来看看如何在代码中实现Alpha的动画,很简单,只需要实例化一个AlphaAnimation对象,然后传递相关参数就行,具体代码如下:
[java]  view plain copy
  1. /** 
  2. * 代码创建图片渐变(Alpha)动画 
  3. * 实例化AlphaAnimation对象: 
  4. * mAnimation = new AlphaAnimation(fromAlpha, toAlpha); 
  5. * fromAlpha:设置动画开始时控件的透明度,0.0为透明,控件不显示,1.0为不透明,控件全部显示 
  6. * toAlpha:设置动画结束时的控件的透明度 
  7. */  
  8. Animation mAnimation = new AlphaAnimation(0.0f, 1.0f);  
分别使用xml和代码实现Alpha动画后,最后我们为一个ImageView对象设置我们创建好的Alpha动画,ImageView对象只需要调用startAnimation(Animation animation)方法,将刚刚创建好的alpha动画对象作为参数添加传进去搞定,具体代码如下:
[java]  view plain copy
  1. ImageView mImageView = (ImageView)findViewById(R.id.view_animation_imageview);  
  2. //设置控件开始执行动画  
  3. mImageView.startAnimation(mAnimation);  
最后运行程序,操作效果效果图如下:

接着实现Scale渐变尺寸伸缩动画效果,同理,也在res/anim文件夹下创建view_animation_scale.xml,文件中的代码如下:
[html]  view plain copy
  1. <!--   
  2.    * android:fromXScale="0.0":设置动画开始时X轴方向的缩放起始值。0.0为X轴方向的控件缩成一点,1.0为X轴方向的控件不缩放  
  3.    * android:toXScale="1.0":设置动画结束时X轴方向的缩放结束值。  
  4.    * android:fromYScale="0.5":设置动画开始时Y轴方向的缩放起始值。0.0为Y轴方向的控件缩成一点,1.0为Y轴方向的控件不缩放  
  5.    * android:toYScale="1.0":设置动画结束时Y轴方向的缩放结束值  
  6.    * android:pivotX="100%":动画相对于物件的X坐标的开始位置,当value加了百分号%时(例如80%),则实现相对于控件本身  
  7.    *                    的位置而定的,当value是数值时(不加%号),则是指相对于父控件的位置而定的,单位是px。  
  8.    * android:pivotY="80%":动画相对于物件的Y坐标的开始位置,当value加了百分号%时(例如80%),则实现相对于控件本身  
  9.    *                    的位置而定的,当value是数值时(不加%号),则是指相对于父控件的位置而定的,单位是px。  
  10.    * android:duration="2500":设置每一次动画持续的时间值,单位为毫秒(ms)  
  11.    * android:repeatCount="5":设置动画重复的次数  
  12.    * android:repeatMode="reverse":设置动画重复的模式:  
  13.    *    reverse为0.0 -> 1.0 -> 0.0,动画反复执行;  
  14.    *    restart为0.0 -> 1.0, 0.0 -> 1.0,动画每次都重新开始执行  
  15.  -->  
  16. <scale xmlns:android="http://schemas.android.com/apk/res/android"  
  17.     android:fromXScale="0.0"  
  18.     android:toXScale="1.0"  
  19.     android:fromYScale="0.5"  
  20.     android:toYScale="1.0"  
  21.     android:pivotX="100%"  
  22.     android:pivotY="80%"  
  23.     android:duration="2500"  
  24.     android:repeatCount="5"  
  25.     android:repeatMode="reverse"  
  26.    />  
然后在代码中使用AnimationUtils类调用loadAniamtion(context,id)方法加载xml文件,具体代码如下:
[java]  view plain copy
  1. /** 
  2. * xml文件加载图片缩放(Scale)动画 
  3. */  
  4. mAnimation = AnimationUtils.loadAnimation(this, R.anim.view_animation_scale);  
上面是使用xml实现Scale的动画效果,下面我们来看看如何在代码中实现Scale的动画,很简单,只需要实例化一个ScaleAnimation对象,然后传递相关参数就行,具体代码如下:
[java]  view plain copy
  1. Animation mAnimation = null;  
  2. /** 
  3. * 代码创建图片渐变(Scale)动画 
  4. * mAnimation = new ScaleAnimation(fromX, toX, fromY, toY, pivotXType, pivotXValue, pivotYType, pivotYValue); 
  5. * fromX:设置动画开始时X轴方向的缩放起始值。0.0为X轴方向的控件缩成一点,1.0为X轴方向的控件不缩放; 
  6. * toX:设置动画结束时X轴方向的缩放结束值; 
  7. * fromY:设置动画开始时Y轴方向的缩放起始值。0.0为Y轴方向的控件缩成一点,1.0为Y轴方向的控件不缩放; 
  8. * toY:设置动画结束时Y轴方向的缩放结束值; 
  9. * pivotXtype:动画在X轴相对于物件位置类型 ,分为RELATIVE_TO_SELF、RELATIVE_TO_PARENT和ABSOLUTE三种类型: 
  10. *                       1、RELATIVE_TO_SELF:相对于控件自身; 
  11. *                       2、RELATIVE_TO_PARENT:相对于父控件; 
  12. *                       3、ABSOLUTE:绝对坐标; 
  13. * pivotXValue:动画相对于物件的X坐标的开始位置; 
  14. * pivotYType:动画在Y轴相对于物件位置类型 ,分为RELATIVE_TO_SELF、RELATIVE_TO_PARENT和ABSOLUTE三种类型; 
  15. * pivotYValue:动画相对于物件的Y坐标的开始位置; 
  16. */  
  17. //mAnimation = new ScaleAnimation(fromX, toX, fromY, toY)  
  18. //mAnimation = new ScaleAnimation(fromX, toX, fromY, toY, pivotX, pivotY)  
  19. //mAnimation = new ScaleAnimation(fromX, toX, fromY, toY, pivotXType, pivotXValue, pivotYType, pivotYValue)  
  20. mAnimation = new ScaleAnimation(0.0f, 1.0f, 0.5f, 1.0f, Animation.RELATIVE_TO_SELF, 1.0f, Animation.RELATIVE_TO_SELF, 1.0f);  
接下来就是为ImageView加载动画并开启动画,实现代码如上面为Imageview加载Alpha动画的代码一样,在这里就不重复列出,具体详细代码可以下载文章末尾的代码进行学习。最后我们运行程序,操作效果图如图下:

接着实现Translate动画效果,同理,在res/anim/文件夹下创建view_animation_translate.xml,文件中的代码如下:
[html]  view plain copy
  1. <!--   
  2.    * android:interpolator:修饰动画效果,定义动画的变化率,可以使存在的动画效果accelerated(加速),  
  3.                decelerated(减速),repeated(重复),bounced(弹跳)等。  
  4.    * android:fromXDelta="0%"动画开始执行时X轴方向控件的起始位置,当value加%时(例如80%),意思是相对于控件的原始位置往右的80%控件宽度的位置  
  5.    *                    又例如value为-20%时,意思是相对于控件的原始位置往左的20%控件宽度的位置。当Value不加%时(例如80),表示相对于  
  6.    *                    控件的原始位置往右80个像素(px)的位置,又如当Value为-20时,即往左20个像素。  
  7.    * android:toXDelta="100%"动画结束执行时X轴方向控件的结束位置,Value可以加%,也可以不加%,原理同上  
  8.    * android:fromYDelta="0%"动画开始执行时Y轴方向控件的起始位置,Value可以加%,也可以不加%,原理同上  
  9.    * android:toYDelta="100%"动画结束执行时Y轴方向控件的结束位置,Value可以加%,也可以不加%,原理同上  
  10.    * android:duration="2500"设置每一次动画持续的时间值  
  11.    * android:repeatMode="reverse"设置动画重复的模式,reverse为0% -> 100% -> 0%,动画反复执行;  
  12.    *    restart为0% -> 100%, 0% -> 100%,动画每次都重新开始执行  
  13.    * android:repeatCount="1"设置动画重复的次数  
  14.    * android:startOffset="1000":动画执行前的延迟时间    
  15.    * android:fillEnabled="false"当设置为true时,fillAfter和fillBefroe将会都为true,此时会忽略fillBefore和fillAfter两种属性  
  16.    * android:fillAfter="true":当Value为true,表示动画执行结束后停留在结束后的状态  
  17.    * android:fillBefore="false":当Value为false:表示动画执行结束后回到动画开始时的状态  
  18.  -->  
  19. <translate xmlns:android="http://schemas.android.com/apk/res/android"  
  20.     android:interpolator="@android:anim/bounce_interpolator"  
  21.     android:fromXDelta="0%"  
  22.     android:toXDelta="100%"  
  23.     android:fromYDelta="0%"  
  24.     android:toYDelta="100%"  
  25.     android:duration="2500"  
  26.     android:repeatMode="reverse"  
  27.     android:repeatCount="1"  
  28.     android:startOffset="1000"  
  29.     android:fillEnabled="false"  
  30.     android:fillAfter="true"  
  31.     android:fillBefore="true"  
  32.    />  
然后在代码中使用AnimationUtils类调用loadAniamtion(context,id)方法加载xml文件,具体代码如下:
[java]  view plain copy
  1. //xml文件加载图片位置位移(Translate)动画  
  2. mAnimation = AnimationUtils.loadAnimation(this, R.anim.view_animation_translate);  
上面是使用xml实现Translate的动画效果,下面我们来看看如何在代码中实现Translate的动画,很简单,只需要实例化一个TranslateAnimation对象,然后传递相关参数就行,具体代码如下:
[java]  view plain copy
  1. Animation mAnimation = null;  
  2. /** 
  3.  * 代码创建图片位置位移(Translate)动画 
  4. * mAnimation = new TranslateAnimation(fromXType, fromXValue, toXType, toXValue, fromYType, 
  5. *  fromYValue, toYType, toYValue); 
  6. * fromXType:动画开始执行时在X轴相对于物件位置类型 
  7. * fromXValue:动画开始执行时X轴方向的的起始位置,当位置类型为RELATIVE_TO_SELF时,Value取0.0f~1.0f之间, 
  8.  * 当位置类型为RELATIVE_TO_PARENT或ABSOLUTE时, Value使用(px)像素值 
  9. * toXType:动画结束执行时在X轴相对于物件位置类型 
  10. * toXValue:动画结束执行时X轴方向的的结束位置,Value取值方式同上 
  11. * fromYType:动画开始执行时在Y轴相对于物件位置类型 
  12. * fromYValue:动画开始执行时Y轴方向的的起始位置,Value取值方式同上 
  13. * toYType:动画在结束执行时在Y轴相对于物件位置类型 
  14. * toYValue:动画结束执行时Y轴方向的的结束位置,Value取值方式同上 
  15. */  
  16. //mAnimation = new TranslateAnimation(fromXDelta, toXDelta, fromYDelta, toYDelta);  
  17. mAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 1.0f,   
  18.         Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 1.0f);  
最后我们运行程序,操作效果图如图下:

接着实现Rotate动画效果,同理,在res/anim/文件夹下创建view_animation_rotate.xml,文件中的代码如下:
[html]  view plain copy
  1. <!--   
  2.   * android:fromDegrees="-50":动画开始执行时的控件起始状态的角度;  
  3.   * android:toDegrees="360":动画结束执行时的控件结束状态的角度;  
  4.   * android:pivotX="50%":旋转动画的中心点的X轴方向的横坐标,Value加%时,意思是相对于控件的原始位置往右方向的50%控件宽度的位置为横坐标,  
  5.   *                         若Value为负值(例如-50%),则表示往左方向;当Value为数值时,则代表相对于屏幕的(px)像素值;  
  6.   * android:pivotY="50%":旋转动画的中心店的Y轴方向的纵坐标,原理同上;  
  7.   * android:startOffset="1000":动画执行前的延迟时间;  
  8.   * android:fillAfter="true":当Value为true,表示动画执行结束后停留在结束后的状态;  
  9.   * android:duration="2500":设置每一次动画持续的时间值,单位为毫秒(ms);  
  10.   * android:repeatCount="5":设置动画重复的次数;  
  11.   * android:repeatMode="reverse":设置动画重复的模式:  
  12.   *     reverse为0.0 -> 1.0 -> 0.0,动画反复执行;  
  13.   *     restart为0.0 -> 1.0, 0.0 -> 1.0,动画每次都重新开始执行  
  14.  -->  
  15. <rotate xmlns:android="http://schemas.android.com/apk/res/android"  
  16.    android:interpolator="@android:anim/accelerate_decelerate_interpolator"  
  17.    android:fromDegrees="520"  
  18.    android:toDegrees="3600"  
  19.    android:pivotX="50%"  
  20.    android:pivotY="50%"  
  21.    android:startOffset="1000"  
  22.    android:fillAfter="true"  
  23.    android:duration="2500"  
  24.    android:repeatCount="4"  
  25.    android:repeatMode="reverse"  
  26.    />  
然后在代码中使用AnimationUtils类调用loadAniamtion(context,id)方法加载xml文件,具体代码如下:
[java]  view plain copy
  1. /** 
  2. * xml文件加载图片旋转(Rotate)动画 
  3. */  
  4. mAnimation = AnimationUtils.loadAnimation(this, R.anim.view_animation_rotate);  
上面是使用xml实现Rotate的动画效果,下面我们来看看如何在代码中实现 Rotate 的动画,很简单,只需要实例化一个RotateAnimation对象,然后传递相关参数就行,具体代码如下:
[java]  view plain copy
  1. Animation mAnimation = null;  
  2. /** 
  3. * 代码创建图片旋转(Rotate)动画 
  4. * mAnimation = new RotateAnimation(fromDegrees, toDegrees, pivotXType, pivotXValue, pivotYType, pivotYValue); 
  5. * fromDegrees:动画开始执行时的控件起始状态的角度; 
  6. * toDegrees:动画结束执行时的控件结束状态的角度; 
  7. * pivotXType:动画在X轴相对于物件位置类型 ,分为RELATIVE_TO_SELF、RELATIVE_TO_PARENT和ABSOLUTE三种类型: 
  8. *                       1、RELATIVE_TO_SELF:相对于控件自身; 
  9. *                       2、RELATIVE_TO_PARENT:相对于父控件; 
  10. *                       3、ABSOLUTE:绝对坐标; 
  11. * pivotXValue:动画开始执行时X轴方向的的起始位置,当位置类型为RELATIVE_TO_SELF时,Value取0.0f~1.0f之间,当位置类型为RELATIVE_TO_PARENT或ABSOLUTE时, 
  12. *              Value使用(px)像素值; 
  13. * pivotYType:动画在Y轴相对于物件位置类型 ,分为RELATIVE_TO_SELF、RELATIVE_TO_PARENT和ABSOLUTE三种类型; 
  14. * pivotYValue:旋转动画的中心店的Y轴方向的纵坐标,原理同上; 
  15. */  
  16. //mAnimation = new RotateAnimation(fromDegrees, toDegrees)  
  17. //mAnimation = new RotateAnimation(fromDegrees, toDegrees, pivotX, pivotY)  
  18. mAnimation = new RotateAnimation(-50f, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);  
最后我们运行程序,操作效果图如图下:


至此我们就实现了Tween Animation中的Alpha、Scale、Translate和Rotate四种动画效果,那我们就可以根据我们APP的具体需求,定义我们自己的动画,不过除了单独使用这些动画效果外,Android还允许我们使用这几种动画里面的若干种组合成一个动画集合,从而实现更加炫更加多样的动画效果。下面我们也分别使用xml和代码来利用动画集合的方式实现一个图片飞进飞出的动画效果,首先也是在res/anim文件夹下创建一个view_animation_set.xml文件,文件中的代码如下:
[html]  view plain copy
  1. <set   
  2.     xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:shareInterpolator="true"  
  4.     android:duration="1000"  
  5.     android:repeatMode="reverse"  
  6.     android:repeatCount="2"  
  7.     >  
  8.     <alpha  
  9.         android:fromAlpha="1.0"  
  10.         android:toAlpha="0.0"  
  11.         android:fillAfter="true"  
  12.         android:duration="1000"  
  13.         android:repeatCount="1"  
  14.         android:repeatMode="reverse"  
  15.         />  
  16.     <scale  
  17.         android:fromXScale="1.0"  
  18.         android:toXScale="0.0"  
  19.         android:fromYScale="1.0"  
  20.         android:toYScale="0.0"  
  21.         android:pivotX="50%"  
  22.         android:pivotY="50%"  
  23.         android:fillAfter="true"  
  24.         android:duration="1000"  
  25.         android:repeatCount="1"  
  26.         android:repeatMode="reverse"  
  27.         />  
  28.     <translate  
  29.         android:fromXDelta="0%"  
  30.         android:toXDelta="100%"  
  31.         android:fromYDelta="0%"  
  32.         android:toYDelta="100%"  
  33.         android:fillAfter="true"  
  34.         android:duration="1000"  
  35.         android:repeatCount="1"  
  36.         android:repeatMode="reverse"  
  37.         />  
  38.     <rotate   
  39.         android:fromDegrees="0"  
  40.         android:toDegrees="360"  
  41.         android:pivotX="50%"  
  42.         android:pivotY="50%"  
  43.         android:fillAfter="true"  
  44.         android:duration="1000"  
  45.         android:repeatCount="1"  
  46.         android:repeatMode="reverse"  
  47.         />  
  48. </set>  
查看上面代码,我们可以使用一个<set/>节点将动画组合成一个集合,需要注意的是<set/>里面也可以嵌套<set/>。接着在代码中加载xml动画,具体代码如下:
[java]  view plain copy
  1. /** 
  2. * xml文件加载图片集合(Set)动画 
  3. */  
  4. mAnimation = AnimationUtils.loadAnimation(this, R.anim.view_animation_set);  
上面是使用xml实现Set集合动画效果,下面我们来看看如何在代码中实现Set集合 动画,具体代码如下:
[java]  view plain copy
  1. /** 
  2.  * 代码创建图片集合(Set)动画 
  3. */  
  4. AnimationSet mAnimationSet = new AnimationSet(true);  
  5. //定义渐变动画对象  
  6. AlphaAnimation mAlphaAnimation = new AlphaAnimation(1.0f, 0.0f);  
  7. mAlphaAnimation.setRepeatCount(1);  
  8. mAlphaAnimation.setRepeatMode(Animation.REVERSE);  
  9. mAlphaAnimation.setFillAfter(true);  
  10. mAlphaAnimation.setDuration(1000);  
  11. //定义缩放动画对象  
  12. ScaleAnimation mScaleAnimation = new ScaleAnimation(1.0f, 0.0f, 1.0f, 0.0f,  
  13.         Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);  
  14. mScaleAnimation.setRepeatCount(1);  
  15. mScaleAnimation.setRepeatMode(Animation.REVERSE);  
  16. mScaleAnimation.setFillAfter(true);  
  17. mScaleAnimation.setDuration(1000);  
  18. //定义位移动画对象  
  19. TranslateAnimation mTranslateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f,   
  20.         Animation.RELATIVE_TO_SELF, 1.0f, Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 1.0f);  
  21. mTranslateAnimation.setRepeatCount(1);  
  22. mTranslateAnimation.setRepeatMode(Animation.REVERSE);  
  23. mTranslateAnimation.setFillAfter(true);  
  24. mTranslateAnimation.setDuration(1000);  
  25. //定义旋转动画对象  
  26. RotateAnimation mRotateAnimation = new RotateAnimation(0360, Animation.RELATIVE_TO_SELF, 0.5f,  
  27.         Animation.RELATIVE_TO_SELF, 0.5f);  
  28. mRotateAnimation.setRepeatCount(1);  
  29. mRotateAnimation.setRepeatMode(Animation.REVERSE);  
  30. mRotateAnimation.setFillAfter(true);  
  31. mRotateAnimation.setDuration(1000);  
  32. //添加动画到集合动画对象中  
  33. mAnimationSet.addAnimation(mAlphaAnimation);  
  34. mAnimationSet.addAnimation(mScaleAnimation);  
  35. mAnimationSet.addAnimation(mTranslateAnimation);  
  36. mAnimationSet.addAnimation(mRotateAnimation);  
最后我们运行程序,操作效果图如图下:

Frame Animation

       早期的老式电影放映机估计很多人都有所了解,它的播放原理就是在电影胶片上有一幅幅固定的画面,然后在放映的时候通过电机匀速推进切换画面,画面在人们视觉差作用下产生连续的动画效果。这里我们所要学习的Frame Animation的实现原理和老式电影放映机的播放原理是一样的,我们通过将一系列准备好的图片按照顺序播放,形成动画效果,就如同放电影一般。接下来我们就来学习如何在Android中使用Frame Animation,首先我们得准备一系列连续的图片,如图下:
           
           
上面是一系列小机器人走路的连续图片,我们将要做的就是将它们一张张匀速连续播放,从而感觉小机器人在走的动画效果。准备好图片后,我们就将它们一一组织起来,具体怎么设置呢?先在项目中的res/drawable/文件夹下创建drawable_animation_robot.xml文件,文件中具体代码如下:
[html]  view plain copy
  1. <animation-list xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:oneshot="false"  
  3.     >  
  4.     <item android:drawable="@drawable/drawable_animation01" android:duration="80"/>  
  5.     <item android:drawable="@drawable/drawable_animation02" android:duration="80"/>  
  6.     <item android:drawable="@drawable/drawable_animation03" android:duration="80"/>  
  7.     <item android:drawable="@drawable/drawable_animation04" android:duration="80"/>  
  8.     <item android:drawable="@drawable/drawable_animation05" android:duration="80"/>  
  9.     <item android:drawable="@drawable/drawable_animation06" android:duration="80"/>  
  10.     <item android:drawable="@drawable/drawable_animation07" android:duration="80"/>  
  11.     <item android:drawable="@drawable/drawable_animation08" android:duration="80"/>  
  12.     <item android:drawable="@drawable/drawable_animation09" android:duration="80"/>  
  13.     <item android:drawable="@drawable/drawable_animation10" android:duration="80"/>  
  14.     <item android:drawable="@drawable/drawable_animation11" android:duration="80"/>  
  15.     <item android:drawable="@drawable/drawable_animation12" android:duration="80"/>  
  16.     <item android:drawable="@drawable/drawable_animation13" android:duration="80"/>  
  17.     <item android:drawable="@drawable/drawable_animation14" android:duration="80"/>  
  18.     <item android:drawable="@drawable/drawable_animation15" android:duration="80"/>  
  19.     <item android:drawable="@drawable/drawable_animation16" android:duration="80"/>  
  20. </animation-list>  
上面代码中,我们将刚准备好的图片使用<animation-list/>将它们一一组合排列,并使用android:duration属性设置每张图片的显示时间,这里设置为80毫秒(ms),其中android:oneshot属性的作用是设置是否只播放一次,设置false则循环播放动画,true则只播放一次动画。
设置完图片后,我们来播放动画,那需要播放连续动画,则我们需借助AnimationDrawable类来完成,AnimationDrawable是实现逐帧动画的帮助类。下面就是我们执行逐帧动画的具体代码实现,如下:
[java]  view plain copy
  1. public class LoadingRobotProgressView extends ImageView {  
  2.   
  3.     private AnimationDrawable mAnimationDrawable;  
  4.       
  5.     public LoadingRobotProgressView(Context context) {  
  6.         super(context);  
  7.         // TODO Auto-generated constructor stub  
  8.         init();  
  9.     }  
  10.     public LoadingRobotProgressView(Context context, AttributeSet attrs) {  
  11.         super(context, attrs);  
  12.         // TODO Auto-generated constructor stub  
  13.         init();  
  14.     }  
  15.     public LoadingRobotProgressView(Context context, AttributeSet attrs, int defStyle) {  
  16.         super(context, attrs, defStyle);  
  17.         // TODO Auto-generated constructor stub  
  18.         init();  
  19.     }  
  20.   
  21.      private void init() {  
  22.            //设置背景  
  23.             this.setBackgroundResource(R.drawable.drawable_animation_robot);  
  24.             //获取当前的背景  
  25.             mAnimationDrawable = (AnimationDrawable) this.getBackground();  
  26.         }  
  27.   
  28.         /** 
  29.          * 当进入当前窗口时,开启动画 
  30.          */  
  31.         @Override  
  32.         protected void onAttachedToWindow() {  
  33.             super.onAttachedToWindow();  
  34.             mAnimationDrawable.start();  
  35.         }  
  36.         /** 
  37.          * 当离开当前窗口时,关闭动画 
  38.          */  
  39.         @Override  
  40.         protected void onDetachedFromWindow() {  
  41.             super.onDetachedFromWindow();  
  42.             mAnimationDrawable.stop();  
  43.         }  
  44. }  
好了,所有的编码工作都做完了,下面我们来运行一下程序,看一下具体的效果,如图下:

观察上面的效果图可知,实验成功。

---------------------------------------------------------------------------------------------------------------

Property Animation初步介绍

---------------------------------------------------------------------------------------------------------------

Property Animation 初步

       Property Animation 是Android3.0(level 11)引入的一个款功能强大、灵活、健壮的动画系统,它可以为任何包括View在内的对象添加动画效果。就实现原理来分析,Property Aniamtion和Tween Animation之间最大的区别就是前者在为对象添加动画效果时,其更改的是对象的实际属性,而后者改变的只是View的绘制效果,View的实际属性值是不发生改变的。比如对一个Button实现左右移动的动画,若使用Property Animation实现的话,Button在移动的过程中,由于Property Animation改变的是Button的实际属性值,因此Button的事件响应焦点也随着移动,若点击移动中的Button,其可响应事件点击;而使用View Animation实现的话,点击移动中的Button,则无法响应事件点击,原因是Button控件本身的实际位置依然在初始位置,其事件响应焦点也依然在初始位置。

       Property Animation功能强大,使用起来也非常的灵活,一般的,要实现一个Property Animation的动画,动画系统允许你设置一下动画的元素信息:

      (1) 持续时间:指定动画的持续显示时间。默认的时长是300毫秒。
      (2) 图像插值方式:指定属性值的变化方式,表示为关于动画已显示时间的函数。
      (3) 重复次数和方式:指定动画是否循环播放,以及重复的次数。还可以指定动画是否反向播放。可以设为先正向播放再反向回放,如此往复直至达到设定的重复次数。
      (4) 动画集合:你可以把动画分为多个逻辑组,以便实现同时播放、顺序播放或间隔一段时间再播放。
      (5) 帧刷新间隔:指定动画帧的刷新频率。默认是每 10 ms刷新一次,但应用程序实际可执行的刷新频率取决于系统整体的繁忙程度,以及系统对定时器的支持程度。

Property Animation的工作方式

    在学习Property Animation的工作之前,我们先通过一张图来看一下使用Property Animation的来实现动画的主要类及其相互之间的工作方式,如图下:



ValueAnimator:该类作为Property Animation系统的主要核心类,它记录了动画自身的一些属性值,比如动画显示的时间,动画对应当前属性的起始值和结束值。同时,ValueAnimator封装了一个计算插值因子的TimeInterpolator以及定义属性计算方式的TypeEvaluator。我们要实现一个Property Animation,都需要使用ValueAnimator或ValueAnimator的子类(ObjectAnimator)来完成动画的配置信息和计算过程。那ValueAnimator及其子类到底是如何完成动画的配置信息和计算过程的呢?下面我将会一一详细地分析,从而使读者了解Property Animation的工作方式:

       要启动动画,首先创建一个 ValueAnimator或其子类并指定要动画显示的属性的初始值、结束值以及显示时间,然后调用 start() 启动动画。在整个动画过程中, ValueAnimator 根据动画总时间和已进行的时间自动计算出一个时间比例因子(elapsed fraction),大小介于0和1之间。 时间比例因子代表动画已完成时间的百分比,0 表示 0%,1 表示 100%。ValueAnimator 算完时间比例因子后,将调用已设置好的 TimeInterpolator 计算出一个插值因子(interpolated fraction)。插值因子是一个由时间比例因子换算出来的图像显示状态因子。算完插值因子, ValueAnimator 就会调用合适的 TypeEvaluator ,根据插值因子、初始值、结束值计算出需要动画显示的属性值。计算出属性值后,最后为需要执行动画的对象设置对应属性的属性值。动画在持续的整个过程中,会根据我们当初设置的TimeInterpolator 和TypeEvaluator的计算方式计算出的不同的属性值,从而不断地改变对象属性值的大小,进而产生各式各样的动画效果。

       上面讲解Property Animation的动画执行过程可能会比较抽象,下面我们将通过一张图和一个实例来更形象地阐述一下Property Animation的属性计算方式和工作流程。Property Animation的工作流程一般分为四个步骤,如下图:


下面我们接着使用一个例子来了解一下一个对象执行Property Aniamtion的属性计算方式过程。下图(图一)是一个对象的x属性值的变化情况,即在屏幕上的水平位置。该对象从0到40像素点的移动变化过程总共持续了40ms的时间,并以每10ms的帧刷新频率来刷新一次对象,观察下图,每隔10ms,对象所移动的距离都是10个像素点,很明显我们能得出结论该对象的动画是匀速移动的。


                                               图 1

在Android中,我们称以上为以线性插值的方式执行动画,那默认设置的计算插值因子的TimeInterpolator为LinearInterpolator,计算属性的TypeEvaluator为IntEvaluator。这两个类均由系统提供,我们分别看一下两个类的源码,首先是LinearInterpolator的源码如下:

[java]  view plain copy
  1. package android.view.animation;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5.   
  6. /** 
  7.  * An interpolator where the rate of change is constant 
  8.  * 
  9.  */  
  10. public class LinearInterpolator implements Interpolator {  
  11.   
  12.     public LinearInterpolator() {  
  13.     }  
  14.       
  15.     public LinearInterpolator(Context context, AttributeSet attrs) {  
  16.     }  
  17.       
  18.     public float getInterpolation(float input) {  
  19.         return input;  
  20.     }  
  21. }  

LinearInterpolator类中的实现比较简单,除了构造方法外,就只有一个getInterpolation(float input)方法,该方法用于计算插值因子,参数input为时间比例因子,我们发现以匀速线性插值方式执行的动画的时间比例因子和插值因子是等值的。那有读者会问,时间比例因子又是怎么得来的呢,很简单,记住这个公式就行:时间比例因子 = 动画执行的总时间/当前已进行的时间。

IntEvaluator的源码如下:

[java]  view plain copy
  1. package android.animation;  
  2.   
  3. /** 
  4.  * This evaluator can be used to perform type interpolation between <code>int</code> values. 
  5.  */  
  6. public class IntEvaluator implements TypeEvaluator<Integer> {  
  7.   
  8.     public Integer evaluate(float fraction, Integer startValue, Integer endValue) {  
  9.         int startInt = startValue;  
  10.         return (int)(startInt + fraction * (endValue - startInt));  
  11.     }  
  12. }  
IntEvaluator类的实现也比较简单,只有一个evaluate(float fraction, Integer startValue, Integer endValue)方法用于计算当前的属性值,其中参数fraction为插值因子,startValue和endValue为属性的起始值和属性的结束值。

分析LinearInterpolator和IntEvaluator后,我们来做一个实验,就是借助LinearInterpolator和IntEvaluator类自己来计算一下上面实例的属性值的变化,具体代码如下:

[java]  view plain copy
  1. public class LinearTest {  
  2.   
  3.     public static void main(String[] args) {  
  4.         // TODO Auto-generated method stub  
  5.         //当前的时间  
  6.         float time = 10;  
  7.         //时间比例因子  
  8.         float timeFactor = 0.0f;  
  9.         for(int i = 0;i < 4;i++){  
  10.             System.out.println("时间 = " + time +"ms");  
  11.             timeFactor = time/40;  
  12.             System.out.println("时间因子 = "+ timeFactor);  
  13.             //插值因子  
  14.             float interpolatorFactor = LinearTest.getInterpolation(timeFactor);  
  15.             System.out.println("插值因子 = " + interpolatorFactor);  
  16.             //属性值  
  17.             int propertyValue = evaluate(interpolatorFactor, 040);  
  18.             System.out.println("属性值 = " + propertyValue);  
  19.             time += 10;  
  20.             System.out.println();  
  21.         }  
  22.           
  23.     }  
  24.       
  25.     /** 
  26.      * 计算插值因子 
  27.      * @param input 时间比例因子 
  28.      * @return float 返回插值因子 
  29.      */  
  30.      public static float getInterpolation(float input) {  
  31.             return input;  
  32.      }  
  33.       
  34.      /** 
  35.       * 计算当前的属性值 
  36.       * @param fraction 插值因子 
  37.       * @param startValue 属性起始值 
  38.       * @param endValue 属性结束值 
  39.       * @return Integer 当前的属性值 
  40.       */  
  41.      public static Integer evaluate(float fraction, Integer startValue, Integer endValue) {  
  42.             int startInt = startValue;  
  43.             return (int)(startInt + fraction * (endValue - startInt));  
  44.      }  
  45.       
  46. }  
运行程序,打印的结果如下:

[plain]  view plain copy
  1. 时间 = 10.0ms  
  2. 时间因子 = 0.25  
  3. 插值因子 = 0.25  
  4. 属性值 = 10  
  5.   
  6. 时间 = 20.0ms  
  7. 时间因子 = 0.5  
  8. 插值因子 = 0.5  
  9. 属性值 = 20  
  10.   
  11. 时间 = 30.0ms  
  12. 时间因子 = 0.75  
  13. 插值因子 = 0.75  
  14. 属性值 = 30  
  15.   
  16. 时间 = 40.0ms  
  17. 时间因子 = 1.0  
  18. 插值因子 = 1.0  
  19. 属性值 = 40  
查看实验结果,我们发现执行的结果与我们预想一致。

       我们再来看看下图(图2),如同上一张图一样,对象的x属性值都是从0到40像素的变化过程,总共持续时间也都是40ms,帧刷新频率都是10ms每次。唯一不同的就是在相同间隔的时间里对象所移动的距离各不相同,如下图中对象在前10ms所移动的距离为6个像素点,而在第10至20ms这个时间段对象所移动的距离为14个像素点,还有在第20至30ms和第30至40ms之间该对象所移动的距离又分别是14和6个像素点。依此我们可以发现,该对象是以先加速后减速的方式进行非匀速运动的。


                                                                            图2

 那在Android中,我们称以上为以非线性插值的方式执行动画。那默认设置的计算插值因子的TimeInterpolator为AccelerateDecelerateInterpolator,计算属性的TypeEvaluator为FloatEvaluator。这两个类均由系统提供,我们分别看一下两个类的源码,首先是AccelerateDecelerateInterpolator的源码如下:

[java]  view plain copy
  1. package android.view.animation;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5.   
  6. /** 
  7.  * An interpolator where the rate of change starts and ends slowly but 
  8.  * accelerates through the middle. 
  9.  *  
  10.  */  
  11. public class AccelerateDecelerateInterpolator implements Interpolator {  
  12.     public AccelerateDecelerateInterpolator() {  
  13.     }  
  14.       
  15.     @SuppressWarnings({"UnusedDeclaration"})  
  16.     public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {  
  17.     }  
  18.       
  19.     public float getInterpolation(float input) {  
  20.         return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;  
  21.     }  
  22. }  
FloatEvaluator的源代码,如下:

[java]  view plain copy
  1. package android.animation;  
  2.   
  3. /** 
  4.  * This evaluator can be used to perform type interpolation between <code>float</code> values. 
  5.  */  
  6. public class FloatEvaluator implements TypeEvaluator<Number> {  
  7.   
  8.     public Float evaluate(float fraction, Number startValue, Number endValue) {  
  9.         float startFloat = startValue.floatValue();  
  10.         return startFloat + fraction * (endValue.floatValue() - startFloat);  
  11.     }  
  12. }  
AccelerateDecelerateInterpolator类和FloatEvaluator中的方法在这里就不多介绍了,它们和前面所讲的LinearInterpolator和IntEvaluator类似。那我们同样做这样一个测试实验,具体的源代码如下:

[java]  view plain copy
  1. public class AccelerateDecelerateTest {  
  2.   
  3.     public static void main(String[] args) {  
  4.         // TODO Auto-generated method stub  
  5.         //当前的时间  
  6.         float time = 10;  
  7.         //时间比例因子  
  8.         float timeFactor = 0.0f;  
  9.         for(int i = 0;i < 4;i++){  
  10.             System.out.println("时间 = " + time +"ms");  
  11.             timeFactor = time/40;  
  12.             System.out.println("时间因子 = "+ timeFactor);  
  13.             //插值因子  
  14.             float interpolatorFactor = AccelerateDecelerateTest.getInterpolation(timeFactor);  
  15.             System.out.println("插值因子 = " + interpolatorFactor);  
  16.             //属性值  
  17.             float propertyValue = evaluate(interpolatorFactor, 040);  
  18.             System.out.println("属性值 = " + propertyValue);  
  19.             time += 10;  
  20.             System.out.println();  
  21.         }  
  22.           
  23.     }  
  24.       
  25.     /** 
  26.      * 计算插值因子 
  27.      * @param input 时间比例因子 
  28.      * @return float 返回插值因子 
  29.      */  
  30.     public static float getInterpolation(float input) {  
  31.         return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;  
  32.     }  
  33.       
  34.     /** 
  35.       * 计算当前的属性值 
  36.       * @param fraction 插值因子 
  37.       * @param startValue 属性起始值 
  38.       * @param endValue 属性结束值 
  39.       * @return Integer 当前的属性值 
  40.       */  
  41.     public static Float evaluate(float fraction, Number startValue, Number endValue) {  
  42.         float startFloat = startValue.floatValue();  
  43.         return startFloat + fraction * (endValue.floatValue() - startFloat);  
  44.     }  
  45.       
  46. }  
运行程序, 打印的结果如下:
[plain]  view plain copy
  1. 时间 = 10.0ms  
  2. 时间因子 = 0.25  
  3. 插值因子 = 0.14644662  
  4. 属性值 = 5.8578644  
  5.   
  6. 时间 = 20.0ms  
  7. 时间因子 = 0.5  
  8. 插值因子 = 0.5  
  9. 属性值 = 20.0  
  10.   
  11. 时间 = 30.0ms  
  12. 时间因子 = 0.75  
  13. 插值因子 = 0.8535534  
  14. 属性值 = 34.142136  
  15.   
  16. 时间 = 40.0ms  
  17. 时间因子 = 1.0  
  18. 插值因子 = 1.0  
  19. 属性值 = 40.0  

观察运行结果,与我们预期的结果基本一致。


---------------------------------------------------------------------------------------------------------------

ValueAnimator和ObjectAnimator实现动画实例

---------------------------------------------------------------------------------------------------------------

使用ValueAnimator实现动画

       ValueAnimator是Property Animation系统的核心类,它包含了配置Property Animation属性的大部分方法,那要实现一个Property Animation,都需要直接或间接使用ValueAnimator类。那接下来我们将借助ValueAnimator类提供的方法来实现各种各样的动画效果,不过在此之前我们来学习一下实现动画的几个关键知识点。

Animator监听器介绍

      Property Animation提供了Animator.AnimatorListener和Animator.AnimatorUpdateListener两个监听器用于动画在播放过程中的重要动画事件。下面是两个监听器接口和方法的一些介绍和说明:
Animator.AnimatorListener:
       onAnimationStart() —— 动画开始时调用;
       onAnimationEnd() —— 动画结束时调用;
       onAnimationRepeat() —— 动画循环播放时调用;
       onAnimationCancel() —— 动画被取消时调用。不管终止的方式如何,被取消的动画仍然会调onAnimationEnd();
Animator.AnimatorUpdateListener:
       onAnimationUpdate() —— 动画每播放一帧时调用。在动画过程中,可侦听此事件来获取并使用 ValueAnimator 计算出来的属性值。利用传入事件的 ValueAnimator 对象,调用其 getAnimatedValue() 方法即可获取当前的属性值。如果使用 ValueAnimator来实现动画的话 ,则必需实现此侦听器。

使用及自定义Interpolator

       关于Interpolator(插值器)的知识,我们在上一篇博文就有接触,在这里再做详细介绍:插值器它定义了动画变化过程中的属性变化规则,它根据时间比例因子计算出一个插值因子,用于设定目标对象的动画执行是否为线性变化、非线性变化或先加速后减速等等。Android系统本身内置了一些通用的Interpolator(插值器),如下:

我们可以使用以上系统提供的插值器对目标对象实现一些诸如先加速后加速、弹跳和回移的动画效果。那如果以上这些插值器都无法满足我们的具体开发需求的话,那么也可以通过继承TimeInterpolator类来自定义自己的Interpolator,在自定义的Interpolator类中只需要要重写getInterpolation(float input)方法,并在该方法通过相关的计算,再返回计算后的结果(插值因子)就搞定,具体示例如下:
[java]  view plain copy
  1. public class CustomInterpolator implements TimeInterpolator {  
  2.   
  3.     @Override  
  4.     public float getInterpolation(float input) {  
  5.         // 编写相关的逻辑计算  
  6.         //input *= 0.8f;  
  7.         return input * input;  
  8.     }  
  9. }  
上面是一个简单的示例,我们要实现一个自定义的Interpolator,只需在方法中编写相关的罗就计算就行。

使用及自定义TypeEvaluator

        上面学习了如何自定义Interpolator计算返回插值因子,那下一步就是使用TypeEvaluator根据插值因子计算属性值,Android系统可识别的类型包括int、float和颜色,分别由 IntEvaluator、 FloatEvaluator、 ArgbEvaluator 提供支持,如下:

同样的,如何系统提供的TypeEvaluator不能满足我们的要求,我们也可以通过继承TypeEvaluator类来定义自己的Evaluator,在自定义的TypeEvaluator类中只需要要重写getInterpolation(float input)方法,并在该方法通过相关的计算,再返回计算后的结果(插值因子)就搞定,具体示例如下:
[java]  view plain copy
  1. import android.animation.TypeEvaluator;  
  2. import android.annotation.SuppressLint;  
  3.   
  4. @SuppressLint("NewApi")  
  5. public class CustomEvaluator implements TypeEvaluator<Number> {  
  6.   
  7.     @Override  
  8.     public Float evaluate(float fraction, Number startValue, Number endValue) {  
  9.         // TODO Auto-generated method stub  
  10.         float propertyResult = 0;  
  11.         /*float startFloat = startValue.floatValue(); 
  12.         return (startFloat + fraction * (endValue.floatValue() - startFloat));*/  
  13.         return propertyResult;  
  14.     }  
  15. }  

使用ValueAnimator实现动画的步骤及实践

那一般使用ValueAnimator实现动画分为以下七个步骤:

      1. 调用ValueAnimation类中的ofInt(int...values)、ofFloat(String propertyName,float...values)等静态方法实例化ValueAnimator对象,并设置目标属性的属性名、初始值或结束值等值;

      2.调用addUpdateListener(AnimatorUpdateListener mListener)方法为ValueAnimator对象设置属性变化的监听器;

      3.创建自定义的Interpolator,调用setInterpolator(TimeInterpolator value)为ValueAniamtor设置自定义的Interpolator;(可选,不设置默认为缺省值)

      4.创建自定义的TypeEvaluator,调用setEvaluator(TypeEvaluator value)为ValueAnimator设置自定义的TypeEvaluator;(可选,不设置默认为缺省值)

      5.在AnimatorUpdateListener 中的实现方法为目标对象的属性设置计算好的属性值。

      6.设置动画的持续时间、是否重复及重复次数等属性;

      7.为ValueAnimator设置目标对象并开始执行动画。


接下来我们将按照以上步骤实现我们的动画。首先开始第一个例子:就是放置在屏幕中的一张图片,通过不断地改变该图片相对于左边屏幕的距离,从而实现图片左右滑动的动画的效果。下面贴出实现动画的关键代码:
[java]  view plain copy
  1. /** 
  2.      * 使用ValueAnimator改变Imageview的margin的值 
  3.      */  
  4.     public void marginValueAnimator(){  
  5.         //1.调用ofInt(int...values)方法创建ValueAnimator对象  
  6.         ValueAnimator mAnimator = ValueAnimator.ofInt(0,screenWidth - mImageViewTest.getWidth());  
  7.         //2.为目标对象的属性变化设置监听器  
  8.         mAnimator.addUpdateListener(new AnimatorUpdateListener() {  
  9.               
  10.             @Override  
  11.             public void onAnimationUpdate(ValueAnimator animation) {  
  12.                 // 3.为目标对象的属性设置计算好的属性值  
  13.                 int animatorValue = (int)animation.getAnimatedValue();  
  14.                 MarginLayoutParams marginLayoutParams = (MarginLayoutParams) mImageViewTest.getLayoutParams();  
  15.                 marginLayoutParams.leftMargin = animatorValue;  
  16.                 mImageViewTest.setLayoutParams(marginLayoutParams);  
  17.             }  
  18.         });  
  19.         //4.设置动画的持续时间、是否重复及重复次数等属性  
  20.         mAnimator.setDuration(2000);  
  21.         mAnimator.setRepeatCount(3);  
  22.         mAnimator.setRepeatMode(ValueAnimator.REVERSE);  
  23.         //5.为ValueAnimator设置目标对象并开始执行动画  
  24.         mAnimator.setTarget(mImageViewTest);  
  25.         mAnimator.start();  
  26.     }  
上面代码中,我们将属性的初始值、结束值分为设置为0和(屏幕宽度-ImageView控件的宽度),然后在监听方法中,通过调用getAnimationValue()获取动画在播放过程中属性值的变化情况,然后将属性值赋给ImageView对象的marginLeft。接着调用setXX()方法设置动画的相关属性,最后调用start()方法,执行动画。动画的实现流程和我们基本按照我们上面罗列的步骤基本一致,只不过没有设置Interpolator和TypeEvaluator,系统会自动默认为缺省。 ImageView的实例化代码和screenWidth屏幕宽度的获取代码在此不列出,具体的代码在文章末尾可以提供下载。
简单讲解了一下代码后,我们下面来看看运行的效果图:

观察上面的运行效果图,我们成功实现了图片左右滑动的效果。
下面我们接着使用ValueAnimator实现图片的缩放效果的功能,先来看一下具体实现的代码,如下:
[java]  view plain copy
  1. /** 
  2.      * 使用ValueAnimator实现图片缩放动画 
  3.      */  
  4.     public void scaleValueAnimator(){  
  5.         //1.设置目标属性名及属性变化的初始值和结束值  
  6.         PropertyValuesHolder mPropertyValuesHolderScaleX = PropertyValuesHolder.ofFloat("scaleX"1.0f,0.0f);  
  7.         PropertyValuesHolder mPropertyValuesHolderScaleY = PropertyValuesHolder.ofFloat("scaleY"1.0f,0.0f);  
  8.         ValueAnimator mAnimator = ValueAnimator.ofPropertyValuesHolder(mPropertyValuesHolderScaleX,mPropertyValuesHolderScaleY);  
  9.         //2.为目标对象的属性变化设置监听器  
  10.         mAnimator.addUpdateListener(new AnimatorUpdateListener() {  
  11.               
  12.             @Override  
  13.             public void onAnimationUpdate(ValueAnimator animation) {  
  14.                 // 3.根据属性名获取属性变化的值分别为ImageView目标对象设置X和Y轴的缩放值  
  15.                 float animatorValueScaleX =  (float) animation.getAnimatedValue("scaleX");  
  16.                 float animatorValueScaleY = (float) animation.getAnimatedValue("scaleY");  
  17.                 mImageViewTest.setScaleX(animatorValueScaleX);  
  18.                 mImageViewTest.setScaleY(animatorValueScaleY);  
  19.                   
  20.             }  
  21.         });  
  22.         //4.为ValueAnimator设置自定义的Interpolator  
  23.         mAnimator.setInterpolator(new CustomInterpolator());  
  24.         //5.设置动画的持续时间、是否重复及重复次数等属性  
  25.         mAnimator.setDuration(2000);  
  26.         mAnimator.setRepeatCount(3);  
  27.         mAnimator.setRepeatMode(ValueAnimator.REVERSE);  
  28.         //6.为ValueAnimator设置目标对象并开始执行动画  
  29.         mAnimator.setTarget(mImageViewTest);  
  30.         mAnimator.start();  
  31.     }  
上面代码列表中,我们通过PropertyValuesHolder类为目标属性名来设置属性值的初始值和结束值,然后ValueAnimator通过调用ofPropertyValuesHolder(PropertyValuesHolder...values)设置已经配置好的PropertyValuesHolder对象。不过需要注意的是使用 PropertyValuesHolder设置的属性必须是目标对象的属性中有setXX()方法才能进行设置,例如ImageView对象中有setScaleX()方法才能为ImageView设置对应属性的PropertyValuesHolder对象。接着ValueAnimator对象调用setInterpolator(TimeInterpolator value)设置自定义的Interpolator,类名为CustomInterpolator,具体的代码如下:
[java]  view plain copy
  1. package com.androidleaf.animation.customproperty;  
  2.   
  3. import android.animation.TimeInterpolator;  
  4. import android.annotation.SuppressLint;  
  5.   
  6. @SuppressLint("NewApi")  
  7. public class CustomInterpolator implements TimeInterpolator {  
  8.   
  9.     @Override  
  10.     public float getInterpolation(float input) {  
  11.         input *= 0.8f;  
  12.         return input * input;  
  13.     }  
  14. }  
接着,为ValueAnimator对象设置相应的属性,如动画重复次数、动画所持续的时间等等。最后,为ValueAnimator设置目标对象并启动动画。我们运行一下程序,看一下运行的效果图,如下:

观察上面的运行效果图,我们可以发现图片不仅实现了收缩动画,而且还实现了一个迅速的收缩和回弹的效果,原因是我们通过自定义Interpolator,使得ScaleX和ScaleY属性值的变化呈非线性的变化,那涉及到自定义Interpolator的知识在上面我们已经学习过了,在此就不重复讲解了。

最后我们再来使用ValueAnimator实现一个比较实用的动画效果,就是通过动态改变一个ListView列表的高,来动态显示或隐藏ListView。我们先来看一下关键代码,如下:
[java]  view plain copy
  1. /** 
  2.      * 隐藏或显示ListView的动画 
  3.      */  
  4.     public void hideOrShowListViewAnimator(final int startValue,final int endValue){  
  5.         //1.设置属性的初始值和结束值  
  6.         ValueAnimator mAnimator = ValueAnimator.ofInt(0,100);  
  7.         //2.为目标对象的属性变化设置监听器  
  8.         mAnimator.addUpdateListener(new AnimatorUpdateListener() {  
  9.             @Override  
  10.             public void onAnimationUpdate(ValueAnimator animation) {  
  11.                 // TODO Auto-generated method stub  
  12.                 int animatorValue = (int)animation.getAnimatedValue();  
  13.                 float fraction = animatorValue/100f;  
  14.                 IntEvaluator mEvaluator = new IntEvaluator();  
  15.                 //3.使用IntEvaluator计算属性值并赋值给ListView的高  
  16.                 mListView.getLayoutParams().height = mEvaluator.evaluate(fraction, startValue, endValue);  
  17.                 mListView.requestLayout();  
  18.             }  
  19.         });  
  20.         //4.为ValueAnimator设置LinearInterpolator  
  21.         mAnimator.setInterpolator(new LinearInterpolator());  
  22.         //5.设置动画的持续时间  
  23.         mAnimator.setDuration(500);  
  24.         //6.为ValueAnimator设置目标对象并开始执行动画  
  25.         mAnimator.setTarget(mListView);  
  26.         mAnimator.start();  
  27.     }  
上面代码列表中,需传递的两个参数分别为属性值的初始值和结束值,当需要显示ListView时,传入0和800像素值,当需要隐藏ListView时,传入800和0像素值,ListView的初始化高度默认是0。运行程序,动画效果如下:



ValueAnimator的相关知识就讲到这里,想要做出更炫更实用的动画,读者可自行深入研究ValueAniamator类的实现和原理。

使用ObjectAnimator实现动画

      我们接着学习一个比较重要的动画实现类--ObjectAnimator。该类作为ValueAnimator的子类不仅继承了ValueAnimator的所有方法和特性,并且还封装很多实用的方法,方便开发人员快速实现动画。同时,由于属性值会自动更新,使用ObjectAnimator实现动画不需要像ValueAnimator那样必须实现 ValueAnimator.AnimatorUpdateListener ,因此实现任意对象的动画显示就更加容易了。我们在大部分的开发工作中,都会使用ObjectAnimator而非ValueAnimator实现我们所需的动画效果。
     前几篇博文我们都介绍了View Animation,我们了解了其实现View对象动画的特点,即View Animation本身是通过改变View的绘制方式来实现动画的,View对象本身的属性值并没有改变,对象仍然停留在原始位置。那Android为了消除这一弊病,在 Android 3.0 中给 View 增加了一些新的属性以及相应的 getter、setter 方法。Property Animation系统可以通过修改 View 对象实际的属性值来实现屏幕上的动画效果。此外,当属性值发生变化时,Views 也会自动调用 invalidate() 方法来刷新屏幕。 View 类中新增的便于实现 property 动画的属性包括:
     (1) translationX 和 translationY:这两个属性控制着 View 的屏幕位置坐标变化量,以 layout 容器的左上角为坐标原点;
     (2) rotation、rotationX 和 rotationY:这三个属性控制着 2D 旋转角度(rotation属性)和围绕某枢轴点的 3D 旋转角度;
     (3) scaleX、scaleY:这两个属性控制着 View 围绕某枢轴点的 2D 缩放比例;
     (4) pivotX 和 pivotY: 这两个属性控制着枢轴点的位置,前述的旋转和缩放都是以此点为中心展开的,缺省的枢轴点是 View 对象的中心点;
     (5) x 和 y:这是指 View 在容器内的最终位置,等于 View 左上角相对于容器的坐标加上 translationX 和 translationY 后的值;
     (6)alpha:表示 View 的 alpha 透明度。缺省值为 1 (不透明),为 0 则表示完全透明(看不见);
要动画显示 View 对象的某个属性,比如颜色或旋转值,我们所有要做的事情就是创建一个 Property animation,并设定对应的 View 属性。那接下来我们就用ObjectAnimator类来分别实现View的透明度渐变、收缩、移动和旋转等动画效果,那在此之前我们也来总结下使用ObjectAnimator实现动画的几个步骤,如下:
1.通过调用ofFloat()、ofInt()等方法创建ObjectAnimator对象,并设置目标对象、需要改变的目标属性名、初始值和结束值;
2.设置动画的持续时间、是否重复及重复次数等属性;
3.启动动画。
我们发现使用ObjectAnimator实现动画比ValueAnimator更加简单和方便,下面分别是我们实例中使用ObjectAnimator实现透明度渐变、伸缩、移动和旋转的代码(具体代码在文章末尾提供下),如下:
[java]  view plain copy
  1. public class ObjectAnimatorFragment extends Fragment implements OnClickListener{  
  2.   
  3.     private ListView mListViewFront;  
  4.     private ListView mListViewReverse;  
  5.     private Button mButtonFlip;  
  6.     private Button mButtonAlpha;  
  7.     private Button mButtonScale;  
  8.     private Button mButtonTranslate;  
  9.     private Button mButtonRotate;  
  10.     private Button mButtonSet;  
  11.     private ImageView mImageView;  
  12.       
  13.     private int screenWidth = 0;  
  14.     private int screenHeight = 0;  
  15.       
  16.     String[] frontStrs = {  
  17.             "Front Page 1",  
  18.             "Front Page 2",  
  19.             "Front Page 3",  
  20.             "Front Page 4",  
  21.             "Front Page 5",  
  22.             "Front Page 6",  
  23.     };  
  24.       
  25.     String[] reverseStrs = {  
  26.             "Reverse Page 1",  
  27.             "Reverse Page 2",  
  28.             "Reverse Page 3",  
  29.             "Reverse Page 4",  
  30.             "Reverse Page 5",  
  31.             "Reverse Page 6",  
  32.     };  
  33.     @Override  
  34.     public void onActivityCreated(Bundle savedInstanceState) {  
  35.         // TODO Auto-generated method stub  
  36.         super.onActivityCreated(savedInstanceState);  
  37.         DisplayMetrics metrics = new DisplayMetrics();  
  38.         getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);  
  39.         float density = metrics.density;  
  40.         //screenWidth = (int)(metrics.widthPixels * density + 0.5f);  
  41.         //screenHeight = (int)(metrics.heightPixels * density + 0.5f);  
  42.         screenWidth = metrics.widthPixels;  
  43.         screenHeight = metrics.heightPixels;  
  44.     }  
  45.   
  46.     @Override  
  47.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  48.             Bundle savedInstanceState) {  
  49.         // TODO Auto-generated method stub  
  50.         View rootView = inflater.inflate(R.layout.fragment_objectanimator, container, false);  
  51.         mListViewFront = (ListView) rootView.findViewById(R.id.front_page_listview);  
  52.         mListViewReverse = (ListView) rootView.findViewById(R.id.reverse_page_listview);  
  53.         mButtonFlip = (Button)rootView.findViewById(R.id.button_flip);  
  54.         mButtonFlip.setOnClickListener(this);  
  55.         mButtonAlpha = (Button)rootView.findViewById(R.id.button_alpha);  
  56.         mButtonAlpha.setOnClickListener(this);  
  57.         mButtonScale = (Button)rootView.findViewById(R.id.button_scale);  
  58.         mButtonScale.setOnClickListener(this);  
  59.         mButtonTranslate = (Button)rootView.findViewById(R.id.button_translate);  
  60.         mButtonTranslate.setOnClickListener(this);  
  61.         mButtonRotate = (Button)rootView.findViewById(R.id.button_rotate);  
  62.         mButtonRotate.setOnClickListener(this);  
  63.         mButtonSet = (Button)rootView.findViewById(R.id.button_set);  
  64.         mButtonSet.setOnClickListener(this);  
  65.         mImageView = (ImageView)rootView.findViewById(R.id.objectanimator_imageview);  
  66.         mImageView.setOnClickListener(this);  
  67.         initData();  
  68.         return rootView;  
  69.     }  
  70.   
  71.     public void initData(){  
  72.         ArrayAdapter<String> frontListData = new ArrayAdapter<String>(getActivity(), R.layout.layout_objectanimator_item,R.id.objectanimtor_item_textview, frontStrs);  
  73.         ArrayAdapter<String> reverseListData = new ArrayAdapter<String>(getActivity(), R.layout.layout_objectanimator_item,R.id.objectanimtor_item_textview, reverseStrs);  
  74.           
  75.         mListViewFront.setAdapter(frontListData);  
  76.         mListViewReverse.setAdapter(reverseListData);  
  77.         mListViewReverse.setRotationX(-90.0f);  
  78.       
  79.     }  
  80.     @Override  
  81.     public void onPause() {  
  82.         // TODO Auto-generated method stub  
  83.         super.onPause();  
  84.           
  85.     }  
  86.   
  87.     @Override  
  88.     public void onClick(View v) {  
  89.         // TODO Auto-generated method stub  
  90.         switch (v.getId()) {  
  91.         case R.id.button_flip:  
  92.             flip();  
  93.             break;  
  94.         case R.id.button_alpha:  
  95.             alphaAnimator();  
  96.             break;  
  97.         case R.id.button_scale:  
  98.             scaleAnimator();  
  99.             break;  
  100.         case R.id.button_translate:  
  101.             translateAniamtor();  
  102.             break;  
  103.         case R.id.button_rotate:  
  104.             rotateAniamtor();  
  105.             break;  
  106.         case R.id.button_set:  
  107.             setAnimator();  
  108.             break;  
  109.         case R.id.objectanimator_imageview:  
  110.             mListViewFront.setVisibility(View.VISIBLE);  
  111.             mImageView.setVisibility(View.GONE);  
  112.             break;  
  113.         default:  
  114.             break;  
  115.         }  
  116.     }  
  117.       
  118.     /** 
  119.      * 翻转动画效果 
  120.      */  
  121.     public void flip(){  
  122.         final ListView visibleView;  
  123.         final ListView invisibleView;  
  124.         if(mListViewFront.getVisibility() == View.GONE){  
  125.             visibleView = mListViewReverse;  
  126.             invisibleView = mListViewFront;  
  127.         }else{  
  128.             visibleView = mListViewFront;  
  129.             invisibleView = mListViewReverse;  
  130.         }  
  131.         //创建ListView从Visible到Gone的动画  
  132.         ObjectAnimator visibleToInVisable = ObjectAnimator.ofFloat(visibleView, "rotationX"0.0f,90.0f);  
  133.         //设置插值器  
  134.         visibleToInVisable.setInterpolator(new AccelerateInterpolator());  
  135.         visibleToInVisable.setDuration(500);  
  136.           
  137.         //创建ListView从Gone到Visible的动画  
  138.         final ObjectAnimator invisibleToVisible = ObjectAnimator.ofFloat(invisibleView, "rotationX", -90.0f,0.0f);  
  139.         //设置插值器  
  140.         invisibleToVisible.setInterpolator(new DecelerateInterpolator());  
  141.         invisibleToVisible.setDuration(500);  
  142.           
  143.         visibleToInVisable.addListener(new AnimatorListenerAdapter() {  
  144.             @Override  
  145.             public void onAnimationEnd(Animator animation) {  
  146.                 // TODO Auto-generated method stub  
  147.                 super.onAnimationEnd(animation);  
  148.                 visibleView.setVisibility(View.GONE);  
  149.                 invisibleToVisible.start();  
  150.                 invisibleView.setVisibility(View.VISIBLE);  
  151.             }  
  152.         });  
  153.         visibleToInVisable.start();  
  154.     }  
  155.       
  156.     /** 
  157.      * 渐变动画效果 
  158.      */  
  159.     public void alphaAnimator(){  
  160.         ListView alphaListView = null;  
  161.         if(mListViewFront.getVisibility() == View.GONE){  
  162.             alphaListView = mListViewReverse;  
  163.         }else{  
  164.             alphaListView = mListViewFront;  
  165.         }  
  166.         //1、通过调用ofFloat()方法创建ObjectAnimator对象,并设置目标对象、需要改变的目标属性名、初始值和结束值;  
  167.         ObjectAnimator mAnimatorAlpha = ObjectAnimator.ofFloat(alphaListView, "alpha"1.0f,0.0f);  
  168.         //2、设置动画的持续时间、是否重复及重复次数属性;  
  169.         mAnimatorAlpha.setRepeatMode(Animation.REVERSE);  
  170.         mAnimatorAlpha.setRepeatCount(3);  
  171.         mAnimatorAlpha.setDuration(1000);  
  172.         //3、启动动画  
  173.         mAnimatorAlpha.start();  
  174.     }  
  175.       
  176.     /** 
  177.      * 伸缩动画效果 
  178.      */  
  179.     public void scaleAnimator(){  
  180.         ListView scaleListView = null;  
  181.         if(mListViewFront.getVisibility() == View.GONE){  
  182.             scaleListView = mListViewReverse;  
  183.         }else{  
  184.             scaleListView = mListViewFront;  
  185.         }  
  186.           
  187.         ObjectAnimator mAnimatorScaleX = ObjectAnimator.ofFloat(scaleListView, "scaleX"1.0f,0.0f);  
  188.         mAnimatorScaleX.setRepeatMode(Animation.REVERSE);  
  189.         mAnimatorScaleX.setRepeatCount(3);  
  190.         mAnimatorScaleX.setDuration(1000);  
  191.           
  192.         ObjectAnimator mAnimatorScaleY = ObjectAnimator.ofFloat(scaleListView, "scaleY"1.0f,0.0f);  
  193.         mAnimatorScaleY.setRepeatMode(Animation.REVERSE);  
  194.         mAnimatorScaleY.setRepeatCount(3);  
  195.         mAnimatorScaleY.setDuration(1000);  
  196.           
  197.         mAnimatorScaleX.start();  
  198.         mAnimatorScaleY.start();  
  199.     }  
  200.       
  201.     /** 
  202.      * 位移动画效果 
  203.      */  
  204.     public void translateAniamtor(){  
  205.         ListView translateListView = null;  
  206.         if(mListViewFront.getVisibility() == View.GONE){  
  207.             translateListView = mListViewReverse;  
  208.         }else{  
  209.             translateListView = mListViewFront;  
  210.         }  
  211.           
  212.         ObjectAnimator mAnimatorTranslateX = ObjectAnimator.ofFloat(translateListView, "translationX"0.0f,screenWidth/2);  
  213.         mAnimatorTranslateX.setRepeatMode(Animation.REVERSE);  
  214.         mAnimatorTranslateX.setRepeatCount(3);  
  215.         mAnimatorTranslateX.setDuration(1000);  
  216.           
  217.         ObjectAnimator mAnimatorTranslateY = ObjectAnimator.ofFloat(translateListView, "translationY"0.0f,screenHeight/2);  
  218.         mAnimatorTranslateY.setRepeatMode(Animation.REVERSE);  
  219.         mAnimatorTranslateY.setRepeatCount(3);  
  220.         mAnimatorTranslateY.setDuration(1000);  
  221.           
  222.         mAnimatorTranslateX.start();  
  223.         mAnimatorTranslateY.start();  
  224.     }  
  225.       
  226.     /** 
  227.      * 旋转动画效果 
  228.      */  
  229.     public void rotateAniamtor(){  
  230.         ListView rotateListView = null;  
  231.         if(mListViewFront.getVisibility() == View.GONE){  
  232.             rotateListView = mListViewReverse;  
  233.         }else{  
  234.             rotateListView = mListViewFront;  
  235.         }  
  236.         ObjectAnimator mAnimatorRotate = ObjectAnimator.ofFloat(rotateListView, "rotation"0.0f,360.0f);  
  237.         mAnimatorRotate.setRepeatMode(Animation.REVERSE);  
  238.         mAnimatorRotate.setRepeatCount(2);  
  239.         mAnimatorRotate.setDuration(2000);  
  240.           
  241.         mAnimatorRotate.start();  
  242.     }  
  243.     /** 
  244.      * 动画集合 
  245.      */  
  246.     public void setAnimator(){  
  247.         ListView setListView = null;  
  248.         if(mListViewFront.getVisibility() == View.GONE){  
  249.             setListView = mListViewReverse;  
  250.         }else{  
  251.             setListView = mListViewFront;  
  252.         }  
  253.         setListView.setVisibility(View.GONE);  
  254.         if(mImageView.getVisibility() == View.GONE){  
  255.             mImageView.setVisibility(View.VISIBLE);  
  256.         }  
  257.         //代码方式设置动画  
  258.         codeAnimatorSet(mImageView);  
  259.           
  260.         //用ViewPropertyAnimator实现动画  
  261.         //viewPropertyAnimator(setListView);  
  262.           
  263.         //加载XML文件中的动画  
  264.         /*AnimatorSet mAnimatorSet = (AnimatorSet) AnimatorInflater.loadAnimator(getActivity(), R.animator.property_animation_animatorset); 
  265.         mAnimatorSet.setTarget(mImageView); 
  266.         mAnimatorSet.start();*/  
  267.     }  
  268.       
  269.     /** 
  270.      * 使用编码方式实现动画效果 
  271.      * @param mImageView 
  272.      */  
  273.     public void codeAnimatorSet(ImageView mImageView){  
  274.         AnimatorSet mAnimatorSet = new AnimatorSet();  
  275.           
  276.         ObjectAnimator mAnimatorSetRotateX = ObjectAnimator.ofFloat(mImageView, "rotationX"0.0f,360.0f);  
  277.         mAnimatorSetRotateX.setDuration(3000);  
  278.           
  279.         ObjectAnimator mAnimatorSetRotateY = ObjectAnimator.ofFloat(mImageView, "rotationY"0.0f,360.0f);  
  280.         mAnimatorSetRotateY.setDuration(3000);  
  281.           
  282.         ObjectAnimator mAnimatorScaleX = ObjectAnimator.ofFloat(mImageView, "scaleX"1.0f,0.5f);  
  283.         mAnimatorScaleX.setRepeatCount(1);  
  284.         mAnimatorScaleX.setRepeatMode(Animation.REVERSE);  
  285.         mAnimatorScaleX.setDuration(1500);  
  286.           
  287.         ObjectAnimator mAnimatorScaleY = ObjectAnimator.ofFloat(mImageView, "scaleY"1.0f,0.5f);  
  288.         mAnimatorScaleY.setRepeatCount(1);  
  289.         mAnimatorScaleY.setRepeatMode(Animation.REVERSE);  
  290.         mAnimatorScaleY.setDuration(1500);  
  291.           
  292.         mAnimatorSet.play(mAnimatorSetRotateY).with(mAnimatorScaleX);  
  293.         mAnimatorSet.play(mAnimatorScaleX).with(mAnimatorScaleY);  
  294.         mAnimatorSet.play(mAnimatorSetRotateY).before(mAnimatorSetRotateX);  
  295.           
  296.         mAnimatorSet.start();  
  297.     }  
  298.       
  299.     public void viewPropertyAnimator(ListView mListViewHolder){  
  300.         mListViewHolder.animate().cancel();  
  301.         mListViewHolder.animate().rotationX(360.0f).setDuration(3000).start();  
  302.     }  
  303.       
  304. }  
Fragment的布局界面代码如下:
[html]  view plain copy
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical"  
  6.     android:background="@color/view_animation_background"  
  7.      >  
  8.   <LinearLayout   
  9.       android:layout_width="match_parent"  
  10.       android:layout_height="wrap_content"  
  11.       android:orientation="horizontal"  
  12.       >  
  13.        <Button  
  14.            android:layout_width="wrap_content"  
  15.            android:layout_height="wrap_content"  
  16.            android:text="翻转"  
  17.            android:id="@+id/button_flip"   
  18.            />  
  19.        <Button  
  20.            android:layout_width="wrap_content"  
  21.            android:layout_height="wrap_content"  
  22.            android:text="Alpha"  
  23.            android:id="@+id/button_alpha"   
  24.            />  
  25.        <Button  
  26.            android:layout_width="wrap_content"  
  27.            android:layout_height="wrap_content"  
  28.            android:text="Scale"  
  29.            android:id="@+id/button_scale"   
  30.            />  
  31.        <Button  
  32.            android:layout_width="wrap_content"  
  33.            android:layout_height="wrap_content"  
  34.            android:text="Translate"  
  35.            android:id="@+id/button_translate"   
  36.            />  
  37.      </LinearLayout>  
  38.      <LinearLayout   
  39.       android:layout_width="match_parent"  
  40.       android:layout_height="wrap_content"  
  41.       android:orientation="horizontal"  
  42.       >  
  43.        <Button  
  44.            android:layout_width="wrap_content"  
  45.            android:layout_height="wrap_content"  
  46.            android:text="Rotate"  
  47.            android:id="@+id/button_rotate"   
  48.            />  
  49.        <Button  
  50.            android:layout_width="wrap_content"  
  51.            android:layout_height="wrap_content"  
  52.            android:text="Set"  
  53.            android:id="@+id/button_set"   
  54.            />  
  55.      </LinearLayout>  
  56.    <ListView  
  57.        android:layout_width="match_parent"  
  58.        android:layout_height="0dp"  
  59.        android:layout_weight="1"  
  60.        android:divider="@color/propertyanimator_background"  
  61.        android:dividerHeight="0.5dp"  
  62.        android:id="@+id/front_page_listview"   
  63.        />  
  64.    <ListView  
  65.        android:layout_width="match_parent"  
  66.        android:layout_height="0dp"  
  67.        android:layout_weight="1"  
  68.        android:divider="@color/propertyanimator_background"  
  69.        android:visibility="gone"  
  70.        android:dividerHeight="0.5dp"  
  71.        android:id="@+id/reverse_page_listview"   
  72.        />  
  73.    <ImageView  
  74.        android:layout_width="match_parent"  
  75.        android:layout_height="match_parent"  
  76.        android:scaleType="fitStart"  
  77.        android:visibility="visible"  
  78.        android:src="@drawable/liduohai"  
  79.        android:id="@+id/objectanimator_imageview"   
  80.        />  
  81. </LinearLayout>  
代码量有些多,不过每个方法都有注释,而且逻辑也比较简单,所以不难理解,只要读者花点时间研究一下就可以很好的掌握。那接下来我们运行程序,运行及操作效果图如下:
(1)ListView翻转、渐变透明、伸缩、 移动和旋转动画效果: (2)图片翻转集合动画效果:
                           
    上面的动画效果很炫吧,不过呢 使用ObjectAnimator实现动画也有一些要求和限制,一般有以下几点需要注意:
       1.动画显示的属性必须带有一个 setter 方法(以骆驼拼写法命名),格式类似 set<propertyName>()。 因为 ObjectAnimator 会在动画期间自动更新属性值,它必须能够用此 setter 方法访问到该属性。 例如:假设属性名称为foo,则需要有一个setFoo()方法。 而你如果此 setter 方法不存在,那么我们可以有以下三种选择:
        <1>如果权限允许的话,直接在类中增加此 setter 方法;
        <2>修改封装类来增加此 setter 方法,并让该封装类来接收属性值并传给初始的对象;
        <3>换用 ValueAnimator。
      2.如果在调用 ObjectAnimator 的某个工厂方法时,我们只为 values... 参数指定了一个值,那此值将被认定为动画属性的结束值。 这样的话,动画显示的属性必须带有一个 getter 方法,用于获取动画的起始值。 此 getter 方法必须以get<propertyName>()的格式命名。 例如:假设属性名为foo,则需要有一个getFoo()方法。
      3.动画属性的 getter 方法(如果必要的话)和 setter 方法所操作数据的类型必须与 ObjectAnimator 中设定的起始和结束值相同。 

小结:今天我们主要了解了ValueAnimator和ObjectAnimator的概念及其它们关系和特点,并通过实例学习了如何使用这两个类实现了非常炫的动画效果。总结一下本文我们需要掌握的知识点:(1)了解并学会使用动画监听器;(2)了解Interpolator的概念及作用,并学会自定义Interpolator;(3)解TypeEvaluator的概念及作用,并学会自定义TypeEvaluator;(4)熟练使用ValueAnimator实现动画;(5)熟练使用ObjectAnimator实现动画;(6)使用ObjectAnimator实现动画的一些限制和需要注意的事项。


---------------------------------------------------------------------------------------------------------------

ViewGroup中的Layout设置动画实例

---------------------------------------------------------------------------------------------------------------

LayoutTransition 类介绍

       我们先来认识一个重要的类--LayoutTransition,该类是Android API提供的用于动画显示ViewGroup中的Layout的帮助类,我们可以使用该类设置动画并绑定目标Layout。那一般在什么情况下为Layout设置动画效果呢?比方说,如果我们需要对一个Layout实现动态地添加或删除子View对象,那么我们可以分别为需添加或删除的View对象在移动到新的位置的过程添加动画形式。一般地,Layout中的View对象有四种动画变化的形式,如下:
      APPEARING —— 元素在容器中显现时需要动画显示。
      CHANGE_APPEARING —— 由于容器中要显现一个新的元素,其它元素的变化需要动画显示。
      DISAPPEARING —— 元素在容器中消失时需要动画显示。
      CHANGE_DISAPPEARING —— 由于容器中某个元素要消失,其它元素的变化需要动画显示。
其实这四种动画形式也很好理解,举个例子,我们有一个GridLayout中有多个Button对象,如果需要删除其中一个Button对象的话,该Button对象可以设置动画(即DISAPPEARING 动画形式),Layout中的其它Button对象此时移动到新的位置的过程中也可以设置相关的动画(即CHANGE_DISAPPEARING 动画形式),如图下:

而当需要为Layout添加一个新的Button对象时,此时我们可以为这个 新添加的Button对象设置动画(即APPEARING 动画形式),Layout中的其它Button对象此时移动到新的位置过程也可以设置相关的动画(即CHANGE_APPEARING动画形式 ),如图下:

那基本了解了这四种动画形式后,我们又应该如何使用呢?LayoutTransition类中有一个setAnimator(int transitionType, Animator animator)用于添加动画以及设置动画形式类型,在开发当中,我们只需要确定好需要在不同的动画类型中显示什么动画,然后创建并设置好相关的动画对象,最后使用该方法将两者进行绑定就行。那废话不多说,下面我们通过一个例子来学习一下如何为Layout添加动画。代码实例在文章末尾提供下载,
下面是LayoutAnimatorFragment主界面及和主要功能代码,如下:
[java]  view plain copy
  1. /** 
  2.  * @author AndroidLeaf 
  3.  *ViewGroup中Layout的动画实现 
  4.  *主要知识点: 
  5.  *调用 LayoutTransition 对象的 setAnimator() 方法来定义下列动画方式,调用参数是 Animator 
  6.  *对象和以下 LayoutTransition 常量: 
  7.  *  (1)APPEARING —— 元素在容器中显现时需要动画显示。 
  8.  *  (2)CHANGE_APPEARING —— 由于容器中要显现一个新的元素,其它元素的变化需要动画显示。 
  9.  *  (3)DISAPPEARING —— 元素在容器中消失时需要动画显示。 
  10.  *  (4)CHANGE_DISAPPEARING —— 由于容器中某个元素要消失,其它元素的变化需要动画显示。 
  11.  */  
  12. public class LayoutAnimatorFragment extends Fragment implements OnClickListener{  
  13.   
  14.     private Button mButtonAdd;  
  15.     private Button mButtonReset;  
  16.     private GridLayout mGridLayout;  
  17.     private int buttonNumbers = 1;  
  18.       
  19.     private LayoutTransition mLayoutTransition;  
  20.       
  21.     @Override  
  22.     public void onActivityCreated(Bundle savedInstanceState) {  
  23.         // TODO Auto-generated method stub  
  24.         super.onActivityCreated(savedInstanceState);  
  25.           
  26.     }  
  27.   
  28.     @Override  
  29.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  30.             Bundle savedInstanceState) {  
  31.         // TODO Auto-generated method stub  
  32.         View rootView = inflater.inflate(R.layout.fragment_layoutanimator, container, false);  
  33.         initViews(rootView);  
  34.         return rootView;  
  35.     }  
  36.       
  37.     public void initViews(View rootView){  
  38.           
  39.         mButtonAdd = (Button)rootView.findViewById(R.id.layout_animator_addbutton);  
  40.         mButtonAdd.setOnClickListener(this);  
  41.         mButtonReset = (Button)rootView.findViewById(R.id.layout_animator_resetbutton);  
  42.         mButtonReset.setOnClickListener(this);  
  43.         mGridLayout = (GridLayout)rootView.findViewById(R.id.layout_animator_gridview);  
  44.         mLayoutTransition = new LayoutTransition();  
  45.         //为GridLayout设置mLayoutTransition对象  
  46.         mGridLayout.setLayoutTransition(mLayoutTransition);  
  47.         mLayoutTransition.setStagger(LayoutTransition.CHANGE_APPEARING, 30);  
  48.         mLayoutTransition.setStagger(LayoutTransition.CHANGE_DISAPPEARING, 30);  
  49.         //设置每个动画持续的时间  
  50.         mLayoutTransition.setDuration(300);  
  51.         //初始化自定义的动画效果  
  52.         customLayoutTransition();  
  53.     }  
  54.   
  55.     @Override  
  56.     public void onPause() {  
  57.         // TODO Auto-generated method stub  
  58.         super.onPause();  
  59.           
  60.     }  
  61.   
  62.     @Override  
  63.     public void onClick(View v) {  
  64.         // TODO Auto-generated method stub  
  65.         switch (v.getId()) {  
  66.         case R.id.layout_animator_addbutton:  
  67.             addButton();  
  68.             break;  
  69.         case R.id.layout_animator_resetbutton:  
  70.             resetButton();  
  71.             break;  
  72.         default:  
  73.             break;  
  74.         }  
  75.     }  
  76.       
  77.     /** 
  78.      * 增加Button 
  79.      */  
  80.     public void addButton(){  
  81.         Button mButton = new Button(getActivity());  
  82.         LinearLayout.LayoutParams mLayoutParams = new LinearLayout.LayoutParams(5050);  
  83.         mButton.setLayoutParams(mLayoutParams);  
  84.         mButton.setText(String.valueOf(buttonNumbers++));  
  85.         mButton.setTextColor(Color.rgb(000));  
  86.         if(buttonNumbers % 2 == 1){  
  87.             mButton.setBackgroundColor(Color.rgb(4511887));  
  88.         }else{  
  89.             mButton.setBackgroundColor(Color.rgb(225240));  
  90.         }  
  91.         mButton.setOnClickListener(new OnClickListener() {  
  92.               
  93.             @Override  
  94.             public void onClick(View v) {  
  95.                 // TODO Auto-generated method stub  
  96.                 mGridLayout.removeView(v);  
  97.             }  
  98.         });  
  99.           
  100.         mGridLayout.addView(mButton, Math.min(1, mGridLayout.getChildCount()));  
  101.     }  
  102.       
  103.     /** 
  104.      * 删除所有的Button,重置GridLayout 
  105.      */  
  106.     public void resetButton(){  
  107.         mGridLayout.removeAllViews();  
  108.         buttonNumbers = 1;  
  109.     }  
  110.       
  111.     public void customLayoutTransition(){  
  112.           
  113.         /** 
  114.          * Add Button 
  115.          * LayoutTransition.APPEARING 
  116.          * 增加一个Button时,设置该Button的动画效果 
  117.          */  
  118.         ObjectAnimator mAnimatorAppearing = ObjectAnimator.ofFloat(null"rotationY"90.0f,0.0f)  
  119.                 .setDuration(mLayoutTransition.getDuration(LayoutTransition.APPEARING));  
  120.         //为LayoutTransition设置动画及动画类型  
  121.         mLayoutTransition.setAnimator(LayoutTransition.APPEARING, mAnimatorAppearing);  
  122.         mAnimatorAppearing.addListener(new AnimatorListenerAdapter() {  
  123.             @Override  
  124.             public void onAnimationEnd(Animator animation) {  
  125.                 // TODO Auto-generated method stub  
  126.                 super.onAnimationEnd(animation);  
  127.                  View view = (View) ((ObjectAnimator) animation).getTarget();  
  128.                  view.setRotationY(0.0f);  
  129.             }  
  130.         });  
  131.           
  132.         /** 
  133.          * Add Button 
  134.          * LayoutTransition.CHANGE_APPEARING 
  135.          * 当增加一个Button时,设置其他Button的动画效果 
  136.          */  
  137.           
  138.         PropertyValuesHolder pvhLeft =  
  139.                     PropertyValuesHolder.ofInt("left"01);  
  140.         PropertyValuesHolder pvhTop =  
  141.                 PropertyValuesHolder.ofInt("top"01);  
  142.         PropertyValuesHolder pvhRight =  
  143.                 PropertyValuesHolder.ofInt("right"01);  
  144.         PropertyValuesHolder pvhBottom =  
  145.                 PropertyValuesHolder.ofInt("bottom"01);  
  146.           
  147.         PropertyValuesHolder mHolderScaleX = PropertyValuesHolder.ofFloat("scaleX"1.0f,0.0f,1.0f);  
  148.         PropertyValuesHolder mHolderScaleY = PropertyValuesHolder.ofFloat("scaleY"1.0f,0.0f,1.0f);  
  149.         ObjectAnimator mObjectAnimatorChangeAppearing = ObjectAnimator.ofPropertyValuesHolder(this, pvhLeft,  
  150.                 pvhTop,pvhRight,pvhBottom,mHolderScaleX,mHolderScaleY).setDuration(mLayoutTransition  
  151.                         .getDuration(LayoutTransition.CHANGE_APPEARING));  
  152.         mLayoutTransition.setAnimator(LayoutTransition.CHANGE_APPEARING, mObjectAnimatorChangeAppearing);  
  153.         mObjectAnimatorChangeAppearing.addListener(new AnimatorListenerAdapter() {  
  154.             @Override  
  155.             public void onAnimationEnd(Animator animation) {  
  156.                 // TODO Auto-generated method stub  
  157.                 super.onAnimationEnd(animation);  
  158.                  View view = (View) ((ObjectAnimator) animation).getTarget();  
  159.                  view.setScaleX(1f);  
  160.                  view.setScaleY(1f);  
  161.             }  
  162.         });  
  163.           
  164.         /** 
  165.          * Delete Button 
  166.          * LayoutTransition.DISAPPEARING 
  167.          * 当删除一个Button时,设置该Button的动画效果 
  168.          */  
  169.         ObjectAnimator mObjectAnimatorDisAppearing = ObjectAnimator.ofFloat(null"rotationX"0.0f,90.0f)  
  170.                 .setDuration(mLayoutTransition.getDuration(LayoutTransition.DISAPPEARING));  
  171.         mLayoutTransition.setAnimator(LayoutTransition.DISAPPEARING, mObjectAnimatorDisAppearing);  
  172.         mObjectAnimatorDisAppearing.addListener(new AnimatorListenerAdapter() {  
  173.             @Override  
  174.             public void onAnimationEnd(Animator animation) {  
  175.                 // TODO Auto-generated method stub  
  176.                 super.onAnimationEnd(animation);  
  177.                  View view = (View) ((ObjectAnimator) animation).getTarget();  
  178.                  view.setRotationX(0.0f);  
  179.             }  
  180.         });  
  181.           
  182.         /** 
  183.          * Delete Button 
  184.          * LayoutTransition.CHANGE_DISAPPEARING 
  185.          * 当删除一个Button时,设置其它Button的动画效果 
  186.          */  
  187.         //Keyframe 对象中包含了一个时间/属性值的键值对,用于定义某个时刻的动画状态。  
  188.         Keyframe mKeyframeStart = Keyframe.ofFloat(0.0f, 0.0f);  
  189.         Keyframe mKeyframeMiddle = Keyframe.ofFloat(0.5f, 180.0f);  
  190.         Keyframe mKeyframeEndBefore = Keyframe.ofFloat(0.999f, 360.0f);  
  191.         Keyframe mKeyframeEnd = Keyframe.ofFloat(1.0f, 0.0f);  
  192.           
  193.         PropertyValuesHolder mPropertyValuesHolder = PropertyValuesHolder.ofKeyframe("rotation",   
  194.                 mKeyframeStart,mKeyframeMiddle,mKeyframeEndBefore,mKeyframeEnd);  
  195.         ObjectAnimator mObjectAnimatorChangeDisAppearing = ObjectAnimator.ofPropertyValuesHolder(this, pvhLeft,pvhTop,pvhRight,pvhBottom,mPropertyValuesHolder)  
  196.                 .setDuration(mLayoutTransition.getDuration(LayoutTransition.CHANGE_DISAPPEARING));  
  197.         mLayoutTransition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, mObjectAnimatorChangeDisAppearing);  
  198.         mObjectAnimatorChangeDisAppearing.addListener(new AnimatorListenerAdapter() {  
  199.             @Override  
  200.             public void onAnimationEnd(Animator animation) {  
  201.                 // TODO Auto-generated method stub  
  202.                 super.onAnimationEnd(animation);  
  203.                  View view = (View) ((ObjectAnimator) animation).getTarget();  
  204.                   view.setRotation(0.0f);  
  205.             }  
  206.         });  
  207.     }  
  208. }  
LayoutAnimatorFragment的界面布局fragment_layoutanimator.xml代码如下:
[html]  view plain copy
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:background="@color/view_animation_background"  
  6.     android:orientation="vertical"  
  7.      >  
  8.     <LinearLayout   
  9.         android:layout_width="match_parent"  
  10.         android:layout_height="wrap_content"  
  11.         android:orientation="horizontal"  
  12.         >  
  13.         <Button   
  14.             android:layout_width="0dp"  
  15.             android:layout_weight="1"  
  16.             android:layout_height="wrap_content"  
  17.             android:text="Add Button"  
  18.             android:id="@+id/layout_animator_addbutton"  
  19.             />  
  20.         <Button  
  21.             android:layout_width="0dp"  
  22.             android:layout_weight="1"  
  23.             android:layout_height="wrap_content"  
  24.             android:text="Reset Button"  
  25.             android:id="@+id/layout_animator_resetbutton"   
  26.             />  
  27.     </LinearLayout>  
  28.    <ScrollView   
  29.        android:layout_width="match_parent"  
  30.        android:layout_height="match_parent"  
  31.        >  
  32.        <GridLayout  
  33.         android:layout_width="match_parent"  
  34.         android:layout_height="match_parent"  
  35.         android:columnCount="5"  
  36.         android:animateLayoutChanges="true"   
  37.         android:id="@+id/layout_animator_gridview"  
  38.         />  
  39.    </ScrollView>  
  40.       
  41. </LinearLayout>  
布局界面比较简单,唯一要注意的是GridLayout布局中的android:animateLayoutChanges属性,该属性的值为true或false,true代表当前Layout的布局发生改变时使用动画效果,false则不使用。
最后我们运行程序,看一下运行及操作效果图:

转http://blog.csdn.net/yegongheng/article/details/38455191

---------------------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------------------


---------------------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------------------



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值