概述
- 持续时间:可以指定动画的持续时间,默认是300 ms
- 时间插值:可以在动画运行时通过一个函数来指定需要改变的属性值多久计算一次。
- 重复次数和行为:当动画播放到末尾时,可以指定是否重复播放以及播放的次数。你还可以指定动画是否倒着播放。设置反向播放动画反复向前然后向后,直到达到重复出现的次数。
- 动画设置:可以对动画进行逻辑分组,一起播放或顺序播放或指定延迟播放。
- 帧刷新延迟:可以指定动画帧的刷新频率。默认值是每10ms 刷新一次,但是最终的刷新速度取决于你系统整体的繁忙度和响应时间。
属性动画工作原理
ValueAnimator
封装了一个
TimeInterpolator,被用来定义补间动画,还有一个
TypeEvaluator
,被用来定义动画开始后的属性值。例如,在图2,
TimeInterpolator
将会使用
AccelerateDecelerateInterpolator
,而
TypeEvaluator
将会使用
IntEvaluator
.。
ValueAnimator
并且给它你想要动画化属性开始和结束的值,随着动画的进行。当调用
start(),动画开始播放。在动画播放的整个时间段内,
ValueAnimator
计算一个在0和1之间的已播放分数(
elapsed fraction
),基于持续时间和已播放时间。这个已播放分数代表着动画播放完成时间的百分百,0意味着0%,1意味着100%。例如,在图1,在t=10ms时的已播放分数是0.25因为总的持续时间是40ms 。
ValueAnimator
计算完成了一个已播放分数,就会调用刚才设置的
TimeInterpolator
来计算插值分数(
interpolated fraction
)。一个插值分数将已播放分数映射到一个新的需要考虑时间插值的分数。例如,图2中,因为动画是逐渐加速的,在t=10ms 时,它的插值分数,大约是0.15,小于此时的已播放分数0.15。在图1,插值分数永远等于已播放分数。
ValueAnimator
调用适当的
TypeEvaluator
,来计算你需要动画化的属性的值,基于插值分数,开始值和结束值。例如,在图2中,t =10ms 时的插值分数是0.15,所以此时的属性值将会是0.15x (40 - 0),或者6。
属性动画与视图动画的不同之处
API 概述
android.animation
.中可以找到最多的关于属性动画系统的API相关知识。因为在
android.view.animation
中已经定义了许多视图动画系统的插入器,你可以在属性动画系统中使用这些插入器。接下来的几张表格展示了属性动画系统的主要组件。
Table 1. Animators
Class | Description |
---|---|
ValueAnimator | 属性动画的主要计时引擎,也经常计算动画时的属性值。它具有所有的核心功能——计算动画值和包含每个动画的时间细节, 有关于动画何时重复播放的信息, 接收更新事件的监听器, 设置自定义的估计(evaluate)类型. 它主要有两个功能: 计算动画的值和在动画时为对象设置这些值。ValueAnimator 并不执行第二个功能, 因此你必须通过监听. ValueAnimator 计算出来的值的更新事件 来用你自己的逻辑修改你想要动画的对象的值。查看 #使用ValueAnimator制作动画部分获取更多消息。 |
ObjectAnimator | ValueAnimator 的一个子类,允许你设置目标对象和需要动画化的属性。 该类根据动画计算出来的新值来更新属性值. 大多数时候你需要用到 ObjectAnimator , 因为它使动画值用在目标对象上更加容易。然而,有些时候你必须直接使用ValueAnimator 因为ObjectAnimator 有一些限制,例如在目标对象上请求特定的存取方法。 |
AnimatorSet | 提供一个组织动画的途径,这样你可以让它们通过一个顺序运行。例如一起播放,顺序播放,或延迟播放. 查看#使用AnimatorSet编排复合动画 章节获取更多信息。 |
求值器(Evaluator )告诉属性动画系统如何计算给定属性的值。它们通过 Animator
类来获取提供的时间数据,动画开始和结束的值,并且基于这些值计算动画时的属性值。属性动画系统支持如下求值器:
Table 2. Evaluators
Class/Interface | Description |
---|---|
IntEvaluator | 计算int 属性的默认求值器 |
FloatEvaluator | 计算float 属性的默认求值器 |
ArgbEvaluator | 计算颜色属性的默认求值器,是一个十六进制的值 |
TypeEvaluator | 一个允许你创建自己的求值器的接口。 如果你要计算的对象属性值不在int,float,或颜色之中,你必须实现 TypeEvaluator 接口来指定如何计算动画属性值。你也可以为int,float,color指定一个自定义的求值器,如果不你想用默认方法的处理这些类型。查看 #使用TypeEvaluator章节获取更多信息 |
时间插值器(time interpolator)定义了一个时间函数如何在动画时计算一个特定的值。例如,你可以指定动画在整个动画时是线性发生的,意味着在整个时间内动画移动是匀速的。或者你可以指定为非线性的,例如在开始时加速,结束时减速。表3描述了包含在android.view.animation
.中的插值器。如果这之中没有你需要的,实现TimeInterpolator接口来创建你自己的。查看#使用插值(Interpolators)来获取更多关于如何自定义插值器的知识。
Table 3. Interpolators
Class/Interface | Description |
---|---|
AccelerateDecelerateInterpolator | 开始和结束时慢,中间快 |
AccelerateInterpolator | 开始慢,然后加速 |
AnticipateInterpolator | 开始向后,然后向前 |
AnticipateOvershootInterpolator | 开始向后,甩向前并且冲过目标值,最终回到目标值 |
BounceInterpolator | 最后反弹 |
CycleInterpolator | 重复指定次数 |
DecelerateInterpolator | 开始快,然后减速 |
LinearInterpolator | 恒定不变速度 |
OvershootInterpolator | 甩向前,冲过目标值,然后回来 |
TimeInterpolator | 可以让你实现自动的插值器的一个接口 |
ValueAnimator
类可以在动画持续时间内
通过指定一组int,float 或color 值实现某些动画类型。通过调用它的工厂方法来获得
ValueAnimator
:
ofInt()
,
ofFloat()
, or
ofObject()
例如
ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
animation.setDuration(1000);
animation.start();
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();
ValueAnimator
开始计算动画值,介于
startPropertyValue
和
endPropertyValue
之间,使用由
MyTypeEvaluator
提供的逻辑,持续时间1000ms 。
ValueAnimator
并不直接操作对象或值。更有可能的方法是通过修改这些动画的计算值来修改对象。在
ValueAnimator
中定义一个监听器来处理动画期间的重要事件,如帧刷新。当实现了该监听器,你可以通过调用
getAnimatedValue()
.
得到这一帧刷新时的计算值。更多关于这个监听器的知识,请看
#动画监听器
。
ObjectAnimator
是
ValueAnimator
的子类(在之前章节介绍过)并且结合了时序引擎和计算
ValueAnimator的目标对象动画化属性值的功能。这使得动画化任何对象变得容易,一如你不再需要实现
ValueAnimator.AnimatorUpdateListener,
因为它的动画属性会自动更新。
ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
anim.setDuration(1000);
anim.start();
ObjectAnimator
正确的更新,你必须做如下工作:
ObjectAnimator
在动画时自动的更新属性,一定要通过这个setter函数来设置属性。例如,如果这个属性名为
foo
,你就必须要有一个
setFoo()
的方法。如果没有这样的setter 方法,你有三种选择:
- 如果你有权利这么做,在类中加入这个setter方法。
- 使用包装类,这样你就有权去更改这个包装类,并且该包装类通过setter函数获得有效的值,并将它转发给原来的对象。
- 使用
ValueAnimator
替代
ObjectAnimator
的某一构造函数中,
你的
values...
参数中只有一个值,它将被假定为结束值。因此,动画对象的属性必须有一个getter函数用来获得动画开始时的开始值。这个getter函数必须是
get<propertyName>()形式的。
例如,如果属性名为
foo
,你必须有一个
getFoo()
的函数。
ObjectAnimator
指定的
开始和结束值。例如,如果你构造了如下的
ObjectAnimator
:,你必须有
targetObject.setPropName(float)
和
targetObject.getPropName(float):
ObjectAnimator.ofFloat(targetObject, "propName", 1f)
setAlpha()
和
setTranslationX()
直接无效了,所以当调用这些方法设置新值时,你不必无效化view。更多关于监听器的信息,请查看
#动画监听器
AnimatorSet
对象。
- Plays
bounceAnim
. - Plays
squashAnim1
,squashAnim2
,stretchAnim1
, andstretchAnim2
同时播放 - Plays
bounceBackAnim
. - Plays
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()方法来重绘视图和更新动画值。在onAnimationUpdate()回调函数中做这些事情。例如,为一个可绘制对象的颜色属性做动画,当这个对象重绘自己的时候才导致更新屏幕。所有view 上的设置,如setAlpha() 和 setTranslationX() 直接无效了,所以当调用这些方法设置新值时,你不必无效化view。
-
你可以继承 AnimatorListenerAdapter
类而不是实现Animator.AnimatorListener接口,如果你不想实现Animator.AnimatorListener接口中的所有方法。AnimatorListenerAdapter
类提供了空的实现方法供你选择性的覆盖。
例如,在api示例中的Bouncing Balls 案例中,创建了一个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());
}
为ViewGroups做布局改变动画
setVisibility()
方法设置时。当你添加或移除View时,
ViewGroup
中
其余的View也可以动画移动到新的位置。通过调用
LayoutTransition
的
setAnimator()
传递一个
Animator
对象和如下的一个
LayoutTransition
常量
,来定义一个动画:
APPEARING
- 一个标志,表明那些出现在容器中的项目的动画。CHANGE_APPEARING
- 表明那些由于新项目出现在容器中而需要改变的项目的动画DISAPPEARING
-表明那些从容器中消失的项目的动画CHANGE_DISAPPEARING
- 表明那些由于一个项目在容器中消失而需要改变的项目的动画,
android:animateLayoutchanges
属性为
true
,如下:
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="@+id/verticalContainer"
android:animateLayoutChanges="true" />
TypeEvaluator
接口来创建自己的求值器。目前安卓系统可识别的类型是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
(or
ObjectAnimator
)运行时,为动画计算了一个确切的已运行分数(介于0-1之间的值),然后计算了一个插值,具体取决于你所使用的插值器。插值分数是你的
TypeEvaluator通过
fraction
参数收到的,所有在计算动画值时不需要考虑插值器。
android.view.animation package
.包下安卓系统提供了一组通用的插值器。如果这些都不适合你,可以实现
TimeInterpolator
接口来创建自己的插值器。
AccelerateDecelerateInterpolator
和
LinearInterpolator
是如何计算插值分数的。
LinearInterpolator
对已播放分数没有影响。
AccelerateDecelerateInterpolator 加速进入动画,减速出来。下面展示了他们的插值逻辑:
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
public float getInterpolation(float input) {
return input;
}
已播放 ms | 已播放分数/插值分数 (Linear) | 插值分数 (Accelerate/Decelerate) |
---|---|---|
0 | 0 | 0 |
200 | .2 | .1 |
400 | .4 | .345 |
600 | .6 | .8 |
800 | .8 | .9 |
1000 | 1 | 1 |
LinearInterpolator
以恒定速度改变值,每过200ms增长.2。
AccelerateDecelerateInterpolator
比
LinearInterpolator
改变值的速度,在200ms -600ms 的时候更快,在600ms -1000ms的时候更慢。
指定关键帧
Keyframe
对象,你必须yonder其中的一个工厂方法,
ofInt()
,
ofFloat()
, 或
ofObject()
来获得合适类型的
Keyframe。之后调用
ofKeyframe()
工厂方法来获得一个
PropertyValuesHolder
对象。一旦你有了该对象,你可以通过传递 该对象和目标动画对象 来获得一个动画制作者对象。像这样:
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);
视图动画化
invalidate()
方法来刷新屏幕。在View 类中新添加的属性如下:
translationX
andtranslationY
: 这些属性控制了View的位置,作为它左部和上部坐标的增量,由布局容器设置。平移rotation
,rotationX
, androtationY
: 这些属性控制了在2D和3D坐标系中围绕着中心点(锚点)的旋转。旋转scaleX
andscaleY
:这些属性控制了在2d坐标系中围绕着中心点(锚点)的缩放。缩放pivotX
andpivotY
: 这些属性控制了中心点的位置,它与旋转和缩放转换有关。默认情况下,中心点位于对象的正中央。锚点x
andy
: 用来描述view在容器中的最终坐标,是左部和上部加上x和y的偏移量的和。alpha
: 代表着view的透明度变化。默认值为1(不透明),越接近0代表着越透明(看不见)。
ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);
使用ViewPropertyAnimator制作动画
ViewPropertyAnimator
提供了一个简单的方法来并行动画化几个视图属性,使用一个简单的底层
Animator
对象。它的行为更像是
ObjectAnimator
,因为它修改了view的真实属性值,但是更加高效的是它同时动画化许多属性。另外,关于使用
ViewPropertyAnimator的代码更加简洁和易于阅读。接下来的代码片段展示了复合
ObjectAnimator对象,单一的
ObjectAnimator
,和
ViewPropertyAnimator
在同时动画化x和y属性时的不同。
ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
myView.animate().x(50f).y(100f);
在XML文件中声明动画
res/animator/
目录(不是
res/anim/
)下保持你的属性动画xml文件。使用
animator
目录是可选的,但是如果你想使用ADT插件(ADT 11.0.0+)的视图编辑工具时,这是必须的。因为ADT只会查找在
res/animator/
目录下的属性动画文件。
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>
setTarget()
来设置一个简单对象,为方便,目标会应用于所有
AnimatorSet
的子节点。代码如下:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
R.anim.property_animator);
set.setTarget(myObject);
set.start();