前言:把握生命里的每一分钟,全力以赴心中的梦。不经历风雨,怎么见彩虹,没有人能随随便便成功。 ----《真心英雄》
我们在上一篇文章中已经详细讲解了ObjectAnimator的使用,但是ValueAnimator和ObjectAnimator只能简单实现一个动画,如果我要实现一个组合动画呢?比如一边缩放,一边移动那怎么办?系统也提供了一个AnimatorSet属性动画组合类,由于ValueAnimator比较少用到,这里就讲解ObjectAnimator配合AnimatorSet的使用。另外,这里最后增加了PropertyValuesHolder用法的讲解。
AnimatorSet继承自Animator类,Animator类有的属性AnimatorSet都有,这里列出常用的一部分函数:
/**
* 开启动画
*/
void start()
/**
* 设置动画时间
*/
AnimatorSet setDuration(long duration)
/**
* 退出动画
*/
void cancel()
/**
* 设置插值器
*/
void setInterpolator(TimeInterpolator interpolator)
监听器:
/**
* 监听器一:监听动画变化时四个状态
*/
public static interface AnimatorListener {
void onAnimationStart(Animator animation);
void onAnimationEnd(Animator animation);
void onAnimationCancel(Animator animation);
void onAnimationRepeat(Animator animation);
}
//添加方法为:public void addListener(AnimatorListener listener)
/**
*监听器二:监听动画暂停和暂停后再恢复的状态
*/
public static interface AnimatorPauseListener {
void onAnimationPause(Animator animation);
void onAnimationResume(Animator animation);
}
//添加方法:public void addPauseListener(AnimatorPauseListener listener)
这些属性在《Android动画篇(三)—— 属性动画ValueAnimator的使用》已经作了详细解释,这里就不一一细讲了。
ObjectAnimator color = ObjectAnimator.ofArgb(mTextView1, "backgroundColor", 0xffff00ff, 0xffffff00, 0xff0000ff);
color.setEvaluator(new ArgbEvaluator());
ObjectAnimator translation = ObjectAnimator.ofFloat(mTextView1, "translationY", 0f, 400f, 0f);
//构建AnimatorSet实例
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(color, translation);
//添加动画监听
animatorSet.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
Log.e(TAG, "onAnimationStart");
}
@Override
public void onAnimationEnd(Animator animation) {
Log.e(TAG, "onAnimationEnd");
}
@Override
public void onAnimationCancel(Animator animation) {
Log.e(TAG, "onAnimationCancel");
}
@Override
public void onAnimationRepeat(Animator animation) {
Log.e(TAG, "onAnimationRepeat");
}
});
//设置动画时长
animatorSet.setDuration(2000);
//开启动画
animatorSet.start();
效果如下:
从图中可以看出,我们设置了两个动画,一个颜色渐变,一个向下移动,调用playTogether()函数后,两个动画都同时启动了。
(1)playTogether
这里的函数都是一样的,只是参数不一样,第一个传入的是可变长参数,第二个传入的是集合Collection
ObjectAnimator color = ObjectAnimator.ofArgb(mTextView1, "backgroundColor", 0xffff00ff, 0xffffff00, 0xff0000ff);
color.setEvaluator(new ArgbEvaluator());
ObjectAnimator translation = ObjectAnimator.ofFloat(mTextView1, "translationY", 0f, 400f, 0f);
ObjectAnimator translation2 = ObjectAnimator.ofFloat(mTextView2, "translationY", 0f, 600f, 0f);
//构建AnimatorSet实例
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(color, translation, translation2);
//设置动画时长
animatorSet.setDuration(2000);
//开启动画
animatorSet.start();
效果图如下:
从图中可以看出,开启动画的时候,三个动画时同时执行的,tv1的背景颜色变化和位移以及tv2的位移都是同时开始。
(2)playSequentially
我们来看看使用playSequentially会有怎么样的效果:
ObjectAnimator color = ObjectAnimator.ofArgb(mTextView1, "backgroundColor", 0xffff00ff, 0xffffff00, 0xff0000ff);
color.setEvaluator(new ArgbEvaluator());
ObjectAnimator translation = ObjectAnimator.ofFloat(mTextView1, "translationY", 0f, 400f, 0f);
ObjectAnimator translation2 = ObjectAnimator.ofFloat(mTextView2, "translationY", 0f, 600f, 0f);
//将动画添加到集合中
List animatorList = new ArrayList<>();
animatorList.add(color);
animatorList.add(translation);
animatorList.add(translation2);
//构建AnimatorSet实例
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playSequentially(animatorList);
//设置动画时长
animatorSet.setDuration(2000);
//开启动画
animatorSet.start();
效果如下:
从上面的效果可以看到,动画时一个执行完以后再执行下一个动画,动画合集里面我们是一个个添加进去的,即先加入颜色变化动画,再加入tv1的移动,最后加入tv2的移动动画。调用playSequentially()是将动画组装起来然后依次播放动画。
那么,我们如果第一个动画一直在执行没有结束动画呢?即将第一个动画设置为无限循环,那会是怎么样的效果,我们来看看:
ObjectAnimator color = ObjectAnimator.ofArgb(mTextView1, "backgroundColor", 0xffff00ff, 0xffffff00, 0xff0000ff);
color.setEvaluator(new ArgbEvaluator());
//设置循环模式
color.setRepeatMode(ValueAnimator.REVERSE);
//设置无限循环
color.setRepeatCount(ValueAnimator.INFINITE);
ObjectAnimator translation = ObjectAnimator.ofFloat(mTextView1, "translationY", 0f, 400f, 0f);
ObjectAnimator translation2 = ObjectAnimator.ofFloat(mTextView2, "translationY", 0f, 600f, 0f);
//将动画添加到集合中
List animatorList = new ArrayList<>();
animatorList.add(color);
animatorList.add(translation);
animatorList.add(translation2);
//构建AnimatorSet实例
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playSequentially(animatorList);
//设置动画时长
animatorSet.setDuration(2000);
//开启动画
animatorSet.start();
效果:
从上面看到,三个动画已经通过playSequentially添加,第一个动画设置了无限循环,开始后永远不会结束,那么下一个动画也就无法开始。
上面我们知道playTogether和playSequentially能同时开始动画和逐个开始动画,但是如果我想再一个动画开始后,同是开始另外两个动画怎么?系统还提供了一个AnimatorSet.Builder类,这个类是由mAnimatorSet.play(Animator anim)产生的,并且是唯一能产生这个类的方法。我们来看看AnimatorSet.Builder的使用:
(1)常用函数
/**
* 表示要播放那个动画
* 获取AnimatorSet.Builder对象的唯一方法
*/
public AnimatorSet.Builder play(Animator anim)
/**
* 表示和前面的动画一起执行
*/
public AnimatorSet.Builder with(Animator anim)
/**
* 表示anim动画在执行Builder动画之后执行(Builder动画在anim动画之前)
*/
public AnimatorSet.Builder before(Animator anim)
/**
* 表示anim动画在执行Builder动画之前执行(Builder动画在anim动画之后)
*/
public AnimatorSet.Builder after(Animator anim)
/**
* 表示在延迟多少时间后执行该动画,单位为毫秒
*/
public AnimatorSet.Builder after(long delay)
(2)示例
我们使用上面的方法做一个例子:
ObjectAnimator color1 = ObjectAnimator.ofArgb(mTextView1, "backgroundColor", 0xffff00ff, 0xffffff00, 0xff0000ff);
color1.setEvaluator(new ArgbEvaluator());
ObjectAnimator translation1 = ObjectAnimator.ofFloat(mTextView1, "translationY", 0f, 400f, 0f);
ObjectAnimator translation2 = ObjectAnimator.ofFloat(mTextView2, "translationY", 0f, 600f, 0f);
ObjectAnimator rotation2 = ObjectAnimator.ofFloat(mTextView2, "rotation", 0f, 360f, 0f);
//构建AnimatorSet实例
AnimatorSet animatorSet = new AnimatorSet();
//获取builder实例
AnimatorSet.Builder builder = animatorSet.play(color1);//builder播放动画color1
builder.with(translation1);//跟随builder一起播放translation1
builder.before(translation2);//builder在动画translation2之前播放
builder.after(rotation2);//builder在动画rotation2之后播放
//链式写法
//builder.with(translation1).before(translation2).after(rotation2);
//设置动画时长
animatorSet.setDuration(2000);
//开启动画
animatorSet.start();
效果如下:
从效果图可以看出,tv2先旋转after(rotation2),然后tv1背景色变化并且向下移动play(color1),with(translation1);最后才执行tv2向下移动before(translation2);
所以以play(color1)中的动画为基准,with(translation1)会跟随play()中的动画一起执行,after(rotation2)是在play()动画之前执行,也就是说rotation2最先执行,before(translation2)在play()之后执行。也可以这样理解:play()动画在after(rotation2)之后执行,play()动画在before(translation2)之前执行,其实就是以谁来定基准而已,谁在谁前后执行,这里比较容易混淆。
我们也可以将这部分的代码简化为链式写法:
builder.with(translation1).before(translation2).after(rotation2);
我们为AnimatorSet添加监听器,通过addListener(AnimatorListener listener)添加:
ObjectAnimator color = ObjectAnimator.ofArgb(mTextView1, "backgroundColor", 0xffff00ff, 0xffffff00, 0xff0000ff);
color.setEvaluator(new ArgbEvaluator());
color.setRepeatCount(ValueAnimator.INFINITE);
color.setRepeatMode(ValueAnimator.REVERSE);
ObjectAnimator translation = ObjectAnimator.ofFloat(mTextView1, "translationY", 0f, 400f, 0f);
//构建AnimatorSet实例
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(color, translation);
//添加动画监听
animatorSet.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
Log.e(TAG, "onAnimationStart");
}
@Override
public void onAnimationEnd(Animator animation) {
Log.e(TAG, "onAnimationEnd");
}
@Override
public void onAnimationCancel(Animator animation) {
Log.e(TAG, "onAnimationCancel");
}
@Override
public void onAnimationRepeat(Animator animation) {
Log.e(TAG, "onAnimationRepeat");
}
});
//设置动画时长
animatorSet.setDuration(2000);
//开启动画
animatorSet.start();
注意:监听器必须在start()启动动画之前设置,否则有可能接受不到onAnimationStart(Animator animation)等函数的监听。
点击start anim按钮启动动画,一段时间后点击cancel anim按钮结束动画,效果如下:
从上面的代码中,我们设置了TextView1向下移动一次,背景颜色无限循环改变,点击start anim按钮,tv1启动动画,向下移动后回到原点,背景色做无限循环变色,然后我们点击cancel anim 按钮,打印出来的log如上图,只是打印了开始,退出,结束动画,并没有打印onAnimationRepeat()函数,得出结论:
(1)AnimatorSet设置的监听函数也是仅仅监听AnimatorSet的状态,与动画无关。
(2)AnimatorSet中没有设置循环次数的方法,所以AnimatorSet设置的监听方法永远运行不到onAnimationRepeat()中。
/**
* 设置动画目标控件
*/
void setTarget(Object target)()
/**
* 设置动画时间
*/
AnimatorSet setDuration(long duration)
/**
* 设置插值器
*/
void setInterpolator(TimeInterpolator interpolator)
其中AnimatorSet与单个控件拥有函数由一部分是相同的,如果同时使用他们会不会冲突呢?我们来校验一下他们的使用区别:
ObjectAnimator translation1 = ObjectAnimator.ofFloat(mTextView1, "translationY", 0f, 400f, 0f);
translation1.setDuration(5000);
translation1.setInterpolator(new BounceInterpolator());
ObjectAnimator rotation2 = ObjectAnimator.ofFloat(mTextView2, "rotation", 0f, 360f, 0f);
rotation2.setDuration(10000);
rotation2.setInterpolator(new AccelerateInterpolator());
//构建AnimatorSet实例
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(translation1).with(rotation2);
//设置动画时长
animatorSet.setDuration(1000);
animatorSet.setTarget(mTextView1);
animatorSet.setInterpolator(new LinearInterpolator());
//开启动画
animatorSet.start();
从上面的代码中,我们设置了mTextView1在5秒完成动画,向下移动400后再回到原点,插值器为BounceInterpolator;mTextView2在10秒内完成动画,旋转360度,再回到0度,插值器为AccelerateInterpolator。动画集合animatorSet设置了1秒,目标控件为mTextView1,插值器为LinearInterpolator,效果如下:
从动画中可以看出,只有mTextView1做了动画,一秒内向下移动并且旋转然后回到原点,那么说明AnimatorSet中设置的会覆盖单个动画中设置的函数,我们再来看看AnimatorSet没有设置的时候是什么效果:
ObjectAnimator translation1 = ObjectAnimator.ofFloat(mTextView1, "translationY", 0f, 400f, 0f);
translation1.setDuration(5000);
translation1.setInterpolator(new BounceInterpolator());
ObjectAnimator rotation2 = ObjectAnimator.ofFloat(mTextView2, "rotation", 0f, 360f, 0f);
rotation2.setDuration(10000);
rotation2.setInterpolator(new AccelerateInterpolator());
//构建AnimatorSet实例
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(translation1).with(rotation2);
//开启动画
animatorSet.start();
效果如下:
可以看出AnimatorSet没有设置的话则以单个动画中设置的为准。得出结论:
AnimatorSet设置以后会覆盖单个动画的设置,单个动画的设置变为无效;如果AnimatorSet没有设置则以单个动画的设置为准。
ValueAnimator和ObjectAnimator除了通过ofInt()、ofFloat()、ofObject()还可以通过PropertyValuesHolder来创建实例,由于ValueAnimator用的比较少,这里使用ObjectAnimator来配合PropertyValuesHolder使用,来看看PropertyValuesHolder的构造方法:
//ValueAnimator
ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values)
//ObjectAnimator
ObjectAnimator ofPropertyValuesHolder(Object target,PropertyValuesHolder... values)
PropertyValuesHolder的意义就是保存动画过程中所需要的属性和值,其实ofInt()和ofFloat()的内部实现就是通过封装PropertyValuesHolder实例来保存动画状态的,后面的操作也是以PropertyValuesHolder为主。
我们来看看创建PropertyValuesHolder实例的函数:
public static PropertyValuesHolder ofInt(String propertyName, int... values)
public static PropertyValuesHolder ofFloat(String propertyName, float... values)
有不理解的同学可以参考《Android动画篇(五)—— 属性动画ObjectAnimator基本使用》
//创建PropertyValuesHolder实例
PropertyValuesHolder valuesHolder1 = PropertyValuesHolder.ofInt("backgroundColor", 0xffff00ff, 0xffffff00, 0xff0000ff);
valuesHolder1.setEvaluator(new ArgbEvaluator());
PropertyValuesHolder valuesHolder2 = PropertyValuesHolder.ofFloat("scaleX", 0f, 2f, 0f, 3f, 1f);
PropertyValuesHolder valuesHolder3 = PropertyValuesHolder.ofFloat("scaleY", 0f, 2f, 0f, 3f, 1f);
//将创建PropertyValuesHolder实例添加到ObjectAnimator中
ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(mTextView1, valuesHolder1, valuesHolder2, valuesHolder3);
objectAnimator.setDuration(2000);
objectAnimator.start();
创建PropertyValuesHolder实例后,将实例分别添加到ObjectAnimator中,执行动画
效果如下:
好了,有关动画集合的就这里结束了,有需要的朋友可以回头看看我前面写的几篇动画文章!
源码下载地址:https://github.com/FollowExcellence/AndroidAnimation
请大家尊重原创者版权,转载请标明出处: https://blog.csdn.net/m0_37796683/article/details/90645047 谢谢!
动画系列文章:
1、 Android动画篇(一)—— alpha、scale、translate、rotate、set的xml属性及用法
- 补间动画的XML用法以及属性详解
2、Android动画篇(二)—— 代码实现alpha、scale、translate、rotate、set及插值器动画
- 代码动态实现补间动画以及属性详解
3、 Android动画篇(三)—— 属性动画ValueAnimator的使用
- ValueAnimator的基本使用
4、 Android动画篇(四)—— 属性动画ValueAnimator的高级进阶
- 插值器(Interpolator)、计算器(Evaluator)、ValueAnimator的ofObject用法等相关知识
5、 Android动画篇(五)—— 属性动画ObjectAnimator基本使用
- ObjectAnomator的基本使用以及属性详解
6、 Android动画篇(六)—— 组合动画AnimatorSet和PropertyValuesHolder的使用
- AnimatorSet动画集合和PropertyValuesHolder的使用
以上几篇动画文章是一定要掌握的,写的不好请多多指出!