Android属性动画彻底解析(一)

                                       Android属性动画彻底解析(一)

1.为何要引入属性动画?

android提供了几种动画类型:View Animation 、Drawable Animation 、Property Animation 。View Animation至关简单,不过只能支持简单的缩放、平移、旋转、透明度基本的动画,且有必定的局限性。好比:你但愿View有一个颜色的切换动画;你但愿可使用3D旋转动画;你但愿当动画中止时,View的位置就是当前的位置;这些View Animation都没法作到。View Animation只能对“对View进行操做”,没错,补间动画是只可以做用在View上的。补间动画还有一个致命的缺陷,就是它只是改变了View的显示效果而已,而不会真正去改变View的属性。android

2.相关API

Property Animation故名思议就是经过动画的方式改变对象的属性了,咱们首先须要了解几个属性:ide

Duration动画的持续时间,默认300ms。测试

Time interpolation:时间差值,乍一看不知道是什么,可是我说LinearInterpolator、AccelerateDecelerateInterpolator,你们必定知道是干吗的了,定义动画的变化率。动画

Repeat count and behavior:重复次数、以及重复模式;能够定义重复多少次;重复时从头开始,仍是反向。ui

Animator sets: 动画集合,你能够定义一组动画,一块儿执行或者顺序执行。lua

Frame refresh delay:帧刷新延迟,对于你的动画,多久刷新一次帧;默认为10ms,但最终依赖系统的当前状态;基本不用管。spa

相关的类设计

ObjectAnimator  动画的执行类,后面详细介绍日志

ValueAnimator 动画的执行类,后面详细介绍 code

AnimatorSet 用于控制一组动画的执行:线性,一块儿,每一个动画的前后执行等。

AnimatorInflater 用户加载属性动画的xml文件

TypeEvaluator  类型估值,主要用于设置动画操做属性的值。

TimeInterpolator 时间插值,上面已经介绍。

总的来讲,属性动画就是,动画的执行类来设置动画操做的对象的属性、持续时间,开始和结束的属性值,时间差值等,而后系统会根据设置的参数动态的变化对象的属性。

3.ValueAnimator

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

可是ValueAnimator的用法却一点都不复杂,咱们先从最简单的功能看起吧,好比说想要将一个值从0平滑过渡到1,时长1000毫秒,就能够这样写:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ValueAnimator animator = ValueAnimator.ofFloat(0f,1f);
        animator.setDuration(1000);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                Float currentValue = (Float) valueAnimator.getAnimatedValue();
                Log.d("ValueAnimator","current Value is"+currentValue);
            }
        });
        animator.start();
    }

}

运行结果如图:

从打印日志的值咱们就能够看出,ValueAnimator确实已经在正常工做了,值在1000毫秒的时间内从0平滑过渡到了1,而这个计算工做就是由ValueAnimator帮助咱们完成的。另外ofFloat()方法当中是能够传入任意多个参数的,所以咱们还能够构建出更加复杂的动画逻辑,好比说将一个值在6秒内从0过渡到6,再过渡到4,再过渡到11,就能够这样写:

ValueAnimator animator = ValueAnimator.ofFloat(0f,6f,4f,11f);
        animator.setDuration(1000);

animator.start();

固然也许你并不须要小数位数的动画过渡,可能你只是但愿将一个整数值从0平滑地过渡到80,那么也很简单,只须要调用ValueAnimator的ofInt()方法就能够了,以下所示:

ValueAnimator anim = ValueAnimator.ofInt(0, 80);

ValueAnimator当中最经常使用的应该就是ofFloat()和ofInt()这两个方法了,另外还有一个ofObject()方法,我会在下篇文章进行讲解。

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

3.ObjectAnimator

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

不过虽然说ObjectAnimator会更加经常使用一些,可是它实际上是继承自ValueAnimator的,底层的动画实现机制也是基于ValueAnimator来完成的,所以ValueAnimator仍然是整个属性动画当中最核心的一个类。那么既然是继承关系,说明ValueAnimator中可使用的方法在ObjectAnimator中也是能够正常使用的,它们的用法也很是相似,这里若是咱们想要将一个TextView在5秒中内从常规变换成全透明,再从全透明变换成常规,就能够这样写:

/**
 * quagungle 2016/11/22
 */

public class MainActivity extends AppCompatActivity {
    private TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(tv_test);

    }

    public void test(View view){
        ObjectAnimator animator = ObjectAnimator.ofFloat(textView,"alpha",1f,0f,1f);
        animator.setDuration(5000);
        animator.start();
    }
}

能够看到,咱们仍是调用了ofFloat()方法来去建立一个ObjectAnimator的实例,只不过ofFloat()方法当中接收的参数有点变化了。这里第一个参数要求传入一个object对象,咱们想要对哪一个对象进行动画操做就传入什么,这里我传入了一个textview。第二个参数是想要对该对象的哪一个属性进行动画操做,因为咱们想要改变TextView的不透明度,所以这里传入"alpha"。后面的参数就是不固定长度了,想要完成什么样的动画就传入什么值,这里传入的值就表示将TextView从常规变换成全透明,再从全透明变换成常规。以后调用setDuration()方法来设置动画的时长,而后调用start()方法启动动画,效果以下图所示:

学会了这一个用法以后,其它的用法咱们就能够触类旁通了,那好比说咱们想要将TextView进行一次360度的旋转,就能够这样写:

public void test(View view){
        ObjectAnimator animator = ObjectAnimator.ofFloat(textView,"rotation",0f,360f);
        animator.setDuration(5000);
        animator.start();
    }

能够看到,这里咱们将第二个参数改为了"rotation",而后将动画的初始值和结束值分别设置成0和360。因为是静态的图片,效果图就不展现,有兴趣能够本身去写写。

 

那么若是想要将TextView先向左移出屏幕,而后再移动回来,就能够这样写:

public void test(View view){
        Float currenttranslateX = textView.getTranslationX();
        ObjectAnimator animator = ObjectAnimator.ofFloat(textView,"translationX",currenttranslateX,-500f,currenttranslateX);
        animator.setDuration(5000);
        animator.start();
    }

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

 

而后咱们还能够TextView进行缩放操做,好比说将TextView在水平方向上放大3倍再还原,就能够这样写:

public void test(View view){
        Float currenttranslateX = textView.getTranslationX();
        ObjectAnimator animator = ObjectAnimator.ofFloat(textView,"scaleX",1f,3f,1f);
        animator.setDuration(5000);
        animator.start();
    }

 

到目前为止,ObjectAnimator的用法还算是至关简单吧,可是我相信确定会有很多朋友如今内心都有一样一个疑问,就是ofFloat()方法的第二个参数到底能够传哪些值呢?目前咱们使用过了alpha、rotation、translationX和scaleY这几个值,分别能够完成淡入淡出、旋转、水平移动、垂直缩放这几种动画,那么还有哪些值是可使用的呢?其实这个问题的答案很是玄乎,就是咱们能够传入任意的值到ofFloat()方法的第二个参数当中。任意的值?相信这很出乎你们的意料吧,但事实就是如此。由于ObjectAnimator在设计的时候就没有针对于View来进行设计,而是针对于任意对象的,它所负责的工做就是不断地向某个对象中的某个属性进行赋值,而后对象根据属性值的改变再来决定如何展示出来。

那么好比说咱们调用下面这样一段代码:

ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f);

其实这段代码的意思就是ObjectAnimator会帮咱们不断地改变textview对象中alpha属性的值,从1f变化到0f。而后textview对象须要根据alpha属性值的改变来不断刷新界面的显示,从而让用户能够看出淡入淡出的动画效果。

那么textview对象中是否是有alpha属性这个值呢?没有,不只textview没有这个属性,连它全部的父类也是没有这个属性的!这就奇怪了,textview当中并无alpha这个属性,ObjectAnimator是如何进行操做的呢?其实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当中去找一下。

4.组合动画

独立的动画可以实现的视觉效果毕竟是至关有限的,所以将多个动画组合到一块儿播放就显得尤其重要。幸运的是,Android团队在设计属性动画的时候也充分考虑到了组合动画的功能,所以提供了一套很是丰富的API来让咱们将多个动画组合到一块儿。

实现组合动画功能主要须要借助AnimatorSet这个类,这个类提供了一个play()方法,若是咱们向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimator)将会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括如下四个方法:

  • after(Animator anim)   将现有动画插入到传入的动画以后执行
  • after(long delay)   将现有动画延迟指定毫秒后执行
  • before(Animator anim)   将现有动画插入到传入的动画以前执行
  • with(Animator anim)   将现有动画和传入的动画同时执行

好的,有了这四个方法,咱们就能够完成组合动画的逻辑了,那么好比说咱们想要让TextView先从屏幕外移动进屏幕,而后开始旋转360度,旋转的同时进行淡入淡出操做,就能够这样写:

public void test(View view){
        Float currenttranslateX = textView.getTranslationX();
        ObjectAnimator moveIn = ObjectAnimator.ofFloat(textView,"translationX",currenttranslateX,-500f,0f);
        ObjectAnimator rotation = ObjectAnimator.ofFloat(textView,"rotation",0f,360f);
        ObjectAnimator alpha = ObjectAnimator.ofFloat(textView,"alpha",1f,0f,1f);
        AnimatorSet animSet = new AnimatorSet();
        animSet.play(rotation).with(alpha).after(moveIn);
        animSet.setDuration(5000);
        animSet.start();
    }

能够看到,这里咱们先是把三个动画的对象所有建立出来,而后new出一个AnimatorSet对象以后将这三个动画对象进行播放排序,让旋转和淡入淡出动画同时进行,并把它们插入到了平移动画的后面,最后是设置动画时长以及启动动画。

5.Animator监听器

在不少时候,咱们但愿能够监听到动画的各类事件,好比动画什么时候开始,什么时候结束,而后在开始或者结束的时候去执行一些逻辑处理。这个功能是彻底能够实现的,Animator类当中提供了一个addListener()方法,这个方法接收一个AnimatorListener,咱们只须要去实现这个AnimatorListener就能够监听动画的各类事件了。

你们已经知道,ObjectAnimator是继承自ValueAnimator的,而ValueAnimator又是继承自Animator的,所以无论是ValueAnimator仍是ObjectAnimator都是可使用addListener()这个方法的。另外AnimatorSet也是继承自Animator的,所以addListener()这个方法算是个通用的方法。

添加一个监听器的代码以下所示:

anim.addListener(new AnimatorListener() {  
    @Override  
    public void onAnimationStart(Animator animation) {  
    }  
  
    @Override  
    public void onAnimationRepeat(Animator animation) {  
    }  
  
    @Override  
    public void onAnimationEnd(Animator animation) {  
    }  
  
    @Override  
    public void onAnimationCancel(Animator animation) {  
    }  
});

能够看到,咱们须要实现接口中的四个方法,onAnimationStart()方法会在动画开始的时候调用,onAnimationRepeat()方法会在动画重复执行的时候调用,onAnimationEnd()方法会在动画结束的时候调用,onAnimationCancel()方法会在动画被取消的时候调用。

可是也许不少时候咱们并不想要监听那么多个事件,可能我只想要监听动画结束这一个事件,那么每次都要将四个接口所有实现一遍就显得很是繁琐。不要紧,为此Android提供了一个适配器类,叫做AnimatorListenerAdapter,使用这个类就能够解决掉实现接口繁琐的问题了,以下所示:

anim.addListener(new AnimatorListenerAdapter() {  
});

这里咱们向addListener()方法中传入这个适配器对象,因为AnimatorListenerAdapter中已经将每一个接口都实现好了,因此这里不用实现任何一个方法也不会报错。那么若是我想监听动画结束这个事件,就只须要单独重写这一个方法就能够了,以下所示:

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

本身在测试过程当中:不知道咋回事onAnimationStart()并无被调用。

6.使用XML编写动画

咱们可使用代码来编写全部的动画功能,这也是最经常使用的一种作法。不过,过去的补间动画除了使用代码编写以外也是可使用XML编写的,所以属性动画也提供了这一功能,即经过XML来完成和代码同样的属性动画功能。

经过XML来编写动画可能会比经过代码来编写动画要慢一些,可是在重用方面将会变得很是轻松,好比某个将通用的动画编写到XML里面,咱们就能够在各个界面当中轻松去重用它。

若是想要使用XML来编写动画,首先要在res目录下面新建一个animator文件夹,全部属性动画的XML文件都应该存放在这个文件夹当中。而后在XML文件中咱们一共可使用以下三种标签:

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

那么好比说咱们想要实现一个从0到100平滑过渡的动画,在XML当中就能够这样写:

<animator xmlns:android="http://schemas.android.com/apk/res/android"  
    android:valueFrom="0"  
    android:valueTo="100"  
    android:valueType="intType"/>

而若是咱们想将一个视图的alpha属性从1变成0,就能够这样写:

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"  
    android:valueFrom="1"  
    android:valueTo="0"  
    android:valueType="floatType"  
    android:propertyName="alpha"/>

其实XML编写动画在可读性方面仍是挺高的,上面的内容相信不用我作解释你们也都看得懂吧。

另外,咱们也可使用XML来完成复杂的组合动画操做,好比将一个视图先从屏幕外移动进屏幕,而后开始旋转360度,旋转的同时进行淡入淡出操做,就能够这样写:

<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" >  
    </objectAnimator>  
  
    <set android:ordering="together" >  
        <objectAnimator  
            android:duration="3000"  
            android:propertyName="rotation"  
            android:valueFrom="0"  
            android:valueTo="360"  
            android:valueType="floatType" >  
        </objectAnimator>  
  
        <set android:ordering="sequentially" >  
            <objectAnimator  
                android:duration="1500"  
                android:propertyName="alpha"  
                android:valueFrom="1"  
                android:valueTo="0"  
                android:valueType="floatType" >  
            </objectAnimator>  
            <objectAnimator  
                android:duration="1500"  
                android:propertyName="alpha"  
                android:valueFrom="0"  
                android:valueTo="1"  
                android:valueType="floatType" >  
            </objectAnimator>  
        </set>  
    </set>  
  
</set>

这段XML实现的效果和咱们刚才经过代码来实现的组合动画的效果是如出一辙的,每一个参数的含义都很是清楚,相信你们都是一看就懂,我就再也不一一解释了。

最后XML文件是编写好了,那么咱们如何在代码中把文件加载进来并将动画启动呢?只需调用以下代码便可:

Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file);  
animator.setTarget(view);  
animator.start();

到此属性动画的基本介绍已经完毕!

相关文章
相关标签/搜索