转载

Android 属性动画使用解析-属性动画基本用法

ndroid动画的使用前面已经有两篇相关文章了,讲解主要的是View Animation,一篇是 Android补间动画Tween Animation使用解析 ,主要讲解了常见的四种动画淡入淡出、缩放、平移、旋转;另一篇是 Android帧动画Frame Animation使用解析 ,主要用于多张组合图片顺序播放。

本篇文章主要讲解Android另一种动画机制-属性动画Property Animation,属性动画是Android3.0引入了一个新的动画,如果想要在低版本上面使用属性动画,可以使用第三方 NineOldAndroids jar包,属性动画功能相当强大,几乎可以完全替换掉以前所讲的帧动画了。为了有别于前面所讲解的补间动画,补间动画命名是与Animation一起组合命名的,如RotateAnimation、AlphaAnimation、AnimationSet等等,而属性动画命名是与Animator组合命名的,如ValueAnimator、ObjectAnimator、AnimatorSet等等。View Animation已经可以做到许多动画效果了,那么为什么又要引入属性动画呢?

为什么要引入属性动画

View Animation仅仅作用于View,在 Android补间动画Tween Animation使用解析 文章开始就已经介绍了,并且它只对View部分属性有效果,如果我们想要改变一个View的背景色,补间动画是做不到的。如果我们想作用于一个非View该怎么做呢?有些人可能会有疑问了,平常我们看到的一些特效不都是View本身展现出来的吗,为什么会有些作用于非View呢?我们举一个简单的示例,如果我们自定义一个控件,如果我们想让自定义控件在移动过程中动态改变它的色彩,通过画笔Paint实现,前面所讲解的补间动画就做不到了,自定义控件要求对画笔Paint进行渐变色动画处理,背景色不就是画笔Paint的一个color属性吗,只要我们可以操作画笔Paint的这个属性问题就迎刃而解了,属性动画的引入就可以解决这个问题。

补间动画的另外一个缺陷就是它只是改变了View的显示效果而已,而不是真正改变了View的属性,怎么解释呢?比如一个Button,如果Button本身位于屏幕左上角,我们通过补间动画平移到右下角,这时候我们点击右下角所看到的Button是不会触发点击事件的,因为Button本身还在屏幕的左上角,只是补间动画将Button绘制到了右下角而已,该Button所拥有的的所有相关的功能属性还在原来区域。

新引入的属性动画就不仅限于对View操作了,也不仅限于淡入淡出、缩放、平移、旋转这四种效果了,同时通过属性动画操作后也不仅仅是视觉上的效果了。属性动画实际上是对值的操作,并将操作后的属性值赋值给原对象以达到实时渲染的动画效果,它可以操作任意对象的任意属性。属性动画使用起来相当方面,只需要告诉动画执行的时长,执行的动画类型,以及动画的初始值和结束值即可,其余的工作就可以完全交给系统去完成了。上面我们所说的补间动画操作Button后会让人疑惑,属性动画则不会出现这种问题了,它通过改变Button属性将Button移动到了所见区域,而不仅仅是一种视觉上的效果了。

下图是属性动画相关类图:

Android 属性动画使用解析-属性动画基本用法

属性动画的工作机制

下图是一个线性渐变的动画,对于下图的动画,这个对象的X坐标在40ms内从0移动到40 pixel.按默认的10ms刷新一次,这个对象会移动4次,每次移动40/4=10pixel,在40ms时动画结束,该对象位于40px位移处。

Android 属性动画使用解析-属性动画基本用法

对于一个非线性渐变的动画,动画先加速后减速,仍然在40ms内移动40px位置,动画仍然移动4次,动画在前半段加速后半段减速,最后在40ms时仍然位于40px处。

Android 属性动画使用解析-属性动画基本用法

下图展示了属性动画相关类是如何运作的

Android 属性动画使用解析-属性动画基本用法

ValueAnimator即表示一个动画,包含动画的开始值,结束值,持续时间等属性。

ValueAnimator封装了一个TimeInterpolator,TimeInterpolator定义了属性值在开始值与结束值之间的插值方法。

ValueAnimator还封装了一个TypeEvaluator,根据开始、结束值与TimeIniterpolator计算得到的值计算出属性值。

ValueAnimator根据动画已进行的时间跟动画总时间(duration)的比计算出一个时间因子(0~1),然后根据TimeInterpolator计算出另一个插值因子,最后TypeEvaluator通过这个因子计算出属性值,如上例中10ms时:

首先计算出时间因子,即经过的时间百分比:t=10ms/40ms=0.25

经插值计算(inteplator)后的插值因子:大约为0.15,上述例子中用了AccelerateDecelerateInterpolator,计算公式为(input即为时间因子):

(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;

最后根据TypeEvaluator计算出在10ms时的属性值:0.15*(40-0)=6pixel。上例中TypeEvaluator为FloatEvaluator,计算方法为 :

Java

public Float evaluate(float fraction, Number startValue, Number endValue) {         float startFloat = startValue.floatValue();         return startFloat + fraction * (endValue.floatValue() - startFloat);     }
public Float evaluate(float fraction, NumberstartValue, NumberendValue) {         float startFloat = startValue.floatValue();         return startFloat + fraction * (endValue.floatValue() - startFloat);     } 

参数分别为上一步的插值因子,开始值与结束值。

ValueAnimator

ValueAnimator是整个属性动画中最为核心的一个类,前面已经提过属性动画是通过不断对值操作来实现的,它的内部是通过一种时间循环机制来计算动画之间的过度值的。说简单点,ValueAnimator就是一个数值产生器,它本身不作用于任何对象,但是可以对产生的值进行动画处理。

ValueAnimator使用相当简单,代码如下:

Java

ValueAnimator anim = ValueAnimator.ofFloat(0, 1); anim.setDuration(100);  anim.addUpdateListener(new AnimatorUpdateListener() {  @Override  public void onAnimationUpdate(ValueAnimator animation) {   float currentValue = (Float) animation.getAnimatedValue();   Log.i("TAG", "current value:" + currentValue);  } }); anim.start();
ValueAnimatoranim = ValueAnimator.ofFloat(0, 1); anim.setDuration(100);   anim.addUpdateListener(new AnimatorUpdateListener() {  @Override  public void onAnimationUpdate(ValueAnimatoranimation) {  float currentValue = (Float) animation.getAnimatedValue();  Log.i("TAG", "current value:" + currentValue);  } }); anim.start(); 

ValueAnimator是计算动画过程中变化的值,包含动画的开始值,结束值,持续时间等属性。但并没有把这些计算出来的值应用到具体的对象上 面,所以也不会有什么的动画显示出来。要把计算出来的值应用到对象上,必须为ValueAnimator注册一个监听器 ValueAnimator.AnimatorUpdateListener ,该监听器负责更新对象的属性值。在实现这个监听器的时候,可以通过 getAnimatedValue() 的方法来获取当前帧的值,这里我们只是简单的将变化工程中的值通过Log打印了出来。

Android 属性动画使用解析-属性动画基本用法

ofFloat(float... values) 方法 ofFloat传入的参数是不定长参数,这也就意味着我们可以创建更为复杂的动画逻辑。

用于创建ValueAnimator实例最常用的两个方法应该就是ofFloat和ofInt这两个方法了,那么除此之外,我们还可以调用 setStartDelay() 方法来设置动画延迟播放的时间(补间动画中通过 setStartOffset() 设置动画延时播放时间),调用 setRepeatCount()setRepeatMode() 方法来设置动画循环播放的次数以及循环播放的模式(这里API与补间动画一样),循环模式包括RESTART和REVERSE两种,分别表示重新播放和倒序播放的意思。

ObjectAnimator

看名字就知道了,这个类才是我们平常开发过程中最常用到的类,ValueAnimator只是对动画值进行平滑的过度,如果真正设置动画还必须设置一个动画监听器AnimatorUpdateListener。ObjectAnimator可以直接对对象进行动画处理,在上面ValueAnimator的示例中我们在300ms中让值从0到1,这个区间刚好是一个动画透明度的取值区间,下面是一个设置图片渐隐的动画。

Java

ObjectAnimator anim = ObjectAnimator.ofFloat(imageView, "alpha", 1, 0); anim.setDuration(3000); anim.start();
ObjectAnimatoranim = ObjectAnimator.ofFloat(imageView, "alpha", 1, 0); anim.setDuration(3000); anim.start(); 

Android 属性动画使用解析-属性动画基本用法

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

这里简单介绍一下ObjectAnimator的ofFloat方法,第一个参数target就是我们要对那个对象执行动画就传入那个对象,最后一个参数跟ValueAnimator中一样,是一个不定长参数,传入一系列我们所期望的动画值即可,重点在于第二个参数propertyName,有参数名字我们也可以初步了解到要传入一个属性名称,就是第一个参数目标对象的属性,那么问题又来了,究竟可以传入那些属性呢?我们先看一下Android Developers官方的介绍吧

This object should have a public method on it called setName(), where name is the value of the propertyName parameter.

只要target类中有 setName() 方法,那么这个name都可以作为 ObjectAnimator所需要的第二参数值。

变化View的背景色

前面我们说过补间动画不能改变一个View的背景色,属性动画弥补了这一点,那么,我们看看如何使用属性动画来操作背景色。

Java

ObjectAnimator anim = ObjectAnimator.ofObject(colorView, "backgroundColor", new ArgbEvaluator(), Color.BLUE, Color.RED); anim.setDuration(2000); anim.start();
ObjectAnimatoranim = ObjectAnimator.ofObject(colorView, "backgroundColor", new ArgbEvaluator(), Color.BLUE, Color.RED); anim.setDuration(2000); anim.start(); 

这里我们使用了ofObject方法,ofObject方法如下:

public static ObjectAnimator ofObject(Object target, String propertyName,TypeEvaluator evaluator, Object… values)

与上面所介绍的ofFloat参数相似,但是多了一个参数TypeEvaluator,TypeEvaluator我们在下一篇文章中着重介绍,这里不做过多的讲解了,只需要记住它根据动画执行时间实时计算出动画属性的当前值,前文属性动画运行机制部分已经介绍了一个简单的计算示例。

Android 属性动画使用解析-属性动画基本用法

垂直屏幕方向翻转

注意这里说的是翻转,补间动画我们做的都是平行于屏幕方向旋转,这里我们使用属性动画进行垂直翻转,代码如下:

Java

ObjectAnimator anim = ObjectAnimator.ofFloat(imageView, "rotationY", 0.0f, 180.0f); anim.setDuration(2000); anim.start();
ObjectAnimatoranim = ObjectAnimator.ofFloat(imageView, "rotationY", 0.0f, 180.0f); anim.setDuration(2000); anim.start(); 

属性动画使用是不是很方面,而且效果也很炫,翻转效果如下:

Android 属性动画使用解析-属性动画基本用法

组合动画

这里组合动画与补间动画中的组合动画差不多,单个动画实现的效果毕竟是有限的,所以Google官方为我们提供了组合动画,实现组合动画功能主要借助AnimatorSet类,借助该类提供的 play() 方法,我们传入一个Animator对象,这时候会返回一个AnimatorSet.Build的实例,该实例包含了以下四个方法:

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

下面是一个简单的示例:

Java

ObjectAnimator anim1 = ObjectAnimator.ofFloat(imageView, "x", 0f, 400.0f); ObjectAnimator anim2 = ObjectAnimator.ofFloat(imageView, "y", 0f, 400.0f); ObjectAnimator anim3 = ObjectAnimator.ofFloat(imageView, "x", 400.0f, 0f); ObjectAnimator anim4 = ObjectAnimator.ofFloat(imageView, "y", 400.0f, 0f);  AnimatorSet animSet = new AnimatorSet(); animSet.play(anim1).before(anim2); animSet.play(anim3).after(anim2); animSet.play(anim3).with(anim4); animSet.play(anim1).after(100); animSet.setDuration(800); animSet.start();
ObjectAnimatoranim1 = ObjectAnimator.ofFloat(imageView, "x", 0f, 400.0f); ObjectAnimatoranim2 = ObjectAnimator.ofFloat(imageView, "y", 0f, 400.0f); ObjectAnimatoranim3 = ObjectAnimator.ofFloat(imageView, "x", 400.0f, 0f); ObjectAnimatoranim4 = ObjectAnimator.ofFloat(imageView, "y", 400.0f, 0f);   AnimatorSetanimSet = new AnimatorSet(); animSet.play(anim1).before(anim2); animSet.play(anim3).after(anim2); animSet.play(anim3).with(anim4); animSet.play(anim1).after(100); animSet.setDuration(800); animSet.start(); 

上面一个组合动画表示延时100ms后先播放动画anim1,然后是动画anim2,再者是动画anim3和anim4同时播放,形成一个直角路径。

Android 属性动画使用解析-属性动画基本用法

使用XML编写动画

与补间动画一样,属性动画同样可以使用XML来编写动画,而且在项目中使用这种方式使得代码结构更清晰。

使用XML编写动画,我们需要在 res/animator/ 目录下面编写动画文件,属性动画在XML中使用三种标签:

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

从这里我们可以看出除了set标签我们在补间动画见到之外,其余两个标签都是新增的标签。XML动画文件的存放位置,到目前为止我们应该遇见三中不同情况了,补间动画存放在res/anim/目录下面,帧动画一般我们存在res/drawabl/目录下面,今天所介绍的属性动画是存放在res/animato/目录下,三种不同风格动画存放在三种不同目录下面。

在ValueAnimator开始时举的一个示例,我们XML表示如下:

XHTML

<animator  xmlns:android="http://schemas.android.com/apk/res/android"  android:propertyName="alpha"  android:valueFrom="0"  android:duration="100"  android:valueTo="1"  android:valueType="floatType" />
<animator  xmlns:android="http://schemas.android.com/apk/res/android"  android:propertyName="alpha"  android:valueFrom="0"  android:duration="100"  android:valueTo="1"  android:valueType="floatType"/> 

如果我们将一个动画平滑旋转360度,XML动画文件表示如下:

XHTML

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"     android:duration="1000"     android:propertyName="rotationY"     android:valueFrom="0"     android:valueTo="360"     android:valueType="floatType" > </objectAnimator>
<objectAnimatorxmlns:android="http://schemas.android.com/apk/res/android"     android:duration="1000"     android:propertyName="rotationY"     android:valueFrom="0"     android:valueTo="360"     android:valueType="floatType"> </objectAnimator> 

下面是一个组合动画,在旋转360的同时渐隐,XML表示如下:

XHTML

<set xmlns:android="http://schemas.android.com/apk/res/android"     android:ordering="together" >      <objectAnimator         android:duration="1000"         android:propertyName="rotationY"         android:valueFrom="0"         android:valueTo="360"         android:valueType="floatType" >     </objectAnimator>     <objectAnimator         android:duration="1000"         android:propertyName="alpha"         android:valueFrom="1.0"         android:valueTo="0"         android:valueType="floatType" >     </objectAnimator>  </set>
<setxmlns:android="http://schemas.android.com/apk/res/android"     android:ordering="together">       <objectAnimator         android:duration="1000"         android:propertyName="rotationY"         android:valueFrom="0"         android:valueTo="360"         android:valueType="floatType">     </objectAnimator>     <objectAnimator         android:duration="1000"         android:propertyName="alpha"         android:valueFrom="1.0"         android:valueTo="0"         android:valueType="floatType">     </objectAnimator>   </set> 

XML编写完成了,最后我们如何下代码中调用呢?代码如下:

Java

Animator anim = AnimatorInflater.loadAnimator(this, R.animator.flip_on_vertical); anim.setTarget(imageView); anim.start();
Animatoranim = AnimatorInflater.loadAnimator(this, R.animator.flip_on_vertical); anim.setTarget(imageView); anim.start(); 

这里的代码调用方式与补间动画调用还是很类似的,补间动画是借助于AnimationUtils,使用 loadAnimation() 方法,在启动方式上面也有不同,直接上代码吧,如下:

Java

Animation anim = AnimationUtils.loadAnimation(this, R.anim.translate_animate01); imageView.startAnimation(anim);
Animationanim = AnimationUtils.loadAnimation(this, R.anim.translate_animate01); imageView.startAnimation(anim); 

结束语

本文着重介绍了属性动画的基本使用,动画的监听还有动画执行过程中各个状态的表示都没有介绍,在接下来的文章中再继续探讨,另外对于TypeEvaluator和插值器Interpolator的使用也想抽些时间深入一下,我们知道属性动画不再仅限于对View进行操作了,但是平常开发过程中最多的还是操作View,所以Google针对View提供一个专门的类插值器Interpolator的使用用于View的操作,到时对比其他方式动画简单的介绍一下。

原文  http://www.sunnyang.com/386.html
正文到此结束
Loading...