美文网首页
Android 组合动画AnimatorSet

Android 组合动画AnimatorSet

作者: as_pixar | 来源:发表于2020-06-12 12:32 被阅读0次

一无所有的人是有福的,因为他们将获得一切! ——罗曼罗兰

前面两篇博客我们讲解的ValueAnimator和ObjectAnimator都是单飞,只能单独实现一个动画,如果想把动画一锅烩了,让他们一块执行,要用到组合动画,就需要AnimatorSet。

playSequentially()与playTogether()函数

sequentially 单词的意思是顺序,像接力赛跑步,前面一个人跑步完成2km,后面一个人接着完成2km,指动画按顺序一个一个播放。together 单词的意思是 一起,一起玩游戏,一起打羽毛球,两个或多个人一起运动,一起玩,指多个动画一块播放。一般我们会用ObjectAnimator操作View动画,我们着重讲解它。
playSequentially()函数声明如下

public void playSequentially(Animator... items)
public void playSequentially(List<Animator> items)

第一个构造函数是我们最常用的,可以传入任意多个Animator对象,这些对象的动画会依次播放。下面说明playSequentially()函数的使用方法。先看下界面



单击按钮时,调用下面的代码

package com.as.propertyanimator;

import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatButton;

public class SetAnimatorActivity extends AppCompatActivity {

    private TextView tv1;
    private TextView tv2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_set_animator);

        AppCompatButton btnStart = findViewById(R.id.btnStart);
        tv1 = findViewById(R.id.tv1);
        tv2 = findViewById(R.id.tv2);

        btnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                setAnimator1();
            }
        });

    }

    private void setAnimator1() {
        ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(tv1, "backgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff);

        ObjectAnimator tv1TranslationY = ObjectAnimator.ofFloat(tv1, "translationY", 0, 300, 0);

        ObjectAnimator tv2TranslationY = ObjectAnimator.ofFloat(tv2, "translationY", 0, 500, 0);


        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playSequentially(tv1BgAnimator, tv1TranslationY, tv2TranslationY);
        animatorSet.setDuration(2000);
        animatorSet.start();
    }


}

构造了三个动画,针对 tv1 的是tv1BgAnimator 和 tv1TranslationY ,分别用于改变背景和改变控件Y坐标位置,针对tv2 的是 tv2TranslationY,用于改变控件Y坐标位置。然后利用AnimatorSet 的playSequentially()函数将这三个动画组装起来,依次播放。



从效果图中看出,TextView1 做颜色改变动画,之后做位移动画,最后TextView2 做位移动画。

playTogether()函数声明如下

public void playTogether(Animator... items)
public void playTogether(Collection<Animator> items)

第一个构造函数表示传入任意多个Animator对象,第二个参数则需要传入一个组装好的Collection对象,都是将参数中的动画一起播放。
同样是上面的例子,使用playTogether()函数来播放动画,我们来看看效果。

private void setPlayTogether() {
        ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(tv1, "backgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff);

        ObjectAnimator tv1TranslationY = ObjectAnimator.ofFloat(tv1, "translationY", 0, 300, 0);

        ObjectAnimator tv2TranslationY = ObjectAnimator.ofFloat(tv2, "translationY", 0, 500, 0);


        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(tv1BgAnimator, tv1TranslationY, tv2TranslationY);
        animatorSet.setDuration(2000);
        animatorSet.start();
    }

从效果图中可以看出,三个动画是一起播放的。大家可能会好奇,我们没有给每个动画设置时长,每个动画的时长都是2秒中,在 animatorSet.setDuration(2000);设置的时长,会覆盖每个动画设置的时长。只有animatorSet.setStartDelay(1000) 这个函数是设置自己延迟开始时间,不会覆盖每个动画,除了这个函数,其它的设置函数会覆盖每个动画设置。

playSequentially()与playTogether()函数的含义

激活动画后,动画开始后的操作只由动画自己负责,至于动画结不结束,也只有动画自己知道。playSequentially()函数就是激活一个动画后,动画之后的操作由动画自己负责,这个动画结束之后,再开始下一个动画。如果上一个动画没有结束,那么下一个动画永远不会开始。

我们看一个playTogether()函数的例子

        ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(tv1, "backgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff);

        ObjectAnimator tv1TranslationY = ObjectAnimator.ofFloat(tv1, "translationY", 0, 300, 0);
        tv1TranslationY.setStartDelay(2000);
        tv1TranslationY.setDuration(5000);
        tv1TranslationY.setRepeatCount(ObjectAnimator.INFINITE);

        ObjectAnimator tv2TranslationY = ObjectAnimator.ofFloat(tv2, "translationY", 0, 500, 0);
        tv2TranslationY.setStartDelay(2000);


        animatorSet = new AnimatorSet();
        animatorSet.playTogether(tv1BgAnimator, tv1TranslationY, tv2TranslationY);
        animatorSet.setDuration(2000);
        animatorSet.start();

在这个例子中,我们将tv1TranslationY 延迟2000ms开始,动画时长5000ms,并且无限循环;将tv2TranslationY设为延迟2000ms;而对tv1BgAnimator 没有任何设置,所以默认直接开始。



在效果图中,单击按钮后,先进行tv1背景颜色变化,颜色变化完以后,tv2的延迟延迟刚好结束,此时两个TextView开始做平移操作。最后tv1 的位移变换时无限循环的,而且我们将它的动画时长设置为5000ms,但是我们发现它的动画时长2000ms,是因为animatorSet.setDuration(2000);覆盖了每一个动画的时长。

在这个例子中可以看到playTogether()函数只负责动画一起开始,动画开始后,只有每个动画自己清楚干什么事情。我们点击了Cancel Anim,animatorSet 可以取消掉所有正在执行的动画。

再看一个playSequentially()函数的例子

 ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(tv1, "backgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff);
        tv1BgAnimator.setStartDelay(2000);

        ObjectAnimator tv1TranslationY = ObjectAnimator.ofFloat(tv1, "translationY", 0, 300, 0);
        tv1TranslationY.setRepeatCount(ObjectAnimator.INFINITE);

        ObjectAnimator tv2TranslationY = ObjectAnimator.ofFloat(tv2, "translationY", 0, 400, 0);


        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playSequentially(tv1BgAnimator, tv1TranslationY, tv2TranslationY);
        animatorSet.setDuration(2000);
        animatorSet.start();

同样是三个动画,tv1BgAnimator先设置了延迟,tv1TranslationY设置了无限循环。使用playSequentially()函数来一次播放这三个动画,tv1BgAnimator在开始动画后延迟2000ms再开始,结束之后进入tv1TranslationY,这个动画会无限循环,也就意味着永远不会结束,第三个动画tv2TranslationY也永远不会开始。



从效果图中可以看出,TextView1 先等了2000ms毫秒在开始颜色变化,然后开始无限循环的上下移动;而tv2 永远不会开始。

虽然playSequentially()与playTogether()函数分别能实现一起开始动画和依次开始动画,但是并不能非常自由的组合动画。比如我们有三个动画,A,B,C,想先播放C,然后同时播放A和B,为了更方便的组合动画,通过AnimatorSet.Builder类。

我们先看一下它们的函数声明

// 表示要播放哪个动画
public Builder play(Animator anim)

// 和前面的动画一起执行
public Builder with(Animator anim)

// 当前动画在anim动画之前
public Builder before(Animator anim)

// 当前动画在 anim 之后
public Builder after(Animator anim)

// 延迟 n 毫秒之后执行
public Builder after(long delay)

play(Animator anim) 表示当前播放哪个动画,而with(Animator anim) ,before(Animator anim),after(Animator anim) 都是以play()中当前所播放的动画为基准。

比如play(Animator playAnim) 与 before(Animator beforeAnim)共用时,表示在播放beforeAnim 之前,先播放playAnim 动画。同样play(Animator playAnim) 与 after(Animator afterAnim)共用时,则表示播放afterAnim动画之后,再播放playAnim动画。

 private void setPlayBuilder() {
        ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(tv1, "backgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff);
        ObjectAnimator tv1TranslationY = ObjectAnimator.ofFloat(tv1, "translationY", 0, 300, 0);
        ObjectAnimator tv2TranslationY = ObjectAnimator.ofFloat(tv2, "translationY", 0, 400, 0);

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.play(tv1TranslationY).with(tv2TranslationY).after(tv1BgAnimator);
        animatorSet.setDuration(2000);
        animatorSet.start();
    }
AnimatorSet.Builder play = animatorSet.play(tv1TranslationY);
        play.with(tv2TranslationY);
        play.after(tv1BgAnimator);

这两种写法的运行结果一样,这里实现的效果时tv1颜色变化完成之后,两个控件一起开始平移动画。


AnimatorSet 监听器

单机Start Anim 按钮时,两个TextView 一起平移,设置tv2 动画无限循环,给组合动画添加回调监听Animator.AnimatorListener() 函数中每一部分添加打印日志

private void setListenerAnimator() {
        ObjectAnimator tv1TranslationY = ObjectAnimator.ofFloat(tv1, "translationY", 0, 500, 0);
        ObjectAnimator tv2TranslationY = ObjectAnimator.ofFloat(tv2, "translationY", 0, 500, 0);
        tv2TranslationY.setRepeatCount(ValueAnimator.INFINITE);

        mAnimatorSet = new AnimatorSet();
        mAnimatorSet.play(tv1TranslationY).with(tv2TranslationY);
        mAnimatorSet.setDuration(2000);

        mAnimatorSet.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                Log.d("AnimatorSet", "-----onAnimationStart----");
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                Log.d("AnimatorSet", "-----onAnimationEnd----");
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                Log.d("AnimatorSet", "-----onAnimationCancel----");
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
                Log.d("AnimatorSet", "-----onAnimationRepeat----");
            }
        });
        mAnimatorSet.start();
    }

我们看打印结果

06-12 11:27:06.194 17105-17105/com.as.propertyanimator D/AnimatorSet: -----onAnimationStart----
06-12 11:27:09.891 17105-17105/com.as.propertyanimator D/AnimatorSet: -----onAnimationCancel----
06-12 11:27:09.891 17105-17105/com.as.propertyanimator D/AnimatorSet: -----onAnimationEnd----
  • 从效果图中看出,虽然TextView2 再无限循环,但日志中没有打印出对应的重复日志;从日志中也可以看出,AnimatorSet 的监听函数只是用来监听 AnimatorSet 的状态,与其中的动画无关。
  • AnimatorSet没有设置循环的函数,所以动画执行一次就结束了,永远无法执行到onAnimationRepeat()函数中。

常用函数

// 设置单次动画时长
public AnimatorSet setDuration(long duration)

// 设置插值器
public void setInterpolator(TimeInterpolator interpolator) 

// 设置ObjectAnimator 动画目标控件
public void setTarget(Object target)

这个函数再ObjectAnimator也有,那么与AnimatorSet设置的区别是啥?
区别就是AnimatorSet 设置以后,会覆盖掉ObjectAnimator中的设置。也就是说,AnimatorSet设置后,以AnimatorSet为准。如果AnimatorSet没有设置,以ObjectAnimator为准。前门我们讲过动画时长的覆盖就是这样。

来看下面的例子

private void setAnimator2() {
        ObjectAnimator tv1TranslationY = ObjectAnimator.ofFloat(tv1, "translationY", 0, 300, 0);
        tv1TranslationY.setDuration(1000 * 100);
        tv1TranslationY.setInterpolator(new BounceInterpolator());

        ObjectAnimator tv2TranslationY = ObjectAnimator.ofFloat(tv2, "translationY", 0, 300, 0);
        tv2TranslationY.setInterpolator(new AccelerateDecelerateInterpolator());

        mAnimatorSet = new AnimatorSet();
        mAnimatorSet.play(tv1TranslationY).with(tv2TranslationY);
        mAnimatorSet.setInterpolator(new LinearInterpolator());
        mAnimatorSet.setDuration(2000);
        mAnimatorSet.start();
    }

在这个例子中,我们通过 mAnimatorSet.setDuration(2000); 设置为所有动画的单词执行时间2000ms,虽然给tv1 设置了 1000 * 10 ms,但是由于AnimatorSet设置了setDuration(2000),单个动画的时长设置无效。我们还分为给tv1 和 tv2 设置了插值器,给AnimatorSet也设置了插值器,将覆盖tv1 和 tv2 的插值器,如果AnimatorSet没有设置插值器,tv1 和 tv2 按各自的插值器做动画。


从效果图中可以看出,两个控件同时开始和结束,并且都是匀速运动。最后我们来看看Target 这个函数。

ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(tv1, "backgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff);
        tv1BgAnimator.setDuration(1000 * 100);
        tv1BgAnimator.setInterpolator(new BounceInterpolator());

        ObjectAnimator tv2TranslationY = ObjectAnimator.ofFloat(tv2, "translationY", 0, 300, 0);
        tv2TranslationY.setInterpolator(new AccelerateDecelerateInterpolator());

        mAnimatorSet = new AnimatorSet();
        mAnimatorSet.play(tv1BgAnimator).with(tv2TranslationY);
        mAnimatorSet.setInterpolator(new LinearInterpolator());
        mAnimatorSet.setDuration(2000);
        mAnimatorSet.setTarget(tv2);
        mAnimatorSet.start();

在这段代码中,我们给tv1设置了背景色,给tv2设置了上下移动。我们 mAnimatorSet.setTarget(tv2);将各个动画的目标空间设置为tv2,所以tv1不会有任何动画。所有的动画都发生在tv2上。


  • AnimatorSet.setTarget() 函数的作用就是将动画的目标控件同意设置为当前控件。

Animator 动画的XML实现

着重看下如何利用XML来实现ValueAnimator ,ObjectAnimator,AnimatorSet。

  • <animator> : 对应ValueAnimator
  • <objectanimator> : 对应ObjectAnimator
  • <set> : 对应AnimatorSet

animator 标签

<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="int"
    android:valueFrom="float | int | color"
    android:valueTo="float | int | color"
    android:startOffset="int"
    android:repeatCount="int"
    android:repeatMode="reverse | restart"
    android:valueType="colorType intType floatType"
    android:interpolator="@android:interpolator/xxx"
    >
</animator>
  • android:duration : 每次动画播放时长
  • android:valueFrom : 初始动画值,取值范围为 float ,int,和color这三种类型的值。如果取值为float,则对应的值样式为20.0,如果取值为int ,则对应的样式为20,如果取值为color,则对应的样式为#666666。
  • android:valueTo : 结束动画值,取值范围为 float ,int,和color这三种类型的值。
  • android:startOffset :动画激活延时,对应代码中的startDelay(long dealy)函数
  • android:repeatCount : 动画重复次数
  • android:repeatMode :动画重复模式,reverse倒叙重播,restart正序重播。
  • android:valueType :表示参数值类型,取值为 intType,floatType和colorType ,与android:valueFrom和android:valueTo相对应,如果取值为int类型,那么android:valueFrom和android:valueTo对应int类型,如果取值为color类型,那么android:valueFrom和android:valueTo对应color类型。
  • android:interpolator="@android:interpolator/xxx" 设置插值器

写个例子
首先,在 res/animator 文件夹下生成一个动画的xml文件

<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:interpolator="@android:anim/bounce_interpolator"
    android:valueFrom="0"
    android:valueTo="300"
    android:valueType="intType" />

在这里,我们将valueType 设置为intType,所以对应android:valueFrom和android:valueTo都必须是int类型:插值器使用回弹插值器。

private void loadXmlAnimator() {
        ValueAnimator animator = (ValueAnimator) AnimatorInflater.loadAnimator(this, R.animator.valueanimator);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int curValue = (int) animation.getAnimatedValue();
                tv1.layout(curValue, curValue, tv1.getWidth() + curValue, tv1.getHeight() + curValue);
            }
        });
        animator.start();
    }

由于xml文件中根标签所对应的是ValueAnimator,所以在加载后,将其转换为ValueAnimator,然后对其添加控件监听。在监听是,动态改变TextView 的位置。


objectAnimator标签

下面是完整的objectAnimator标签所有字段

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:propertyName="string"
    android:duration="int"
    android:valueFrom="float | int | color"
    android:valueTo="float | int | color"
    android:startOffset="int"
    android:repeatCount="int"
    android:repeatMode="reverse | restart"
    android:valueType="colorType intType floatType"
    android:interpolator="@android:interpolator/xxx"
    >
</objectAnimator>

其中android:propertyName字段对应属性名,既ObjectAnimator所要操作控件的属性名。其它字段与含义与animator 标签的相应字段是一样的。

写个例子
在res/animator 文件夹下生成一个动画xml文件

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:propertyName="translationY"
    android:repeatCount="1"
    android:repeatMode="reverse"
    android:startOffset="3000"
    android:valueFrom="0"
    android:valueTo="500"
    android:valueType="floatType" />

我们更改的属性为translationY,既改变纵坐标,时长为2000ms,translationY的值从0到500,使用的是加速插值器,对用的值类型Float,因为setTranslationY()函数的参数是Float类型。该函数的声明如下

public void setTranslationY(float translationY)

同时,设置重复次数为1,重复模式倒叙重新开始,将动画延迟2000ms后激活。然后加载动画

        ObjectAnimator animator = (ObjectAnimator) AnimatorInflater.loadAnimator(this, R.animator.objectanimator);
        animator.setTarget(tv1);
        animator.start();

从效果图中可以看出,单击按钮后,延迟了2000ms开始动画,然后倒叙重复运行 1 次。

相关文章

网友评论

      本文标题:Android 组合动画AnimatorSet

      本文链接:https://www.haomeiwen.com/subject/tukitktx.html