【动画系列】属性动画

来源:互联网 发布:python和c 的区别 编辑:程序博客网 时间:2024/04/30 07:31

为什么要引入属性动画

补间动画缺陷:
1.它只能够实现移动、缩放、旋转和淡入淡出这四种动画操作,那如果我们希望可以对View的背景色进行动态地改变呢?很遗憾,我们只能靠自己去实现了。说白了,之前的补间动画机制就是使用硬编码的方式来完成的,功能限定死就是这些,基本上没有任何扩展性可言。
2.它只是改变了View的显示效果而已,而不会真正去改变View的属性。什么意思呢?比如说,现在屏幕的左上角有一个按钮,然后我们通过补间动画将它移动到了屏幕的右下角,现在你可以去尝试点击一下这个按钮,点击事件是绝对不会触发的,因为实际上这个按钮还是停留在屏幕的左上角,只不过补间动画将这个按钮绘制到了屏幕的右下角而已。

也正是因为这些原因,Android开发团队决定在3.0版本当中引入属性动画这个功能.

新引入的属性动画机制已经不再是针对于View来设计的了,也不限定于只能实现移动、缩放、旋转和淡入淡出这几种动画操作,同时也不再只是一种视觉上的动画效果了。它实际上是一种不断地对值进行操作的机制,并将值赋值到指定对象的指定属性上,可以是任意对象的任意属性。所以我们仍然可以将一个View进行移动或者缩放,但同时也可以对自定义View中的Point对象进行动画操作了。我们只需要告诉系统动画的运行时长,需要执行哪种类型的动画,以及动画的初始值和结束值,剩下的工作就可以全部交给系统去完成了。

既然属性动画的实现机制是通过对目标对象进行赋值并修改其属性来实现的,那么之前所说的按钮显示的问题也就不复存在了,如果我们通过属性动画来移动一个按钮,那么这个按钮就是真正的移动了,而不再是仅仅在另外一个位置绘制了而已。

属性动画体系

这里写图片描述

我们平时使用属性动画的重点就在于AnimatorSet、ObjectAnimator、ValueAnimator。

属性动画的原理

以alpha动画为例,ObjectAnimator内部的工作机制并不是直接对我们传入的属性名进行操作的,而是会去寻找这个属性名对应的get和set方法,因此alpha属性所对应的get和set方法应该就是:

public void setAlpha(float value);
public float getAlpha();

那么textview对象中是否有这两个方法呢?确实有,并且这两个方法是由View对象提供的,也就是说不仅TextView可以使用这个属性来进行淡入淡出动画操作,任何继承自View的对象都可以的。

既然alpha是这个样子,相信大家一定已经明白了,前面我们所用的所有属性都是这个工作原理,那么View当中一定也存在着setRotation()、getRotation()、setTranslationX()、getTranslationX()、setScaleY()、getScaleY()这些方法,不信的话你可以到View当中去找一下。

view动画与属性动画的区别

1.需要的Anroid API level不一样:兼容性问题,属性动画从API11才有

2.Property Animation适用于所有的Object对象,而View Animation则只能应用于View对象

3.Property Animation的XML声明文件存放在res/animator目录下,而View Animation则存放在res/anim目录

4.property Animation基于Animator和AnimatorSet的类结构,而View Animation则基于Animation和AnimationSet的类结构

5.实现原理不同
1)Animation 动画
在每次进行绘图的时候,通过对整块画布的矩阵进行变换,从而实现一种视图坐标的移动,但实际上其在 View 内部真实的坐标位置及其他相关属性始终恒定

矩阵变化
图形变换通过仿射矩阵实现。图形变换是图形学中的基本知识,简单来说就是,每种变换都是一次矩阵运算。在 Android 中,Canvas 类中包含当前矩阵,当调用 Canvas.drawBitmap (bmp, x, y, Paint) 绘制时,A ndroid 会先把 bmp 做一次矩阵运算,然后将运算的结果显示在 Canvas 上。这样,编程人员只需不断修改 Canvas 的矩阵并刷新屏幕,View 里的对象就会不停的做图形变换,动画就形成了。

2)Animator 动画
Animator 动画的实现机制说起来其实更加简单一点,因为他其实只是计算动画开启之后,结束之前,到某个时间点得时候,某个属性应该有的值,然后通过回调接口去设置具体值,其实 Animator 内部并没有针对某个 view 进行刷新,来实现动画的行为,动画的实现是在设置具体值的时候,方法内部自行调取的类似 invalidate 之类的方法实现的.也就是说,使用 Animator ,内部的属性发生了变化.
view动画改变的只是显示,并不能响应事件

6.体系和原理都不同,属性动画可以实现view动画的所有功能,且更厉害

7.如果用xml来写动画的话,他们xml文件的位置是不一样的

ValueAnimator

概念

属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。除此之外,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等,确实是一个非常重要的类。

ValueAnimator当中最常用的应该就是ofFloat()和ofInt()这两个方法了,另外还有一个ofObject()方法

除此之外,我们还可以调用setStartDelay()方法来设置动画延迟播放的时间,调用setRepeatCount()和setRepeatMode()方法来设置动画循环播放的次数以及循环播放的模式,循环模式包括RESTART和REVERSE两种,分别表示重新播放和倒序播放的意思。

ObjectAnimator(重点掌握这个即可)

概念

相比于ValueAnimator,ObjectAnimator可能才是我们最常接触到的类,因为ValueAnimator只不过是对值进行了一个平滑的动画过渡,但我们实际使用到这种功能的场景好像并不多。而ObjectAnimator则就不同了,它是可以直接对任意对象的任意属性进行动画操作的,比如说View的alpha属性。

ObjectAnimator继承自ValueAnimator,即ValueAnimator中可以用的方法,在ObjectAnimator也可以使用。它们的用法非常的相似。

例子

例1

在5s中将一个view从常规编程全透明,再从全透明变换为常规,可以这么写:

        final View view2 = findViewById(R.id.btn21111);        view2.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                // 这个过程中也是渐变的                ObjectAnimator.ofFloat(view2, "alpha", 1f, 0f, 1f).setDuration(5000).start();            }        });

可以看到我们调用了ofFloat方法来创建一个ObjectAnimator的实例,只不过ofFloat方法当中接收的参数有点变化了。这里第一个参数要求传入一个object对象,我们想要对哪个对象进行动画操作就传入什么,这里传入的是一个view。第二个参数是想要对该对象的哪个属性进行动画操作,由于我们想要改变view的透明度,因此传入的是“alpha”。后面的参数就是不固定长度了,想要完成什么样的动画就传入什么值,这里传入的值就表示将view从常规变换为全透明,再从全透明变换为常规。之后调用setDuration方法来设置动画的时长,然后调用start方法启动动画。

例2

        final View view3 = findViewById(R.id.btn31111);        final float curTranslationX = view3.getTranslationX();        view3.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                ObjectAnimator.ofFloat(view3, "translationX", curTranslationX, -500f, curTranslationX).setDuration(5000).start();            }        });

这里我们先调用了view的getTranslationX方法来获取到当前的view的translationX的位置,然后ofFloat方法的第二个参数传入”translationX”,紧接着后面三个参数用于告诉系统view应该怎么移动。

在在ValueAnimator中,又封装了两个类

插值器Interpolator

TimeInterpolator,也称插值器,是来计算当前动画运动的一个跟时间有关系的比例因子。

时间流逝百分比

LinearInterploator

AccelerateDecelerateInterpolator

DecelerateInterpolator

interpolator定义一个动画的变化率(the rate of change)。这使得基本的动画效果(alpha, scale, translate, rotate)得以加速,减速,重复等。

用通俗的一点的话理解就是:动画的进度使用 Interpolator 控制。Interpolator 定义了动画的变化速度,可以实现匀速、正加速、负加速、无规则变加速等。Interpolator 是基类,封装了所有 Interpolator 的共同方法,它只有一个方法,即 getInterpolation (float input),该方法 maps a point on the timeline to a multiplier to be applied to the transformations of an animation。Android 提供了几个 Interpolator 子类,实现了不同的速度曲线,如下: AccelerateDecelerateInterpolator 在动画开始与介绍的地方速率改变比较慢,在中间的时候加速 AccelerateInterpolator 在动画开始的地方速率改变比较慢,然后开始加速 CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线 DecelerateInterpolator 在动画开始的地方速率改变比较慢,然后开始减速 LinearInterpolator 在动画的以均匀的速率改变

来自《Android群英传》的说法:通过插值器可以定义动画的变换速率,这一点非常类似于物理中的加速度,其作用主要是控制目标变量的变化值对应的变化。书上p172有一个例子

估值器TypeEvaluator

TypeEvaluator,这个就是利用TimeInterpolator计算出来的因子来算出当前动画运行到的位置。

属性改变百分比

IntEvaluator

FloatEvaluator

ArgbEvaluator

那么TypeEvaluator的作用到底是什么呢?简单来说,就是告诉动画系统如何从初始值过度到结束值。我们在上一篇文章中学到的ValueAnimator.ofFloat()方法就是实现了初始值与结束值之间的平滑过度,那么这个平滑过度是怎么做到的呢?其实就是系统内置了一个FloatEvaluator,它通过计算告知动画系统如何从初始值过度到结束值,我们来看一下FloatEvaluator的代码实现:

AnimatorSet

概念

实现组合动画功能主要需要借助AnimatorSet这个类,这个类提供了一个play()方法,如果我们向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimator)将会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括以下四个方法:
after(Animator anim) 将现有动画插入到传入的动画之后执行
after(long delay) 将现有动画延迟指定毫秒后执行
before(Animator anim) 将现有动画插入到传入的动画之前执行
with(Animator anim) 将现有动画和传入的动画同时执行
好的,有了这四个方法,我们就可以完成组合动画的逻辑了,

例子

将view从屏幕外移动进屏幕,然后开始旋转360度,旋转的同时进行淡入淡出操作,就可以写成如下:

        final View view4 = findViewById(R.id.btn41111);        view4.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                ObjectAnimator moveIn = ObjectAnimator.ofFloat(linearLayout, "translationX", -500f, 0f);                ObjectAnimator rotate = ObjectAnimator.ofFloat(linearLayout, "rotation", 0f, 360f);                ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(linearLayout, "alpha", 1f, 0f, 1f);                AnimatorSet animSet = new AnimatorSet();                animSet.play(rotate).with(fadeInOut).after(moveIn);                animSet.setDuration(5000);                animSet.start();            }        });

使用xml来编写动画

通过XML来编写动画可能会比通过代码来编写动画要慢一些,但是在重用方面将会变得非常轻松,比如某个将通用的动画编写到XML里面,我们就可以在各个界面当中轻松去重用它。
如果想要使用XML来编写动画,首先要在res目录下面新建一个animator文件夹,所有属性动画的XML文件都应该存放在这个文件夹当中。然后在XML文件中我们一共可以使用如下三种标签:

<animator>  对应代码中的ValueAnimator<objectAnimator>  对应代码中的ObjectAnimator<set>  对应代码中的AnimatorSet

例子:

<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android"     android:ordering="sequentially"    >    <objectAnimator        android:duration="2000"        android:propertyName="translationX"        android:valueFrom="-500"        android:valueTo="0"        android:valueType="floatType"        />    <set android:ordering="together">        <objectAnimator            android:duration="3000"            android:propertyName="rotation"            android:valueFrom="0"            android:valueTo="360"            android:valueType="floatType"            />        <set android:ordering="sequentially">            <objectAnimator                android:duration="1500"                android:propertyName="alpha"                android:valueFrom="1"                android:valueTo="0"                android:valueType="floatType"                />            <objectAnimator                android:duration="1500"                android:propertyName="alpha"                android:valueFrom="0"                android:valueTo="1"                android:valueType="floatType"                />        </set>    </set></set>
        findViewById(R.id.btn111111).setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                Animator animator = AnimatorInflater.loadAnimator(SimpleActivity.this, R.animator.amima1);                animator.setTarget(linearLayout);                animator.start();            }        });

备注

1.可以对任何对象的属性做动画:满足条件

2.两种写法(根据场景而定)
代码来动态创建
xml(在res/animator)

3.属性动画的监听器:AnimatorUpdateListener

final ObjectAnimator alpha = ObjectAnimator.ofFloat(view2, "alpha", 1f, 0f, 1f).setDuration(5000);        alpha.addListener(new Animator.AnimatorListener() {            @Override            public void onAnimationStart(Animator animation) {            }            @Override            public void onAnimationEnd(Animator animation) {            }            @Override            public void onAnimationCancel(Animator animation) {            }            @Override            public void onAnimationRepeat(Animator animation) {            }        });

我们这里向动画增加监听器,但是也许很多时候我们并不想要监听那么多个事件,可能我只想要监听动画结束这一个事件,那么每次都要将四个接口全部实现一遍就显得非常繁琐。没关系,为此Android提供了一个适配器类,叫作AnimatorListenerAdapter,使用这个类就可以解决掉实现接口繁琐的问题了,如下所示:

        alpha.addListener(new AnimatorListenerAdapter() {            @Override            public void onAnimationEnd(Animator animation) {                super.onAnimationEnd(animation);            }        });

由于AnimatorListenerAdapter中已经将每个接口都实现好了,所以这里不用实现任何一个方法也不会报错,你可以选择自己需要的方法来进行实现,如上选择的是监听动画结束这个事件。

4.使用最多的是AnimatorSet和ObjectAnimator结合

更多资料

http://blog.csdn.net/singwhatiwanna/article/details/17841165

http://blog.csdn.net/guolin_blog/article/details/43816093

http://blog.csdn.net/iispring/article/details/50322625

http://blog.csdn.net/guolin_blog/article/details/44171115

http://www.jcodecraeer.com/a/anzhuokaifa/developer/2013/0312/1007.html

http://blog.csdn.net/sin90lzc/article/details/7520596

http://blog.csdn.net/yanbober/article/details/46481171

http://blog.csdn.net/guolin_blog/article/details/43536355

1 0
原创粉丝点击