Android 属性动画

Animation - Property Animation

官方Api

属性动画

�属性动画实际上是一种�在一定时间�段内不断修改某个对象的某个属性值的��机制。所以我们仍然可以通过属性动画�动画将一个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, 我们可以将多个动画进行自由组合、排序,使得不同类型的动画最终可以一起实现复杂的效果,满足各种各样的交互应用。


动画执行时间——TypeEvaluatorTimeInterpolator

在谈到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 快速到达终点并超出一小步最后回到终点

它的作用时在获得已执行时间百分比后,通过调用 TimeInterpolatorgetInterpolation 函数来对�该百分比做出修改,并返回。

下面以加速插值器为例谈谈 原理 用法
加速插值器的实现原理是 使fraction 参数在动画前面部分变化范围小,越往后变化范围越大。可以联想到初高中学习的物理知识,�直线运动,给一个�物体以�正的加速度,运动相同时间�,前面经过的路程比后面的路程要小很多。

那么如何控制 fraction� 来实现这种效果呢?

那就是 TimeInterpolator 的任务,在�动画执行时,会调用� TimeInterpolatorgetInterpolation 函数使得开发人员有机会参与到� 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();
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,884评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,212评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,351评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,412评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,438评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,127评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,714评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,636评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,173评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,264评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,402评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,073评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,763评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,253评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,382评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,749评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,403评论 2 358

推荐阅读更多精彩内容