Android 的动画 II-Property Animation

来源:互联网 发布:淘宝价格字体 编辑:程序博客网 时间:2024/06/17 08:28

概述:

Property Animation是一个很强大的框架, 可以让我们为任何东西指定动画. 我们可以定义一个动画改变任何对象的属性, 不管它是不是显示在屏幕上的. Property animation可以改变在一段指定的时间里改变一个对象的属性值. 想要让某些东西动起来, 我们需要指定想要修改的对象属性, 比如对象在屏幕上的位置, 想要动多长时间, 以及移动的位移.

我们可以通过property animation定义动画的下列的特性:

1.      Duration: 持续时间, 我们可以指定动画的持续时间,默认长度是300ms.

2.      Time interpolation: 我们可以指定一个方法, 用来指定属性值和当前运行时间的关系.

3.      重复的次数和行为: 我们可以指定一个动画是否重复和重复多少次. 还可以指定是否播放相反的动画. 也就是反向播放.

4.      Animator sets: 动画集, 我们可以为动画分为逻辑组,让他们一起播放或者顺序播放, 还可以在指定延迟之后播放.

5.      Frame refresh delay: 帧刷新延迟. 我们可以指定每帧动画的刷新时间, 默认刷新时间是10ms(每秒100帧), 但是是否能达到这样的速度还取决于系统的繁忙程度和系统计时器, 如果系统太卡刷新不过来, 那么就无法达到这样的帧率.

Propertyanimation是如何工作的:

首先, 通过一个简单的栗子来看一下一个动画是如何工作的. 


上图描述了一个带有x属性的假想的对象, 它表示在屏幕上的横坐标. 动画的持续时间被设置为40ms, 坐标移动了40pixels. 每过10ms, 也就是默认刷新一次的时间, 该对象横向移动10pixels. 在40ms之后, 动画结束, 该对象停止在了横向坐标为40的地方. 这是一个线性的距离与时间的关系, 这意味着该对象是匀速运动的. 我们还可以将这种关系指定为非线性的, 下图展示了一个假想的对象, 它在开始的时候加速移动, 在结束的时候减速.


该对象同样是40ms移动40pixels, 但是是非线性的. 开始的时候, 动画加速移动直到中间的点, 然后开始减速, 直到动画结束. 现在让我们来看一下Android是如何计算这些property animation的过程的. 下图描述了主要的类之间是如何工作的:


ValueAnimator对象跟踪动画的计时器, 比如动画已经运行了多久和当前正在改变的属性值. 该类封装了TimeInterpolator接口, 而TimeInterpolator定义了动画的时间和改变的属性的关系, ValueAnimator中还封装了TypeEvaluator, 它定义了如何计算改变的属性的值. 比如在上面的非线性变化的图中, TimeInterpolator的实例是AccelerateDecelerateInterpolator, 而TypeEvaluator的实例是IntEvaluator.

想要开始一段动画, 那么首先要创建一个ValueAnimator, 然后为它指定一个想要改变的属性的起始和结束的值. 当我们调用start()方法的时候, 动画就开始执行了. 在整个动画的过程中, ValueAnimator计算出一个0~1之间的分数值来表明动画的进度(elapsed fraction). 这个值表示动画经过时间的百分比, 0表示0%, 1表示100%. 比如前面的栗子里在t=10ms的时候这个值为0.25, 因为动画的完整时间为40ms.

PropertyAnimation与ViewAnimation的区别

View animation值能应用于View对象, 所以如果我们想要让一个非View对象动起来, 就必须得在自己代码里实现.View animation包含的动画种类也比较少, 比如View的缩放和旋转, 但是比如背景色改变这种就不能支持. 它还有一个劣势, 就是只能在View被绘制出来之后才能动起来, 而不是针对View本身. 栗如如果我们让一个button横向穿过屏幕, button可以正确的绘制, 但是实际上我们可以点击的位置还在以前的位置, 我们必须要用自己的逻辑来实现点击事件的改变才行.

如果使用property animation的话, 完全不用考虑上面的约束, 我们可以修改一个对象的任意属性, 也不必关注这个对象是不是View, 并且这个对象的属性确实是被修改了的.

View animation设置相对简单, 代码较少. 如果view animation可以满足我们的需求或者代码里已经有了可以顺利工作的view animation, 那么也不必修改它们. 有些时候我们可能需要同时使用View animation和property animation. 

Propertyanimation的API介绍:

我们可以在android:animation中找到大多数的propertyanimation的API. 下面的表哥描述了property animation的主要组件.

Animator类提供了创建动画的基础结构. 我们通常不用这个类直接来创建动画, 因为它只提供了最基础的功能, 必须扩展才能完整的支持动画, 下面是几个扩展自Animator类的类:

描述

ValueAnimator

Property animation主要的时间引擎, 还负责计算需要修改的属性的值. 它有所有的计算动画值的核心功能, 并且包含每种动画的时间详细数据, 决定动画是否重复, 还有接收更新的监听器和设置. 它有两个工作:计算要修改的属性值和将这些值设置给需要的对象. ValueAnimator本身不执行第二个功能, 所以我们需要通过ValueAnimator来监听需要修改的值, 然后通过我们自己的代码来修改对象的属性.

ObjectAnimator

ObjectAnimation是ValueAnimator的子类, 它允许我们可以为动画设置一个目标对象和对象的属性. 这个类在为动画计算出一个新的值之后更新属性的值. 在大多数时候我们会使用这个类, 因为它使得目标对象的动画处理流程更加简单. 然而有时候我们还是需要使用ValueAnimator, 因为ObjectAnimator有时候存在一些限制, 比如当我们想要访问目标对象的指定方法的时候.

AnimatorSet

该类为动画提供了一个分组的机制, 让它们可以跟另一个动画产生关联. 我们可以设置它们一起执行动画, 或者线性执行, 在一个固定延迟之后执行等.

Evaluator告诉property animation对于一个给定的属性如何计算它的值. 它们取得一个Animator提供的时间数据, 知道动画开始和结束的时间, 然后基于这些数据计算需要改变的属性的值. Property animation为我们提供了如下的evaluator:

类/接口

描述

IntEvaluator

用于计算整型属性的默认Evaluator.

FloatEvaluator

用于计算浮点型属性的默认Evaluator.

ArgbEvaluator

用于计算十六进制颜色属性的默认Evaluator.

TypeEvaluator

一个接口, 允许我们创建我们自己的Evaluator. 如果我们要修改的对象的属性值不是一个整型,浮点型或颜色值, 我们必须实现TypeEvaluator接口来指定这个属性的值该如何计算. 当我们想要实现某些特殊的行为的时候, 我们也同样可以用TypeEvaluator来实现整型浮点型和颜色的计算.

Interpolator, 定义了动画中某个指定的值随时间变化的函数. 比如我们可以指定动画在整个过程中线性发生, 也就是匀速的; 也可以指定动画非线性的, 比如开始加速之后减速. 下表介绍了android.view.animation中的interpolator. 如果没有哪个适合我们, 那么我们可以实现TimeInterpolator接口来创建自定义的Interpolator.

类/接口

描述

AccelerateDecelerateInterpolator

一个Interpolator指定动画的速度开始和结束的时候慢, 但是加速通过中间部分.

AccelerateInterpolator

一个Interpolator指定动画的速度启动的时候慢, 但是一直加速.

AnticipateInterpolator

指定动画开始的时候先向反方向然后再向指定方向移动.

AnticipateOvershootInterpolator

指定动画开始的时候先向反方向移动, 然后向指定方向移动并超过终点一点, 最后再回到终点

BounceInterpolator

在结束的时候会有回弹效果, 最后停在终点.

CycleInterpolator

动画会重复几次.

DecelerateInterpolator

开始很快然后减速

LinearInterpolator

匀速变化

OvershootInterpolator

向指定方向改变, 超过终点一段, 然后再回到终点.

TimeInterpolator

自定义.

使用ValueAnimator实现动画:

ValueAnimation让我们可以在一段时间里修改某个对象的int, float和颜色的属性. 我们可以通过ValueAnimator的工厂方法获得它的对象, 比如ofInt(), ofFloat(), ofObject(). 栗子:

ValueAnimator animation = ValueAnimator.ofFloat(0f,1f);
animation.setDuration(1000);
animation.start();

上面代码中, ValueAnimator在调用start()方法之后开始动画的值, 从0~1, 时间是1000ms. 我们还可以这样指定一个自定义的类型:

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

在这段代码里, ValueAnimator开始计算动画值, 区间是startPropertyValue~endPropertyValue, 时间1000ms, 在start()方法之后开始.

上述的代码片段并不会实际的影响某个对象, 因为ValueAnimator没有指定一个对象或者是一个对象的属性. 我们通常要做的事情是通过这些计算的值来修改对象中的某些属性. 我们可以通过在ValueAnimator中定义监听器来处理动画生命周期中的重要事件, 比如帧更新. 实现了监听器之后我们就可以通过getAnimatedValue()方法获得变化的计算值来刷新指定的帧. 

用ObjectAnimator实现动画:

ObjectAnimator类是ValueAnimator的子类. 并可以结合定时器引擎和计算值以及动画的目标对象属性. 这样就使得针对对象的动画变得更加简单, 因为我们不再需要实现ValueAnimator.AnimatorUpdateListener, 它们已经可以自动更新了. 实例化一个ObjectAnimator的过程和ValueAnimator是类似的, 但是我们需要指定对象和对象的属性名字(用string), 栗子:

ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);anim.setDuration(1000);anim.start();

想要让ObjectAnimator正确的更新属性, 我们必须这样做:

1.      目标对象属性必须有一个setter方法, 格式是set<propertyName>(). 因为ObjectAnimator在执行动画的过程中需要自动更新属性, 所以它必须可以通过setter方法访问属性值. 栗如如果属性的名字是foo, 那么我们必须实现一个setFoo()方法. 如果这个setter方法不存在, 我们有三个可以选择的方法:

如果我们有权限的话可以向类中加入该方法.

使用一个我们有权限的包装类, 并在该类中实现setter方法.

使用ValueAnimator类.

2.      如果我们在ObjectAnimator的工厂方法中只指定一个values参数(它可以一次指定多个), 它会被指定为动画的结束值. 这时我们就需要一个起始值, 这个值需要一个getter方法获取. Getter方法必须符合get<propertyName>()格式. 比如如果属性名字为foo,那么getter方法的名字是getFoo().

3.      Getter和setter方法必须使用相同类型的属性来作为起始和结束的值. 比如如果我们构造了如下的ObjectAnimator的话, 那么就必须使用targetObject.setPropName(float)和targetObject.getPropName(float):

ObjectAnimator.ofFloat(targetObject,"propName",1f)

4.      根据动画对象的不同有时候我们可能需要调用invalidate()方法在属性值更新的时候来重新绘制对象. 该方法应该在onAnimationUpdate()方法中调用. 比如, 一个Drawable对象的颜色属性被更改了, 只有它重新绘制的时候才会被更新. 所有View的setter方法比如setAlpha()和setTranslationX()都会重绘View, 所以我们不需要在调用这些方法的时候重绘View.

使用AnimatorSet编排多个动画:

在很多种情况下我们可能得根据前一个动画是否结束或者启动来启动下一个动画. Android允许我们将多个动画打包到一个AnimatorSet中执行, 所以我们可以指定同时/线性/延迟执行动画. AnimatorSet中的对象还可以嵌套使用. 下面看个栗子:

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();

这段代码播放了以下的动画:

1.      播放bounceAnim.

2.      同时播放squashAnim1, squashAnim2, stretchAnim1和stretchAnim2.

3.      播放bounceBackAnim.

4.      播放fadeAnim.

AnimationListener:

在动画播放的时候, 我们可以通过下列的listener来监听其中的重要事件:

Animator.AnimatorListener:

         onAnimationStart():当动画启动的时候调用.

         onAnimationEnd():当动画结束的时候调用.

         onAnimationRepeat():当动画重复的时候调用.

         onAnimationCancel():当动画被取消的时候调用, 被取消的动画也会调用onAnimationEnd().

ValueAnimator.AnimatorUpdateListener:

         onAnimationUpdate():整个动画过程的每帧都会调用. 我们可以通过监听这个事件来使用由ValueAnimator生成的计算值. 想要使用这个值, 我们需要调用getAnimatedValue()方法来获取当前的计算值. 如果我们使用ValueAnimator, 那么我们就需要实现这个监听器. 根据我们修改的对象的属性不同, 我们可能需要在一个View上调用invalidate()方法来重新绘制它. 比如, 如果被修改的是Drawable对象的颜色属性, 那么只有在重新绘制的时候它才会更新. 所有View的setter都会自己重绘View, 比如setAlpha()和setTranslationX(), 所以调用这些方法的时候我们不需要重绘View.

如果我们不想实现所有的Animator.AnimatorListener中的方法, 那么我们可以扩展AnimatorListenerAdapter类来代替Animator.AnimatorListener.AnimatorListenerAdapter提供了方法的空实现, 这样我们就可以只重写我们自己需要的方法. 比如使用AnimatorListenerAdapter只实现onAnimationEnd()方法:

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提供动画:

Property animation有能力为ViewGroup提供动画功能, 方式跟View对象一样简单. 我们可以通过LayoutTransition类来使ViewGroup的布局发生变化. 我们可以在LayoutTransition对象中通过调用setAnimator()方法并传入一个Animator对象来定义一个动画. 该Animator参数需要包含下列的一个LayoutTransition常量:

APPEARING: 一个标志, 指定出现时的动画.

CHANGE_APPEARING: 指定添加View导致布局改变的时候, 整个布局的动画.

DISAPPEARING: 指定消失的时候的动画.

CHANGE_DISAPPEARING: 指定删除或者隐藏View导致布局容器改变的时候, 整个布局的动画.

我们为这四种事件可以定义自己的自定义动画, 也可以让动画系统使用默认的动画. 在LayoutAnimationsByDefault栗子中, 它的layout_animations_by_default.xml资源文件展示给我们如何为ViewGroup指定默认的layout变化. 我们只需要设置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删除或者添加的时候会自动展示一个动画.

使用TypeEvaluator:

如果我们想要让一种Android不认识的数据类型使用动画, 那么我们需要使用TypeEvaluator接口创建自己的Evaluator. Android认识的类型包括int, float或者color. 分别由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);    }}

使用Interpolator:

Interpolator定义了动画中的指定属性如何根据时间变化. 比如线性, 先加速再减速等. Interpolator接收一个代表动画进度的分数,然后修改这个分数与要修改的属性值的类型一样. Android在android.view.animation包中提供了一系列的常用Interpolator. 如果都不能满足我们的需要, 那么就使用TimeInterpolator接口创造我们自定义的Interpolator. 下面用AccelerateDecelerateInterpolator和LinearInterpolator来举栗看一下它们是如何工作的. LinearInterpolator没有对传入的分数进行处理, AccelerateDecelerateInterpolator则对其进行了计算:

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;}

下面是1000ms内它们返回的值:

已经经过的时间ms

计算出来的结果(Linear)

计算出来的结果(Accelerate/Decelerate)

0

0

0

200

0.2

0.1

400

0.4

0.345

600

0.6

0.8

800

0.8

0.9

1000

1

1

就好像上面的表哥显示的, LinearInterpolator用均匀的速度修改值, 每200ms增加0.2. 而AccelerateDecelerateInterpolator则在200ms~600ms之间增速比LinearInterpolator快, 之后则比它要慢.

指定关键帧(Keyframe):

关键帧对象是由一个”时间/数值”对组成, 让我们可以在特殊的时间指定动画的特殊状态. 我们还可以在关键帧之间指定独立的Interpolator.

想要初始化一个关键帧实例, 我们必须使用其工厂方法, ofInt(), ofFloat或者ofObject来获得一个合适类型的关键帧. 然后还需要调用ofKeyframe()工厂方法来获得一个PropertyValueHolder对象. 一旦拥有了对象, 我们就可以通过传入PropertyValueHolder获得一个动画. 下面代码片段展示了这种用法:

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);

AnimatingViews:

Property animation系统支持View对象的线性动画并提供了一些比View animation更优秀的功能. View animation通过改变View对象绘制的方式来修改View对象. 其实这些是由View的容器来处理的, 因为View本身并没有属性被改变, 虽然这可以让View动起来. 但是这样会导致一个对象依然在原来的坐标上, 虽然它已经被绘制在了屏幕上不同的地方. 在Android3.0中, 新的属性和相应的getter和setter的加入, 消除了这一缺陷.

Property animation则通过修改对象的属性来让View移动起来. 另外Views还会自动调用invalidate()方法来重新绘制屏幕. 在View类中对property animation比较有用的属性有:

translationX和translationY: 这俩属性控制view绘制的坐标增量. 坐标是相对layout容器的.

rotation, rotationX和rotationY: 这些属性控制旋转, rotation用于2D, 剩下那俩用于3D.

scaleX和scaleY: 这些属性控制2D缩放.

PivotX和pivotY: 这些属性控制中心点的坐标.用于旋转和缩放. 默认情况下是对象的中间.

X和Y: 这俩表示相对父容器的坐标.相当于left和top的值分别加上translationX和translationY.

Alpha: 代表View的alpha透明度. 该值默认是1, 表示不透明, 0代表全透明.

想要对View对象的某个属性使用动画, 比如颜色和旋转角度, 只需要创建一个property animator然后指定View的属性就可以了, 比如:

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

使用ViewPropertyAnimator实现动画:

ViewPropertyAnimator类提供了简单的方法来并行的为View执行动画, 底层使用的是Animator对象. 它的行为跟ObjectAnimator类似, 因为它修改了View的属性值, 但是更吊的是它可以同时修改多个. 另外使用该类的代码也更加的简洁, 下列代码演示了使用多个ObjectAnimator, 单个ObjectAnimator和使用ViewPropertyAnimator执行线性动画的栗子:

多个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:

myView.animate().x(50f).y(100f);

在XML文件中声明动画:

Property animation系统允许我们在XML文件中声明动画, 通过定义我们的XML动画文件, 我们可以很简单的在多个activity中重用该动画, 编辑也更加简单.

为了区分新版本的property animation API和使用旧版本的view animation, 从Android3.1开始, 我们应该保存property animation的XML文件在res/animator/目录下. 下面三种property animation类在XML文件中有三种不同的标签:

ValueAnimator - <animator>

ObjectAnimator - <objectAnimator>

AnimatorSet - <set>

下面的栗子展示了两组线性执行的object animation, 第一个set嵌套了两个object animation:

<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 set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,    R.anim.property_animator);set.setTarget(myObject);set.start();

 

总结:

Property animation允许我们修改任何对象的属性.它内置了三种可以修改的类型, 包括整型, 浮点型和16位颜色值. Property animation允许我们定义这些对象属性的值与时间的函数, 是线性的, 非线性的, 递增还是递减的等. 通过控制对象属性在一段时间内的变化来完成对象本身的变化.

Property animation包含三个要素, Animators,Evaluators, Interpolators.

Animators: 通常我们使用它的子类ValueAnimator,ObjectAnimator, AnimatorSet. 其中ValueAnimator类是主要类, 它包含全部的核心功能. ObjectAnimator又是ValueAnimator的子类, 也是我们最常用到的类, 通常只需要使用工厂方法创建ObjectAnimator对象, 然后设置一下, 直接启动就可以了. AnimatorSet则是为分组动画服务, 使得多个动画可以线性或者同时执行.

Evaluators: 一个计算器, 用于指定需要变化的对象属性是如何变化的. 包含3个常用类和一个接口, 三个类分别是IntEvaluator, FloatEvaluator, ArgbEvaluator, 分别对应处理三种类型的对象属性. 接口是TypeEvaluator, 用于自定义类型的属性的计算.

Interpolators: 指定动画的属性值是如何变化的, 我们可以指定多种变化, 比如线性, 先加速再减速, 循环播放等.

 

其它有用的特性还包括: 监听器, ViewGroup动画, animator资源文件.


参考: https://developer.android.com/guide/topics/graphics/prop-animation.html



0 0
原创粉丝点击