Android 一共有多少种动画

动画种类

Android 动画可以归纳为以下几种:

视图动画(View 动画)

帧动画(Frame 动画、Drawable 动画)

属性动画

触摸反馈动画(Ripple Effect)

揭露动画(Reveal Effect)

转场动画 & 共享元素(Activity 切换动画)

视图状态动画(Animate View State Changes)

矢量图动画(Vector 动画)

约束布局实现的关键帧动画(ConstraintSet 动画)

上面动画分类是个人通过每种动画种类概念的独立性来划分的,目前能想到的只有这么多,如果有所遗漏大家可以指点出来以供我后续完善。

可能有很多人迅速的反应出,缺少了目前使用也相对较多的airbnb/lottie-android动画。不可置疑,Lottie 库目前在 Android 开发中尤其复杂动画效果上地位显著。但我们今天要是的 Android 原生上为我们提供的能使用的动画方式,Lottie 动画今天暂且搁置。同时对于 RecyclerView item 加载动画今天也暂且不提。我们且把这些动画归为其他,并不是把它们遗忘了。

详尽教程

对于上面列举的动画种类,可能大家对部分较常用的动画早已熟练应用,比如 View 动画、属性动画等。而对部分较少使用(比如 揭露动画)、或者常常使用却从未意识到它也属于动画的一种(比如 触摸反馈动画)知道的并不是那么全面。“那么今天就一一为大家详细讲解每种动画的概念”,那是不可能滴~~,就这么点篇幅,这么可能把每种动画都细说下来。

要这些动画一一梳理清晰,那将是一项浩大的工作,而我已经为大家总结成了一个《详尽 Android 动画系列教程》,大家可以到

https://github.com/OCNYang/Android-Animation-Set

进行查看,由于动画知识点涉及的太多而教程详细程度令人发指,大家可以收藏起来慢慢查看。另外总结的教程中每种动画都提供了动画示例,大家可以结合源码细细品味。(上面总结的系列教程,大多数都是借用前人总结的教程,选用的都是针对每种动画网上流传的最详细全面的教程,在梳理中对部分错误也进行了更正。)

那今天的任务是什么呢?接下来主要通过粗略的介绍来讲解每种动画在开发中都适用在哪种场景。

视图动画(View 动画)

自从有了属性动画,View 动画的处境就非常凄凉,但有时我们需要的仅仅就是简易的动画效果,那我们使用 View 动画起来就十分便捷。

View 动画的一个特点就是,他的动画仅仅是动的 View 的绘制地方,View 真正的位置并没有一起动画。

最后,补间动画还有一个致命的缺陷,就是它只是改变了View的显示效果而已,而不会真正去改变View的属性。什么意思呢?比如说,现在屏幕的左上角有一个按钮,然后我们通过补间动画将它移动到了屏幕的右下角,现在你可以去尝试点击一下这个按钮,点击事件是绝对不会触发的,因为实际上这个按钮还是停留在屏幕的左上角,只不过补间动画将这个按钮绘制到了屏幕的右下角而已。

ObjectAnimator

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

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

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度的旋转,就可以这样写:

ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);

animator.setDuration(5000);

animator.start();

可以看到,这里我们将第二个参数改成了"rotation",然后将动画的初始值和结束值分别设置成0和360,现在运行一下代码,效果如下图所示:

那么如果想要将TextView先向左移出屏幕,然后再移动回来,就可以这样写:

float curTranslationX = textview.getTranslationX();

ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "translationX", curTranslationX, -500f, curTranslationX);

animator.setDuration(5000);

animator.start();

这里我们先是调用了TextView的getTranslationX()方法来获取到当前TextView的translationX的位置,然后ofFloat()方法的第二个参数传入"translationX",紧接着后面三个参数用于告诉系统TextView应该怎么移动,现在运行一下代码,效果如下图所示:

然后我们还可以TextView进行缩放操作,比如说将TextView在垂直方向上放大3倍再还原,就可以这样写:

ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "scaleY", 1f, 3f, 1f);

animator.setDuration(5000);

animator.start();

这里将ofFloat()方法的第二个参数改成了"scaleY",表示在垂直方向上进行缩放,现在重新运行一下程序,效果如下图所示:

到目前为止,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当中去找一下。

组合动画

独立的动画能够实现的视觉效果毕竟是相当有限的,因此将多个动画组合到一起播放就显得尤为重要。幸运的是,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度,旋转的同时进行淡入淡出操作,就可以这样写:

ObjectAnimator moveIn = ObjectAnimator.ofFloat(textview, "translationX", -500f, 0f);

ObjectAnimator rotate = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);

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

AnimatorSet animSet = new AnimatorSet();

animSet.play(rotate).with(fadeInOut).after(moveIn);

animSet.setDuration(5000);

animSet.start();

可以看到,这里我们先是把三个动画的对象全部创建出来,然后new出一个AnimatorSet对象之后将这三个动画对象进行播放排序,让旋转和淡入淡出动画同时进行,并把它们插入到了平移动画的后面,最后是设置动画时长以及启动动画。运行一下上述代码,效果如下图所示:

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) {

}

});

使用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();

调用AnimatorInflater的loadAnimator来将XML动画文件加载进来,然后再调用setTarget()方法将这个动画设置到某一个对象上面,最后再调用start()方法启动动画就可以了,就是这么简单。

好的,通过本篇文章的学习,我相信大家已经对属性动画的基本用法已经有了一个相当不错的认识,并把最常用的一些功能都掌握好了,那么本篇文章的内容就到这里,下篇文章当中会继续介绍属性动画,讲解ValueAnimator和ObjectAnimator的高级用法,感兴趣的朋友请继续阅读 Android属性动画完全解析(中),ValueAnimator和ObjectAnimator的高级用法 。

————————————————

版权声明:本文为CSDN博主「guolin」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/guolin_blog/article/details/43536355

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容