ValueAnimator 和 ObjectAnimator 都只能单单实现一个动画,那如果我们想要使用一个组合动画,就需要用到 AnimatorSet。
AnimatorSet 针对 ValueAnimator 和 ObjectAnimator 都是适用的,但一般而言,我们不会用到 ValueAnimator 的组合动画,所以我们这篇仅讲解 ObjectAnimator 下的组合动画实现。
在 AnimatorSet 中直接给为我们提供了两个方法 playSequentially 和 playTogether,playSequentially 表示所有动画依次播放,playTogether 表示所有动画一起开始。
3.5.1 playSequentially() 与 playTogether() 函数
1. playSequentially()
|
|
这里有两种声明,第一个是我们最常用的,它的参数是可变长参数,也就是说我们可以传进去任意多个 Animator 对象。这些对象的动画会逐个播放。第二个构造函数,是传进去一个 List<Animator> 的列表。原理一样,也是逐个去取 List 中的动画对象,然后逐个播放。
示例:
|
|
布局 act_main.xml:
2. playTogether()
|
|
将上例中的代码更改为:
即三个动画同时播放。
3、playSequentially 和 playTogether 函数的真正意义
想必大家都看到赛马,在赛马开始前,每个马都会被放在起点的小门后面,到点了,门打开,马开始一起往前跑。而假如我们把每匹马看做是一个动画,那我们的 playTogether 就相当于赛马场里每个赛道上门的意义(当比赛开始时,每个赛道上的门会打开,马就可以开始比赛了);也就是说,playTogether 只是一个时间点上的一起开始,对于开始后,各个动画怎么操作就是他们自己的事了,至于各个动画结不结束也是他们自已的事了。所以最恰当的描述就是门只负责打开,打开之后马咋跑,门也管不着,最后,马回不回来跟门也没啥关系。门的责任只是到点就打开而已。放在动画上,就是在激活动画之后,动画开始后的操作只是动画自己来负责。至于动画结不结束,也只有动画自己知道。
而 playSequentially 的意义就是当一匹马回来以后,再放另一匹。那如果上匹马永远没回来,那下一匹马也永远不会被放出来。放到动画上,就是把激活一个动画之后,动画之后的操作就是动画自己来负责了,这个动画结束之后,再激活下一个动画。如果上一个动画没有结束,那下一个动画就永远也不会被激活。
首先用 playTogether 来看个例子:
在这个例子中,我们将 tv1TranslateY 开始延迟 1000 毫秒开始,并设为无限循环。tv2TranslateY 设为开始延迟 1000 毫秒。而tv1BgAnimator 则是没有任何设置,所以是默认直接开始。我们来看效果图:
将上述例子做如下更改:
使用 playSequentially 来逐个播放这三个动画,首先是tv1BgAnimator,动画结束之后,激活 tv1TranslateY。不过由于设置了延时,故 1000 毫秒再开始,而且该动画会无限循环。无限循环也就是说它永远也不会结束。那么第三个动画 tv2TranslateY 也永远不会开始。效果图如下:
总结:
- playTogether 和 playSequentially 在激活动画后,控件的动画情况与它们无关,他们只负责定时激活控件动画。
- playSequentially 只有上一个控件做完动画以后,才会激活下一个控件的动画,如果上一控件的动画是无限循环,那下一个控件就别再指望能做动画了。
4. 实现无限循环动画
因为 AnimatorSet 中没有设置循环次数的函数,所以得为每个动画设置了无限循环,并且只能用 playTogether() 函数。
3.5.2 AnimatorSet.Builder
1. 概述
playTogether 和 playSequentially,分别能实现一起开始动画和逐个开始动画。但并不是非常自由的组合动画,比如我们有三个动画 A、B、C,我们想先播放 C 然后同时播放 A 和 B。利用 playTogether 和 playSequentially 是没办法实现的,所以为了更方便的组合动画,谷歌的开发人员另外给我们提供一个类 AnimatorSet.Builder。
我们这里使用 AnimatorSet.Builder 实现两个控件一同开始动画。
2. AnimatorSet.Builder 的函数
从上面的代码中,我们可以看到 AnimatorSet.Builder 是通过 animatorSet.play(tv1BgAnimator) 生成的,这是生成AnimatorSet.Builder对象的唯一途径!
play(Animator anim) 表示当前在播放哪个动画,另外的 with(Animator anim)、before(Animator anim)、after(Animator anim) 都是以 play 中的当前所播放的动画为基准的。
比如,当 play(playAnim) 与 before(beforeAnim) 共用,则表示在播放 beforeAnim 之前,先播放 playAnim 动画;同样,当 play(playAnim) 与 after(afterAnim) 共用时,则表示在在播放 afterAnim 动画之后,再播放 playAnim 动画。
每个函数的返回值都是 Builder 对象,于是可以使用串行方式使用它们:
3.5.3 AnimatorSet 监听器
在 AnimatorSet 中也可以添加监听器,对应的监听器为:
示例:
日志输出如下:
总结一下 AnimatorSet 的监听:
AnimatorSet 的监听函数也只是用来监听 AnimatorSet 的状态的,与其中的动画无关。
AnimatorSet 中没有设置循环的函数,所以 AnimatorSet 监听器中永远无法运行到 onAnimationRepeat() 中。
3.5.4 常用函数
1. 概述
在 AnimatorSet 中还有几个函数:
在 AnimatorSet 中设置以后,会覆盖单个 ObjectAnimator 中的设置;即如果 AnimatorSet 中没有设置,那么就以 ObjectAnimator 中的设置为准。如果 AnimatorSet 中设置以后,ObjectAnimator 中的设置就会无效。
下面我们简单举个例子来看下:
在第这个例子中,我们通过 animatorSet.setDuration(2000); 设置为所有动画单词运动时长为 2000 毫秒,虽然我们给 tv1TranslateY 设置了单次动画时长为 tv1TranslateY.setDuration(500000000); 但由于 AnimatorSet 设置了 setDuration(2000) 这个参数以后,单个动画的时长设置将无效。所以每个动画的时长为 2000 毫秒。
但我们这里还分别给 tv1 和 tv2 设置了加速器,但并没有给 AnimatorSet 设置加速器,那么 tv1、tv2 将按各自加速器的表现形式做动画。同样,如果我们给 AnimatorSet 设置上了加速器,那么单个动画中所设置的加速器都将无效,以 AnimatorSet 中的加速器为准。
2. setTarget(Object target) 函数
|
|
这个函数是用来设置目标控件的,也就是说,只要通过 AnimatorSet 的 setTartget 函数设置了目标控件,那么单个动画中的目标控件都以 AnimatorSet 设置的为准。
在这段代码中,我们给 tv1 设置了改变背景色,给 tv2 设置了上下移动。但由于我们通过 animatorSet.setTarget(mTv2); 将各个动画的目标控件设置为 mTv2,所以 tv1 将不会有任何动画,所有的动画都会发生在 tv2 上。
3. setStartDelay(long startDelay) 函数
|
|
上面我们讲了,当 AnimatorSet 所拥有的函数与单个动画所拥有的函数冲突时,就以 AnimatorSet 设置为准。但唯一的例外就是 setStartDelay。
- AnimatorSet 的延时是仅针对性的延长 AnimatorSet 激活时间的,对单个动画的延时设置没有影响。
- AnimatorSet 真正激活延时 = AnimatorSet.startDelay + 第一个动画.startDelay
- 在 AnimatorSet 激活之后,第一个动画绝对是会开始运行的,后面的动画则根据自己是否延时自行处理。
3.5.5 示例:路径动画
代码 MainActivity.java:
布局文件 act_main:
引用资源 circle.xml:
主题样式 style.xml: