当前位置: 代码迷 >> Android >> Android学习之 属性动画<Property Animation>
  详细解决方案

Android学习之 属性动画<Property Animation>

热度:188   发布时间:2016-04-28 03:26:04.0
Android学习之 属性动画<Property Animation>
    property 动画系统是相当健壮的框架,它几乎可以动画显示任何对象。 你可以定义一个动画来定时改变任何对象的属性值,不论该对象是否在屏幕上显示。 property 动画将以一定的时间间隔修改属性值(对象中的字段值)。 要实现动画显示,你须指定对象的相应属性(比如对象的屏幕位置),以及动画时长、动画时间间隔。

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

一、Property 动画的工作方式
    首先,我们通过一个简单例子来回顾一下动画的工作原理。图 1 表明了某对象的 x 属性变化情况,即在屏幕上水平的位置。 动画的持续时间设为 40 ms,移动的距离是 40 个像素点。每隔 10 ms,这是默认的帧刷新率,此对象横向移动 10 个像素点。 在 40 ms 到期后,动画停止,此对象位置横移 40 个像素点。以下是采用线性插值的示例,也即此对象以固定的速度移动。
 
                                          图 1. 线性动画的示例
    你还可以把动画设置为非线性插值方式。图 2 表示,某对象开始时加速移动,结束时减速移动。 此对象仍然是在 40 ms 内移动 40 个像素点,但是速度是非线性变化的。在开始时,此动画加速移动至中间位置,然后再减速移动至终点。 如图 2 所示,开始和结束阶段移动的距离比中间位置要少一些。
 
                                          图 2. 非线性动画示例
让我们来仔细查看一下 property 动画系统的关键部件在上述动画中的计算过程。图 3 展示了主要类的相互工作方式。
 
                                            图 3. 动画的计算过程
1、ValueAnimator 对象记录了动画自身的一些值,比如已经显示了多长时间、动画对应属性的当前值。
2、ValueAnimator 中封装了一个定义动画变化方式的 TimeInterpolator ,以及定义了属性计算方式的 TypeEvaluator 。比如,图 2 中的 TimeInterpolator 应该使用 AccelerateDecelerateInterpolator , TypeEvaluator 应该使用 IntEvaluator。要启动动画,请创建一个 ValueAnimator 并指定要动画显示的属性的初始值和结束值。调用 start() 启动动画。在整个动画过程中, ValueAnimator 根据动画总时间和已进行的时间和计算出一个时间比例因子(elapsed fraction),大小介于0和1之间。 时间比例因子代表动画已完成时间的百分比,0 表示 0%,1 表示 100%。例如,在图 1 中,t = 10 ms 时的时间比例因子应该是 0.25,因为总时间 t = 40 ms。
3、ValueAnimator 算完时间比例因子后,将调用已设置好的 TimeInterpolator 计算出一个插值因子(interpolated fraction)。插值因子是一个由时间比例因子换算出来的图像显示状态因子。 比如,在图 2 中,t = 10 ms 时,因为动画的加速度较小,插值因子约是 0.15,它小于时间比例因子 0.25。 在图 1 中,插值因子一直保持不变,并与时间比例因子一致。算完插值因子, ValueAnimator 就会调用合适的 TypeEvaluator ,根据插值因子、初始值、结束值计算出需要动画显示的属性值。 比如,在图 2 中,t = 10 ms 时的插值因子是 0.15,所以此时的属性值应该是 0.15 X (40 - 0),也即为 6。

API 概述
你可以在 android.animation 中找到大部分 property 动画系统的 API。 由于 view 动画系统已经在 android.view.animation 中定义了很多 interpolator ,你可以在 property 动画系统中直接使用它们。 下表对 property 动画系统中的主要内容进行了说明。
Animator 类只提供了用于创建动画的基础构造方法。它只提供了最基本的功能,你必须扩展这些功能才能完成对动画的支持,因此通常你不应该直接使用该类。 以下子类就对 Animator 进行了扩展:
表 1. Animator


evaluator 用于告知 property 动画系统给定属性值的计算方式。根据 Animator 类给出的计时数据、动画初始值和结束值,它将计算出需动画显示的属性值。property 动画系统提供了以下 evaluator:
表 2. Evaluator


时间 interpolator 定义了动画显示的计算方式,它是一个关于时间的函数。 比如,你可以指定线性播放动画,也即全程匀速播放。或者,你也可以指定为非线性播放,比如一开始加速,而临近结束时减速。 表 3 列出了 android.view.animation 中包含的 Interpolator。 如果已有的 interpolator 无法满足需求,你可以实现 TimeInterpolator 接口并创建你自己的 interpolator 。 关于编写自定义 interpolator 的方法。
表 3. Interpolator


二、用ValueAnimator实现动画
通过指定一组int、float、颜色值, ValueAnimator 类能让你在动画过程中修改某些类型的值。通过调用其工厂方法(factory method): ofInt()、 ofFloat()、 ofObject(), 你可以获得一个 ValueAnimator 实例。比如:
ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);animation.setDuration(1000);animation.start();
在这段代码中, ValueAnimator 先设置动画值为0-1之间、持续 1000 ms、并执行start()方法。
你也可以按照以下格式指定自定义类型的动画值:

ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);animation.setDuration(1000);animation.start();

在这段代码中, ValueAnimator 先计算动画值为startPropertyValue 至 endPropertyValue之间,应用 MyTypeEvaluator,持续时间 1000 ms,并执行 start()。


其实这段代码对某对象是无法实现动画效果的,因为 ValueAnimator 还没有直接控制对象或其属性值。你最有可能要做的事就是用计算出来的属性值修改动画对象。 通过在 ValueAnimator 中定义侦听器,你就可以实现这一点。侦听器可以实现动画过程中的事件处理,比如帧的刷新。 在侦听器的实现代码中,你可以调用 getAnimatedValue() 获取为本次画面刷新计算出来的属性值。

三、用ObjectAnimator实现动画
ObjectAnimator 是 ValueAnimator的子类,它是由计时引擎和计算目标对象的属性值 ValueAnimator 组合而成的。因为属性值将会自动更新,你不再需要实现 ValueAnimator.AnimatorUpdateListener了,因此实现任意对象的动画显示就更加容易了。
ObjectAnimator 的示例与 ValueAnimator 类似,只是你还需要在动画区间值之前额外指定对象和属性名称(字符串格式):
ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);anim.setDuration(1000);anim.start();
为了保证 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中设定的起始和结束值相同。比如,如果你建立了以下ObjectAnimator ,那么就必须有targetObject.setPropName(float) 和 targetObject.getPropName(float):

ObjectAnimator.ofFloat(targetObject, "propName", 1f)
4、根据不同的动画显示对象和属性,也许你需要调用 View 的invalidate()方法,以便用新的属性值强制重绘屏幕。你应该在onAnimationUpdate() 回调方法中进行刷新操作。比如,要动画显示 Drawable 对象的颜色属性,就只有通过重绘才能在屏幕上显示出变化。View 中所有的属性 setter 方法,比如setAlpha()和setTranslationX(),都会适时地禁用 View,因此你不需要在用新属性值调用这些方法时禁用 View。

四、用AnimatorSet编排多个动画
很多时候,你需要在一个动画的开始和结束时播放另一个动画。Android 系统可以让你把多个动画组合为一个 AnimatorSet ,这样你就可以设定同时播放、顺序播放或延迟一段时间后再播放。 你还可以嵌套多个 AnimatorSet 对象。
如下面范例,将用以下规则播放 Animator 对象:
1、播放 bounceAnim。
2、同时播放 squashAnim1、squashAnim2、stretchAnim1 和 stretchAnim2。
3、播放 bounceBackAnim。
4、播放 fadeAnim。

AnimatorSet bouncer = new AnimatorSet();bouncer.play(bounceAnim).before(squashAnim1);bouncer.play(squashAnim1).with(squashAnim2);bouncer.play(squashAnim1).with(stretchAnim1);bouncer.play(squashAnim1).with(stretchAnim2);bouncer.play(bounceBackAnim).after(stretchAnim2);ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);fadeAnim.setDuration(250);AnimatorSet animatorSet = new AnimatorSet();animatorSet.play(bouncer).before(fadeAnim);animatorSet.start();

五、动画侦听器
在播放过程中,你可以用下列侦听器来监听那些重要的动画事件。
Animator.AnimatorListener
  onAnimationStart() —— 动画开始时调用。
  onAnimationEnd() —— 动画结束时调用。
  onAnimationRepeat() —— 动画循环播放时调用。
  onAnimationCancel() —— 动画被取消时调用。不管终止的方式如何,被取消的动画仍然会调用onAnimationEnd()。
ValueAnimator.AnimatorUpdateListener
  onAnimationUpdate() —— 动画每播放一帧时调用。在动画过程中,可侦听此事件来获取并使用ValueAnimator计算出来的属性值。利用传入事件的ValueAnimator对象,调用其getAnimatedValue()方法即可获取当前的属性值。如果你使用ValueAnimator,则必需实现此侦听器。
根据不同的动画对象及其属性,你也许需要调用 View 的invalidate()方法,强制用新的属性值重绘屏幕区域。比如,要动画显示 Drawable 对象的颜色属性,只有重绘才能在屏幕上显示出变化。View 中所有的属性 setter 方法,比如setAlpha()和setTranslationX(),都会适时地禁用 View,因此你不需要在用新属性值调用这些方法时禁用 View。
如果你不愿意实现 Animator.AnimatorListener 中所有的方法,那你可以不用此侦听器,而是扩展 AnimatorListenerAdapter 类。 AnimatorListenerAdapter 类为所有方法都实现了空方法体,你自行选择并覆盖即可。
例如 下面范例 创建了一个只带有 onAnimationEnd() 回调方法的 AnimatorListenerAdapter

ValueAnimatorAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);fadeAnim.setDuration(250);fadeAnim.addListener(new AnimatorListenerAdapter() {public void onAnimationEnd(Animator animation) {    balls.remove(((ObjectAnimator)animation).getTarget());}

六、ViewGroup中Layout的动画
与 View 动画一样简单, property 动画系统对 ViewGroup 对象的动画显示提供了支持。
你可以用 LayoutTransition 类来动画显示 ViewGroup 中的 Layout。 通过把 View 加入或移出 ViewGroup ,或者以 VISIBLE、 INVISIBLE、 GONE 调用 View 的 setVisibility() 方法,可以实现某个 View 的显现和消失效果。 当你添加或删除 View 时,ViewGroup 中的其它 View 也可以用动画的方式移到新的位置显示。 你可以调用 LayoutTransition 对象的 setAnimator() 方法来定义下列动画方式,调用参数是 Animator 对象和以下 LayoutTransition 常量:
APPEARING —— 元素在容器中显现时需要动画显示。
CHANGE_APPEARING —— 由于容器中要显现一个新的元素,其它元素的变化需要动画显示。
DISAPPEARING —— 元素在容器中消失时需要动画显示。
CHANGE_DISAPPEARING —— 由于容器中某个元素要消失,其它元素的变化需要动画显示。
你可以为这四种事件定义自己的动画方式,以便定制 layout 变化时的外观,也可以只是通知动画系统采用默认的动画方式。
范例代码:
1、需要把 ViewGroup 的 android:animateLayoutchanges 属性设为 true。 比如:

<LinearLayout    android:orientation="vertical"    android:layout_width="wrap_content"    android:layout_height="match_parent"    android:id="@+id/verticalContainer"    android:animateLayoutChanges="true" />
此属性设为 true 将会自动把 View 加入和移出 ViewGroup 的过程以动画方式显示,ViewGroup 中其它的 View 同时也会以动画方式进行调整。

2、为ViewGroup设置Layout的改变动画

LayoutTransition mTransition = new LayoutTransition();mTransition.setAnimator(                  LayoutTransition.APPEARING,                  ObjectAnimator.ofFloat(this, "scaleX", 0, 1)); mGridLayout.setLayoutTransition(mTransition);  

PS:更详细的案例可以参考 鸿洋大神的一篇博客:

http://blog.csdn.net/lmj623565791/article/details/38092093


七、使用TypeEvaluator
如果 Android 系统无法识别需要动画显示的属性值类型,你可以创建自己的 evaluator,实现 TypeEvaluator 即可。Android 系统可识别的类型包括int、float和颜色,分别由 IntEvaluator、 FloatEvaluator、 ArgbEvaluator 提供支持。
TypeEvaluator接口中只要实现一个方法即可,就是 evaluate()。此方法返回一个当前动画时刻的属性值。FloatEvaluator类展示了这一过程:
public class FloatEvaluator implements TypeEvaluator {    public Object evaluate(float fraction, Object startValue, Object endValue) {        float startFloat = ((Number) startValue).floatValue();        return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);    }}
注意:
当ValueAnimator (或 ObjectAnimator )运行时,它会先算出当前已显示的动画进程比例(介于 0 和 1 之间),然后再根据所用的 interpolator 计算出插值因子。 TypeEvaluator 接收到的 fraction 参数本身就是此插值因子,因此你在计算动画属性值时就不必用到 interpolator 了。

八、使用Interpolator
插值器(interpolator)定义了动画过程中属性值的变化规则,它是一个关于时间的函数。 比如,你可以把动画过程设定为线性变化,这意味着它全程都是匀速变化。 或者,你可以设定动画为非线性变化,比如在开始或结束时加速或减速。
动画系统中的 interpolator 会从 Animator 中接收到一个时间比例因子,此因子代表了动画已显示的时间。 interpolator 会根据动画的类型修改此因子。Android 系统在 android.view.animation 包中提供了一组通用的 interpolator。如果这些都不满足需要,你可以实现 TimeInterpolator 接口来创建自己的 interpolator。
作为示例,缺省的 AccelerateDecelerateInterpolator 和 LinearInterpolator interpolator 将会以下列方式计算插值因子:
AccelerateDecelerateInterpolator

public float getInterpolation(float input) {    return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;}
LinearInterpolator
public float getInterpolation(float input) {    return input;}

九、设定关键帧
Keyframe 对象中包含了一个时间/属性值的键值对,用于定义某个时刻的动画状态。 每个关键帧还可以拥有自己的 interpolator,用于控制前一关键帧至本关键帧之间的动画行为。
要实例化一个 Keyframe 对象,你必须使用它的工厂方法 ofInt()、 ofFloat()、 ofObject() 之一,或者获得相应类型的 Keyframe 。然后,调用工厂方法 ofKeyframe() 获得一个 PropertyValuesHolder 对象。一旦有了这个对象,你就可以把它和需动画显示的对象作为参数得到一个 animator。以下代码段展示了这一过程:

Keyframe kf0 = Keyframe.ofFloat(0f, 0f);Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);Keyframe kf2 = Keyframe.ofFloat(1f, 0f);PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation)rotationAnim.setDuration(5000ms);rotationAnim.start();

十、动画显示View
property 动画系统可以让 View 对象进行一系列的动画显示,相比 view 动画系统具有更多优势。 view 动画系统通过改变 View 对象的绘制方式来实现动画效果。 因为 View 本身没有给出属性以供控制,所以这是由 View 所在容器来完成处理的。 虽然这样能实现 View 的动画效果,但 View 对象本身并没有变化。 因此会出现这种情况:虽然屏幕上的显示位置已经移动过了,但对象实际仍然停留在原来的位置。 为了消除这一弊病,在 Android 3.0 中给 View 增加了一些新的属性以及相应的 getter、setter 方法。
1> property 动画系统可以通过修改 View 对象实际的属性值来实现屏幕上的动画效果。此外,当属性值发生变化时,Views 也会自动调用     invalidate()方法来刷新屏幕。 View 类中新增的便于实现 property 动画的属性包括:
2> translationX 和 translationY:这两个属性控制着 View 的屏幕位置坐标变化量,以 layout 容器的左上角为坐标原点。
3> rotation、rotationX 和 rotationY:这三个属性控制着 2D 旋转角度(rotation属性)和围绕某枢轴点的 3D 旋转角度。
4> scaleX、scaleY:这两个属性控制着 View 围绕某枢轴点的 2D 缩放比例。
5> pivotX 和 pivotY: 这两个属性控制着枢轴点的位置,前述的旋转和缩放都是以此点为中心展开的。缺省的枢轴点是 View 对象的中心点。
   注意: 1、通过调用mView.setPivotX() 和 mView.setPivotY()来设置某View的旋转/缩放枢轴点坐标。
            2、此时的原点坐标(0,0)为该旋转/缩放View的左上角,而不再以手机屏幕左上角为x、y轴坐标原点。

6> x 和 y:这是指 View 在容器内的最终位置,等于 View 左上角相对于容器的坐标加上 translationX 和 translationY 后的值。
7> alpha:表示 View 的 alpha 透明度。缺省值为 1 (不透明),为 0 则表示完全透明(看不见)。
要动画显示 View 对象的某个属性,比如颜色或旋转值,你所有要做的事情就是创建一个 property animator,并设定对应的 View 属性。比如:

ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);

十一、用ViewPropertyAnimator实现动画
利用一个基础的 Animator 对象, ViewPropertyAnimator 为同时动画显示 View 的多个属性提供了一种捷径。这种方式与一个 ObjectAnimator 很类似,因为也会修改 View 的实际属性值,但在需要一次动画显示多个属性时效率会更高一些。 此外,使用 ViewPropertyAnimator 的代码会简洁很多,可读性更好。以下代码段展示了分别使用多个 ObjectAnimator 对象、单个 ObjectAnimator 对象、 ViewPropertyAnimator 对象实现 view 的 x 和 y 属性的同时变化。
多个 ObjectAnimator 对象

ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);AnimatorSet animSetXY = new AnimatorSet();animSetXY.playTogether(animX, animY);animSetXY.start();
单个 ObjectAnimator
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();ViewPropertyAnimator
ViewPropertyAnimator
myView.animate().x(50f).y(100f);

十二、在XML中声明动画
property 动画系统允许你用 XML 声明 property 动画,而不需要编写代码来实现。 通过 XML 定义的方式,你可以方便地在多个 activity 中复用动画资源,并且更容易编排动画顺序。
为了把采用新增 property 动画 API与采用以前 view animation 框架的动画文件区分开来,自 Android 3.1 开始,你应该把 property 动画 XML 文件保存到res/animator/目录下(而不是res/anim/目录)。 目录名animator是可以修改的,但如果你要用 Eclipse ADT(ADT 11.0.0+) 插件作为 layout 编辑工具,那就不能动了。 因为 ADT 只会搜索res/animator/目录下的动画资源。
以下列出了可用 XML 标记声明的 property 动画类:
ValueAnimator - <animator>
ObjectAnimator - <objectAnimator>

AnimatorSet - <set>


以下例子顺序播放两组动画,第一组动画内嵌入了两个对象的动画:
<set android:ordering="sequentially">    <set>        <objectAnimator            android:propertyName="x"            android:duration="500"            android:valueTo="400"            android:valueType="intType"/>        <objectAnimator            android:propertyName="y"            android:duration="500"            android:valueTo="300"            android:valueType="intType"/>    </set>    <objectAnimator        android:propertyName="alpha"        android:duration="500"        android:valueTo="1f"/></set>
为了播放这个动画,你必须用代码把 XML 资源置入 AnimatorSet 中,然后设置动画的所有目标对象,再开始动画。用 setTarget() 可以很方便地为 AnimatorSet 下所有子元素设置一个目标对象。以下代码展示了这种设置方法:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,    R.anim.property_animator);set.setTarget(myObject);set.start();


备注:该篇内容转载于:http://blog.sina.com.cn/s/blog_48d4913001011a1u.html [呆呆大虾:API翻译系列]

英文原文:http://developer.android.com/guide/topics/graphics/prop-animation.html




  相关解决方案