Animation - Property Animation
属性动画
�属性动画实际上是一种�在一定时间�段内不断修改某个对象的某个属性值的��机制。所以我们仍然可以通过属性动画�动画将一个View�进行移动或缩放,但同时也可以对View的其他属性进行动画操作。我们只需要告诉�系统动画要操作的属性、动画时长、需要执行哪种类型的动画,以及动画的初始值和结束值,剩下的工作就可以全部交给系统去完成了。
属性动画核心类——�ValueAnimator
ValueAnimator
是整个属性动画机制当中最核心的一个类,它的作用就是在一定时间段内不断地修改对象的某个属性值。�
ValueAnimator
的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将属性的取值范围、运行时长提供给�ValueAnimator
,那么它就会自动帮我们计算属性值在各个动画运行时段的取值,这些值会按照一定的计算方式来实现平滑过渡。除此之外,ValueAnimator
还负责管理动画的播放次数、播放模式,以及对动画设置��监听器等,这使得它成为属性动画最核心的类型。
ValueAnimator
不仅功能强大,它的API也设计得非常简单。通常我们都是通过ofFloat、ofInt 等静态工厂函数构建ValueAnimator
。例如下面是一个将数值从0.0过渡到1.0的动画:
private void startValueAnimation() {
ValueAnimator animator = ValueAnimator.ofFloat(0.0f, 1.0f);
animator.setDuration(1000);
animator.addUpdateListener(mAnimationListener);
animator.start();
}
ValueAnimator.AnimatorUpdateListener mAnimationListener = new
ValueAnimator.AnimatorUpdateListener() {
@override
public void onAnimationUpdate (ValueAnimator animation) {
float newValue = (Float) animation.getAnimatedValue();
Log.e("","### 新的属性值: " + newValue);
}
};
当然,我们也可以在 res/anim 目录下的XML文件中定义该动画,实现如下:
<?xml version="1.0" encoding="utf-8"?>
<animator
xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="0.0"
android:valueTo="1.0"
android:valueType="floatType"/>
然后在Java代码中加载该动画:
ValueAnimator animator = (ValueAnimator) AnimatorInflater.loadAnimator(
getApplicationContext(), R.anim.value_animator);
启动动画之后,每次更新属性�值时就会调用 onAnimationUpdate
函数,�在这里可以获取�新的属性值。当然,在这里我们并没有将这个值运用到具体的对象�上。但它是非常灵活的实现,它只操作属性值本身,这个值不属于某个具体的对象,但它却能运用于任意对象之上。例如,通过 ValueAnimator
动画将某个View的透明度从0.0 过渡到 1.0, 那么可以对 onAnimationUpdate
做如下修改:
@override
public void onAnimationUpdate (ValueAnimator animation) {
float newValue = (Float) animation.getAnimatedValue();
Log.e("","### 新的属性值: " + newValue);
// 将数值设置给具体的对象
myView.setAlpha(newValue);
}
这样以来,我们就将ValueAnimator
和具体的对象结合在一起,通过这种形式就能更自由地控制动画,完成各种各样的动画效果。
对任意属性进行动画操作——�Object Animator
ViewAnimator
功能强大、自由度高,但是,这也意味着�开发人员需要做更多的工作来实现动画需求,这在效率上�致的软件开发领域来说并不是一个很好的选择。我们开发中运用更多的应该是� ObjectAnimator
,因为 ViewAnimator
只是对值进行了一个平滑的动画过渡 ,但实际�开发中需要做的�通常是对某个对象的某个属性值进行修改,也就是对某个对象执行动画,当然用的最多的就是View的动画,�而ObjectAnimator
就是可以直接对任意对象的任意属性进行动画操作的类。
ObjectAnimator
继承自 �ViewAnimator
,因此,它的动画实现机制也与 ViewAnimator
一致,所以前文才说 ViewAnimator
时属性动画�中最核心的类。 ObjectAnimator
最常用的形式也是通过 �ofFloat, ofInt 等静态工厂的形式构建 Animator 对象,例如下述代码就是在2�秒之内将 myView 的 alpha 属性从 1.0 过渡到 0.3, 再从 0.3 过渡到 0.7:
ObjectAnimator animator = ObjectAnimator.ofFloat(myView, "alpha", 1.0f, 0.3f, 0.7f);
animator.setDuration(2000);
animator.start();
ofXxx
这样的静态工厂函数通常至少含有4个参数,例如,这里的 ofFloat
函数,参数1就是要操作的对象,参数2时要操作该对象的哪个属性,这里是alpha
属性。 剩下的参数就是可变参数,�也就是说它可以是 0 到多个。很多情况�下都是传递� 2 个值,即起始值和目标值。如果是多个值,那么在动画过程中将会逐个过渡到各个值。
ObjectAnimator
�极为强大,它能操作任意对象的任意属性,因此,它突破了补间动画只能用于View的限制,使得任意对象类型都可以使用属性动画。
它的原理是在初始时设置目标对象、目标属性以及要经历的属性值,然后通过内部的计算方式计算出各个时间段该属性的取值,在动画运行期间通过目标对象属性的 setter
函数更新该属性值,如果改属性没有 setter
� 函数。那么将会通过反射的形式更新目标属性值。在运行�周期内不断地计算、更新新的属性值,从而达到对象的属性动画效果。
实现丰富多彩的动画效果——�AnimatorSet
独立的动画能给实现的视觉效果是相当有限的,Android 为我们提供了 将多个动画组合在一起执行的 工具 AnimatorSet
。该类提供一个 play()
方法,如果我们向这个方法中传入一个 Animator
对象 将会返回一个 AnimatorSet.Builder
的实例, AnimatorSet.Builder
中包括以下 5 个核心方法:
函数 | 作用 |
---|---|
after(Animator anim) |
在anim动画执行完之后再执行调用after 函数的动画 |
after(long delay) |
��将调用 after 的动画延迟指定毫秒后执行 |
before(Animator anim |
在anim动画执行之前执行调用after 函数的动画 |
with(Animator anim) |
将现有动画和传入的动画同时执行 |
playTogether(Animator...anims) |
将多个动画一起执行 |
示例:
// 动画集,假设 anim1~anim3� 已经初始化
�AnimatorSet animSet = new AnimatorSet();
// 在anim3执行之后同时执行 anim1 和 anim2
animSet.play(anim1).with(anim2).after(anim3);
/*
三个动画一起执行
animSet.playTogether(anim1,anim2,anim3);
*/
animSet.setDuration(1000);
animSet.start();
�
通过 AnimatorSet
, 我们可以将多个动画进行自由组合、排序,使得不同类型的动画最终可以一起实现复杂的效果,满足各种各样的交互应用。
动画执行时间——TypeEvaluator
与TimeInterpolator
在谈到View动画的时候,我们提到过:动画的原理就是在�一定的时间内不断地修改某个值。 那么某个时间点这个属性的值如何确定呢?
答案就是通过 TypeEvaluator
计算得到。 TypeEvaluator
的中文翻译为�类型估值器,它的作用是根据当前动画已执行时间占总时间的百分比来计算新的属性值。 TypeEvaluator
只有一个 evaluate
函数,该函数的职责就是计算出新的属性值。函数的声明如下:
public abstruct T evaluate (float fraction, T startValue, T endValue)
该函数的参数1为执行时间占总时间的百分比,取值为 0.0 到 1.0 。参数2为属性的起始值,参数3为最终值。通常,属性的计算公式为:
T newValue = startValue + (T)(fraction * (endValue-startValue));
公式很好理解,已执行时间的百分比�乘以取值范围差再加上起始值。 例如�某个动画的总时间为1�秒,动画的�功能是将View的x坐标从0移到100的位置,当已执行时间为300毫秒时,它的计算公式为:
int newValue = 0 + (int)( 0.3 * ( 100 - 0 ));
因此,一个完整的TypeEvaluator
代码如下:
public class TranslateEvaluator implements TypeEvaluator<Integer> {
@override
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
// 计算新的值
int newValue = startValue + ( fraction * ( endValue - startValue));
return newValue;
}
}
�使用代码如下:
private void useCustomEvaluator() {
ObjectAnimator animator = ObjectAnimator.ofObject(mView,
"x", new TranslateEvaluator(), 0, 200);
animator.setDuration(500);
animator.start();
}
fraction 从 0 逐渐增加到 1.0,此过程中属性值也从0慢慢 线性 增加到200。��了解过 Material Design
的话,线性并不能提供最佳的用户体验,我们需要动画产生一些非线性的效果。 这里引入 TimeInterpolator
。
TimeInterpolator
,译为时间插值器,作用是修改动画已执行时间与总时间的百分比,�相应的就是修改 fraction 的值。
系统预置的有如下插值器:
Interpolator | 资源id | 功能作用 |
---|---|---|
AccelerateDecelerateInterpolator | @android:anim/accelerate_decelerate_interpolator | 先加速再减速 |
�AccelerateInterpolator | @android:anim/accelerate_interpolator | 加速 |
�AnticipateInterpolator | @android:anim/anticipate_interpolator | 先回退一小步然后加速前进 |
AnticipateOvershootInterpolator | @android:anim/anticipate_overshoot_interpolator | 在上一个基础上超出终点一小步再退回终点 |
�BounceInterpolator | @android:anim/bounce_interpolator | 最后阶段弹球效果 |
CycleInterpolator | @android:anim/cycle_interpolator | 周期运动 |
DecelerateInterpolator | @android:anim/decelerate_interpolator | 减速 |
LinearInterpolator | @android:anim/linear_interpolator | 匀速 |
�OvershootInterpolator | @android:anim/overshoot_interpolator | 快速到达终点并超出一小步最后回到终点 |
它的作用时在获得已执行时间百分比后,通过调用 TimeInterpolator
的 getInterpolation
函数来对�该百分比做出修改,并返回。
下面以加速插值器为例谈谈 原理 用法。
加速插值器的实现原理是 使fraction 参数在动画前面部分变化范围小,越往后变化范围越大。可以联想到初高中学习的物理知识,�直线运动,给一个�物体以�正的加速度,运动相同时间�,前面经过的路程比后面的路程要小很多。
那么如何控制 fraction� 来实现这种效果呢?
那就是 TimeInterpolator
的任务,在�动画执行时,会调用� TimeInterpolator
的 getInterpolation
函数使得开发人员有机会参与到� fraction 的设定,这样开发人员就可以通过不同的 TimeInterpolator
实现各种各样与动画频率相关的效果。 getInterpolation
函数的声明如下:
public float getInterpolation (float input)
参数就是 fraction 本身,返回值则是修改后的 fraction 值。 例如线性插值器时匀速执行的,因此�,它没有修改 fraction 值, LinearInterpolation
代码如下:
public class LinearInterpolation implements Interpolator, NativeInterpolatorFactory {
// 代码省略
public float getInterpolation (float input) {
return input;
}
}
下面来实现一个加速插值器。它的原理就是对 getInterpolation
的fraction参数进行乘方。因为 fraction 是 float 型,取值在 0.0 到 1.0�,因此�数值越小,乘方后更小,也就是说通过逐渐增大一时段内的 fraction 值变化范围,即可改变动画的执行效果。 实现代码如下:
public class CustomInterpolation implements Interpolator, NativeInterpolatorFactory {
// 代码省略
public float getInterpolation (float input) {
return input * input;
}
}
具体的执行代码:
private void useCustomEvaluator() {
ObjectAnimator animator = ObjectAnimtor.ofObject(mColorImageView, "x",
new TranslateXEvaluator(), 0, 200);
// 使用自定义插值器
animator.setInterpolator(new CustomInterpolation());
animator.setDuration(500);
animator.start();
}