本学习笔记主要来自启舰:
http://blog.csdn.net/harvic880925/article/details/50759059
在学习过程中融入了自己的理解和思路。
在实际使用中,都是使用ObjectAnimator的机率比较大。
但ValueAnimator和ObjectAnimator都只能单单实现一个动画,如果我们想要使用一个组合动画,比如边放大,边移动,边改变alpha值,ValueAnimator和ObjectAnimator动画距明显解决不了问题。对于这种组合型的动画,谷歌给我们提供了一个类AnimatorSet;这篇我们就着重来看看组合动画的实现方法吧。
一、AnimatorSet——playSequentially,playTogether
首先,AnimatorSet针对ValueAnimator和ObjectAnimator都是适用的,但一般而言,我们不会用到ValueAnimator的组合动画,所以我们这篇仅讲解ObjectAnimator下的组合动画实现。
在AnimatorSet中直接给为我们提供了两个方法playSequentially(按顺序)和playTogether(一起),playSequentially表示所有动画依次播放,playTogether表示所有动画一起开始。
1、playSequentially:将动画组装起来,逐个播放
我们先来看看playSequentially的声明:
public void playSequentially(Animator... items);
public void playSequentially(List<Animator> items);
第一个是我们最常用的,它的参数是可变长参数,也就是说我们可以传进去任意多个Animator对象。这些对象的动画会逐个播放。第二个构造方法,是传进去一个List< Animator>的列表。原理一样,也是逐个去取List中的动画对象,然后逐个播放。但使用起来稍微麻烦一些。
private void objAnimatorSequentially(){
ObjectAnimator backGround01 = ObjectAnimator.ofInt(mTvAnimator01,
"BackgroundColor", 0xffff0000, 0xff00ff00, 0xff0000ff);
backGround01.setRepeatCount(1);
ObjectAnimator backGround02 = ObjectAnimator.ofInt(mTvAnimator02,
"BackgroundColor", 0xff0000ff, 0xffff0000, 0xff00ff00);
backGround02.setRepeatCount(1);
ObjectAnimator translationY01 = ObjectAnimator.ofFloat(mTvAnimator01,
"translationY", 150, 280, 500,0);
translationY01.setRepeatCount(1);
ObjectAnimator translationY02 = ObjectAnimator.ofFloat(mTvAnimator02,
"translationY", 200, 320, 550,0);
translationY02.setRepeatCount(1);
animatorSet = new AnimatorSet();
animatorSet.playSequentially(translationY02,
backGround01, translationY01, backGround02);
//animatorSet.playTogether(translationY02,
backGround02, backGround01, translationY01);
animatorSet.setDuration(4000);
animatorSet.setInterpolator(new BounceInterpolator());
animatorSet.start();
}
上面代码运行的时候,会发现:动画是按照playSequentially里面的添加顺序执行的。如果playSequentially里面的添加顺序改成斜体下划线删除线的代码顺序,则会按照新的顺序执行。这正印证了playSequentially的含义:按顺序。
利用AnimatorSet的playSequentially方法将这三个动画组装起来,逐个播放。代码很好理解,不再细讲。
2、playTogether:
playTogether表示将所有动画一起播放 。
我们先来看看playTogether的声明:
public void playTogether(Animator... items);
public void playTogether(Collection<Animator> items);
用法示例:将上面1中有灰色底纹背景的代码改成下面一行代码,其他一切不变:
animatorSet.playTogether(translationY02, backGround02, backGround01, translationY01);
同样是上面的那四个动画,只是将playSequentially改为了playTogether;见加粗部分字体。执行过程中,会发现,无论怎么调整playTogether中动画的顺序,他们都会同时执行,正印证了playTogether的含义:同时,一起。
3、playSequentially,playTogether真正意义
playTogether和playSequentially在开始动画时,只是把每个控件的动画激活,至于每个控件自身的动画是否具有延时、是否无限循环,只与控件自身的动画设定有关,与playTogether、playSequentially无关。playTogether和playSequentially只负责到点激活动画。
总结:
第一:playTogether和playSequentially在激活动画后,控件的动画情况与它们无关,他们只负责定时激活控件动画。
第二:playSequentially只有控件的上一个动画做完以后,才会激活控件的下一个动画,如果控件的上一动画是无限循环,那控件的下一个动画甚至下一个控件的动画就别再指望了。
4、如何实现无限循环动画
是否无限循环主要是看动画本身,与playSequentially/playTogether无关!(这句话有bug。这个分两个情况来解释,一是playTogether:在playTogether情况下,每个控件的所有动画是同时执行的,没有先后顺序之分,所以是否无限循环主要是看动画本身,与playTogether无关;二是playSequentially在执行顺序上,见上面总结的第二条):
private void objAnimatorSequentially(){
ObjectAnimator backGround01 = ObjectAnimator.ofInt(mTvAnimator01,
"BackgroundColor", 0xffff0000, 0xff00ff00, 0xff0000ff);
backGround01.setRepeatCount(ValueAnimator.INFINITE);
ObjectAnimator backGround02 = ObjectAnimator.ofInt(mTvAnimator02,
"BackgroundColor", 0xff0000ff, 0xffff0000, 0xff00ff00);
backGround02.setRepeatCount(ValueAnimator.INFINITE);
ObjectAnimator translationY01 = ObjectAnimator.ofFloat(mTvAnimator01,
"translationY", 150, 280, 500,0);
translationY01.setRepeatCount(ValueAnimator.INFINITE);
ObjectAnimator translationY02 = ObjectAnimator.ofFloat(mTvAnimator02,
"translationY", 200, 320, 550,0);
translationY02.setRepeatCount(ValueAnimator.INFINITE);
animatorSet = new AnimatorSet();
animatorSet.playTogether(translationY02, backGround02,
backGround01, translationY01);
animatorSet.setDuration(2000);
animatorSet.setInterpolator(new BounceInterpolator());
animatorSet.start();
}
联合动画的无限循环,是通过ObjectAnimator的对象translationY01调用setRepeatCount()方法来设置的。AnimatorSet类并没有setRepeatCount()方法。
总之:
AnimatorSet没有setRepeatCount方法,只是通过playTogether和playSequentially负责指定什么时候开始动画,不干涉动画自己的运行过程。换言之:playTogether和playSequentially只是赛马场上的每个赛道的门,门打开以后,赛道上的那匹马怎么跑跟它没什么关系。
二、自由设置动画顺序——AnimatorSet.Builder
1、概述
我们讲了playTogether和playSequentially,分别能实现一起开始动画和逐个开始动画。但并不是非常自由的组合动画,比如我们有三个动画A,B,C我们想先播放C然后同时播放A和B。利用playTogether和playSequentially是没办法实现的,所以为了更方便的组合动画,谷歌的开发人员另外给我们提供一个类AnimatorSet.Builder;
private void objAnimatorBuilder(){
ObjectAnimator backGround01 = ObjectAnimator.ofInt(mTvAnimator01,
"BackgroundColor", 0xffff0000, 0xff00ff00, 0xff0000ff);
backGround01.setRepeatCount(1);
ObjectAnimator backGround02 = ObjectAnimator.ofInt(mTvAnimator02,
"BackgroundColor", 0xff0000ff, 0xffff0000, 0xff00ff00);
backGround02.setRepeatCount(1);
ObjectAnimator translationY01 = ObjectAnimator.ofFloat(mTvAnimator01,
"translationY", 150, 280, 500,0);
translationY01.setRepeatCount(1);
ObjectAnimator translationY02 = ObjectAnimator.ofFloat(mTvAnimator02,
"translationY", 200, 320, 550,0);
translationY02.setRepeatCount(1);
mAnimatorset = new AnimatorSet();
AnimatorSet.Builder mBuilder = mAnimatorset.play(backGround01).with(translationY01)
.after(backGround02).before(translationY02);
mAnimatorset.setDuration(3000);
mAnimatorset.setTarget(mTvAnimator02);
mAnimatorset.start();
}
首先是构造一个AnimatorSet对象。然后调用animatorSet.play(backGround01)方法生成一个AnimatorSet.Builder对象并加入要先播放的动画backGround01,再调用builder.with()添加第二个要播放的动画translationY01,就实现了两个控件同时做动画了,多么神奇。
2、AnimatorSet.Builder方法含义详解
从上面的代码中,我们可以看到AnimatorSet.Builder是通过animatorSet.play(backGround01)生成的,这是生成AnimatorSet.Builder对象的唯一途径!
//调用AnimatorSet中的play方法是获取AnimatorSet.Builder对象的唯一途径
//表示要播放哪个动画
public Builder play(Animator anim)
//和前面动画一起执行
public Builder with(Animator anim)
//执行前面的动画后才执行该动画
public Builder before(Animator anim)
//先执行after方法里面的动画再执行前面动画
public Builder after(Animator anim)
//延迟n毫秒之后执行动画
public Builder after(long delay)
如下:
private void objAnimatorBuilder(){
ObjectAnimator backGround01 = ObjectAnimator.ofInt(mTvAnimator01,
"BackgroundColor", 0xffff0000, 0xff00ff00, 0xff0000ff);
backGround01.setRepeatCount(1);
ObjectAnimator backGround02 = ObjectAnimator.ofInt(mTvAnimator02,
"BackgroundColor", 0xff0000ff, 0xffff0000, 0xff00ff00);
backGround02.setRepeatCount(1);
ObjectAnimator translationY01 = ObjectAnimator.ofFloat(mTvAnimator01,
"translationY", 150, 280, 500,0);
translationY01.setRepeatCount(1);
ObjectAnimator translationY02 = ObjectAnimator.ofFloat(mTvAnimator02,
"translationY", 200, 320, 550,0);
translationY02.setRepeatCount(1);
mAnimatorset = new AnimatorSet();
AnimatorSet.Builder mBuilder1 = mAnimatorset.play(backGround01);
mBuilder1.with(translationY01);
mBuilder1.after(backGround02);//after括号里面的动画先执行。
mBuilder1.before(translationY02);
mAnimatorset.setDuration(3000);
mAnimatorset.setTarget(mTvAnimator02);
mAnimatorset.start();
}
上面动画执行顺序是:先执行backGround02,然后translationY01和backGround01同时执行,最后再是translationY02;这里的执行顺序与after、before代码的先后顺序无关,只跟里面的动画对象有关。
由于每个方法的返回值都是Builder对象,所以我们是依然可以直接调用Builder的所有方法的,所以就可以用串行的方式把他们一行串起来,所以上面的代码我们也可以写成下面的简化方式:
AnimatorSet.Builder mBuilder = mAnimatorset
.play(backGround01).with(translationY01)
.after(backGround02).before(backGround01);
三、AnimatorSet监听器
在AnimatorSet中也可以添加监听器,对应的监听器为:
public static interface AnimatorListener {
// 当AnimatorSet开始时调用
void onAnimationStart(Animator animation);
// 当AnimatorSet结束时调用
void onAnimationEnd(Animator animation);
// 当AnimatorSet被取消时调用
void onAnimationCancel(Animator animation);
// 当AnimatorSet重复时调用,由于AnimatorSet没有设置repeat的方法,
// 所以这个方法永远不会被调用
void onAnimationRepeat(Animator animation);
}
添加方法为:
//这与前面给ValueAnimator添加监听是一致的。
public void addListener(AnimatorListener listener);
AnimatorSet的监听总结:
1、AnimatorSet的监听方法也只是用来监听AnimatorSet的状态的,与其中的动画无关;
2、AnimatorSet中没有设置循环的方法,所以AnimatorSet监听器中永远无法运行到onAnimationRepeat()中!
3、有关如何实现无限循环的问题,解决方式是给每个动画独立设置onAnimationRepeat();
四、通用方法逐个设置与AnimatorSet设置的区别
1、概述及示例
在AnimatorSet中还有几个方法:
//设置单次动画时长
public AnimatorSet setDuration(long duration);
//设置加速器
public void setInterpolator(TimeInterpolator interpolator) ;
//设置ObjectAnimator动画目标控件
public void setTarget(Object target) ;
在ObjectAnimator中也都有这几个方法。那在AnimatorSet中设置与在单个ObjectAnimator中设置有什么区别呢?
————区别就是: 如果AnimatorSet中没有设置,那么就以ObjectAnimator中的设置为准。如果AnimatorSet中设置以后,ObjectAnimator中的设置就会无效,即: 在AnimatorSet中设置以后,会覆盖单个ObjectAnimator中的设置。
2、setTarget(Object target):
只要通过AnimatorSet的setTartget方法设置了目标控件,那么单个动画中的目标控件都以AnimatorSet设置的为准 ;如前面的Builder顺序动画,我们给mTvAnimator01和mTvAnimator02设置了改变背景色和上下移动的动画。但由于我们通过mAnimatorset.setTarget(mTvAnimator02);将各个动画的目标控件设置为mTvAnimator02,所以mTvAnimator01将不会有任何动画,所有的动画都会发生在mTvAnimator02上。
五、AnimatorSet之setStartDelay(long startDelay)
//API提供的setStartDelay方法
//设置延时开始动画时长
public void setStartDelay(long startDelay)
上面我们讲了,当AnimatorSet所拥有的方法与单个动画所拥有的方法冲突时,就以AnimatorSet设置为准。但唯一的例外就是setStartDelay。setStartDelay方法不会覆盖单个动画的延时,而且仅针对性的延长AnimatorSet的激活时间,单个动画的所设置的setStartDelay仍对单个动画起作用。
ObjectAnimator tv1TranslateY = ObjectAnimator.ofFloat(mTv1,
"translationY", 0, 400, 0);
ObjectAnimator tv2TranslateY = ObjectAnimator.ofFloat(mTv2,
"translationY", 0, 400, 0);
tv2TranslateY.setStartDelay(2000);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(tv1TranslateY).with(tv2TranslateY);
animatorSet.setStartDelay(2000);
animatorSet.setDuration(2000);
animatorSet.start();
在这个动画中,我们首先给AnimatorSet设置了延时,所以AnimatorSet会在2000毫秒以后,才会执行start()方法。另外我们还给tv2设置了延时2000毫秒,所以在动画开始后,mTv1会直接运动,但mTv2要等2000毫秒以后,才会开始运动。也就是mTv2一共被延时了4000ms的时间;
另外,AnimatorSet的延时是仅针对性的延长AnimatorSet激活时间的,对单个动画的延时设置没有影响。
注意:注意::注意:::
上面的动画顺序:
animatorSet.play(tv1TranslateY).with(tv2TranslateY);
这里将动画顺序调换一下:
animatorSet.play(tv2TranslateY).with(tv1TranslateY);
看会是什么结果呢?
按说这里的效果应该与上个的效果是一样的才对,即在AnimatorSet被激活以后,mTv1应该立即运行,等2000毫秒后mTv2才开始运行。但这里的效果却是过了一段时间以后,mTv1和mTv2一起运行!
这是因为:AnimatorSet真正激活延时 = AnimatorSet.startDelay + 第一个动画.startDelay;也就是说AnimatorSet被激活的真正延时等于它本身设置的setStartDelay(2000)延时再加上第一个动画的延时;
结论:
1、AnimatorSet的延时是仅针对性的延长AnimatorSet激活时间的,对单个动画的延时设置没有影响。
2、AnimatorSet真正激活延时 = AnimatorSet.startDelay+第一个动画.startDelay
3、在AnimatorSet激活之后,第一个动画绝对是会开始运行的,后面的动画则根据自己是否延时自行处理。