自定义View学习笔记我爱编程

自定义View(六)-动画- AnimatorSet与XML设置

2018-01-02  本文已影响132人  g小志

介绍

AnimatorSet是组合动画,前面在ObjectAnimator.ofPropertyValuesHolder(),时也可以做到控制多个属性做动画,但是.ofPropertyValuesHolder(),仅仅是将多个属性同时做动画却无法灵活控制每个属性的播放顺序,针对的是一个控件,而AnimatorSet是组合动画。更侧重的是在多个动画播放时对动画的控制(可以控制动画的顺序,延时,同时可以控制多个控件的动画等等)。


<font color=#006400 size=6>AnimatorSet</font>

AnimatorSet针对ValueAnimator和ObjectAnimator都是适用的,但一般而言,我们不会用到ValueAnimator的组合动画,所以我们仅讲解ObjectAnimator下的组合动画实现。

主要方法:

  1. playSequentially :表示所有动画依次播放
  2. playTogether :表示所有动画一起开始。

方法参数:

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

第一个是我们最常用的,它的参数是可变长参数,也就是说我们可以传进去任意多个Animator对象。这些对象的动画会逐个播放。第二个构造函数,是传进去一个List< Animator>的列表。原理一样,也是逐个去取List中的动画对象,然后逐个播放。

使用:

·
  private void doPlaySequentiallyAnimator() {
        ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(mTv1, "BackgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff);
        tv1BgAnimator.setEvaluator(new ArgbEvaluator());
        ObjectAnimator tv1TranslateY = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 300, 0);
        ObjectAnimator tv2TranslateY = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 300, 0);

            AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playSequentially(tv1BgAnimator, tv1TranslateY, tv2TranslateY);
        animatorSet.setDuration(1000);
        animatorSet.start();
    }

当按钮被点击时执行doPlaySequentiallyAnimator()方法。效果如下:

GIF11.gif
这就是playSequentially的效果,即逐个播放动画,一个动画结束后,播放下一个动画,播放的顺序就是传入动画的先后顺序。(这里也可以看出AnimatorSet是针对于动画,并不管你在动画是在哪个控件或是几个控件,这就与.ofPropertyValuesHolder()不同)

playTogether表示将所有动画一起播放 。

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

参数含义与上面一致。
代码:

animatorSet.playTogether(tv1BgAnimator, tv1TranslateY, tv2TranslateY);

其他代码去上面一致.效果:

GIF12.gif
此时三个动画一起播放。

首先用playTogether来看个例子:

GIF12.gif
将tv1TranslateY开始延迟2000毫秒开始,并设为无限循环。tv2TranslateY设为开始延迟2000毫秒。而tv1BgAnimator则是没有任何设置,所以是默认直接开始。
从这个例子中也可以看到,playTogether只是负责在同一时间点一起开始,对于开始后,各个动画怎么操作就是他们自己的事了,至于各个动画结不结束也是他们自已的事了。

将播放改成playSequentially顺序播放动画:

·
 private void doPlaySequentiallyAnimator2() {
        ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(mTv1, "BackgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff);
        tv1BgAnimator.setEvaluator(new ArgbEvaluator());
        ObjectAnimator tv1TranslateY = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 400, 0);
        tv1TranslateY.setRepeatCount(ValueAnimator.INFINITE);
        ObjectAnimator tv2TranslateY = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0);

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playSequentially(tv1BgAnimator, tv1TranslateY, tv2TranslateY);
        animatorSet.setDuration(2000);
        animatorSet.start();
    }

效果:

GIF13.gif

tv1BgAnimator颜色改变后移动,并设置成无限循环。那么tv2TranslateY永远无法得到执行。

总结:
<font color=#006400>


AnimatorSet.Builder-自由设置动画顺序

上面两种播放方法只能一起播放或者顺序播放,无法指定某一个动画的播放顺序,如果想ABC三个动画想指定C先播放就要用到AnimatorSet.Builder。
AnimatorSet.Builder可以更加灵活设置动画播放的先后顺序。

//调用AnimatorSet中的play方法是获取AnimatorSet.Builder对象的唯一途径
//表示要播放哪个动画
public Builder play(Animator anim)
//和前面动画一起执行
public Builder with(Animator anim)
//执行前面的动画后才执行该动画
public Builder before(Animator anim)
//执行先执行这个动画再执行前面动画
public Builder after(Animator anim)
//延迟n毫秒之后执行动画
public Builder after(long delay)

<font color=#DC143C>注意: play(Animator anim)表示当前在播放哪个动画,另外的with(Animator anim)、before(Animator anim)、after(Animator anim)都是以play中的当前所播放的动画为基准的</font>。
例如: 当play(playAnim)与before(beforeAnim)共用,则表示在播放beforeAnim之前,先播放playAnim动画;同样,当play(playAnim)与after(afterAnim)共用时,则表示在播放afterAnim动画之后,再播放playAnim动画。

方式一:使用builder对象逐个添加动画

AnimatorSet.Builder builder = animatorSet.play(tv1TranslateY);
builder.with(tv2TranslateY);
builder.after(tv1BgAnimator);

方式二:串行方式

animatorSet.play(tv1TranslateY).with(tv2TranslateY).after(tv1BgAnimator);

如下:

ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(mTv1, "BackgroundColor",  0xffff00ff, 0xffffff00, 0xffff00ff);
ObjectAnimator tv1TranslateY = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 400, 0);
ObjectAnimator tv2TranslateY = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0);

AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(tv1TranslateY).with(tv2TranslateY).after(tv1BgAnimator);
animatorSet.setDuration(2000);
animatorSet.start();

表示在tv1颜色变化后,两个控件一同开始位移动画:

GIF14.gif

AnimatorSet监听器

因为ValueAnimator和AnimatorSet都派生自Animator类,而AnimatorListener是Animator类中的函数。所以都是使用相同的监听:

/** 
 * 监听器二:监听动画变化时四个状态 
 */ 
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);
}
 
//添加方法为:public void addListener(AnimatorListener listener)   
/** 
 * 监听器三:监听动画暂停与重新开始
 */ 
public static interface AnimatorPauseListener {
       
        void onAnimationPause(Animator animation);
        void onAnimationResume(Animator animation);
    }
//添加方法为:public void addPasueListener(AnimatorPauseListener listener)

同时我们也可以传入AnimatorListenerAdapter监听器,他是一个抽象方法,里面实现了Animator.AnimatorListener,Animator.AnimatorPauseListener接口,这样我们可以只实现我们需要的方法。这里我全部实现因为为了打印看出效果:

GIF16.gif

虽然我们的tv2TranslateY动画在无限循环,但Log中没有打印出对应的repeat的日志,从日志中也可以看出,AnimatorSet的监听函数也只是用来监听AnimatorSet的状态的,与其中的动画无关;

总结:
<font color=#006400>

AnimatorSet设置与单个动画属性冲突时

//设置单次动画时长
public AnimatorSet setDuration(long duration);
//设置加速器
public void setInterpolator(TimeInterpolator interpolator)
//设置ObjectAnimator动画目标控件
public void setTarget(Object target)

这几个函数在ObjectAnimator也存在,当单个动画设置上面的属性,同时组合动画AnimatorSet也设置了相同的属性。则遵循下面的规则:

<font color=#006400>在AnimatorSet中设置以后,会覆盖单个ObjectAnimator中的设置;即如果AnimatorSet中没有设置,那么就以ObjectAnimator中的设置为准。如果AnimatorSet中设置以后,ObjectAnimator中的设置就会无效。
</font>

例如:
<font color=#DC143C>

  1. 当单个动画(ObjectAnimator)与组合动画(AnimatorSet)同时设置setDuration(long duration)动画时长,那么所有单个动画设置的时长失效。如果组合动画(AnimatorSet)没有设置setDuration(long duration)动画时长,那么会每个动画会根据自己的时长做动画。
  2. 当单个动画(ObjectAnimator)与组合动画(AnimatorSet)同时设置setInterpolator,那么所有单个动画设置的加速器失效。如果组合动画(AnimatorSet)没有设置加速器,那么会每个动画会根据自己的加速器做动画。
    </font>
    如下:
·
    private void doPlaySequentiallyAnimator4() {
        ObjectAnimator tv1TranslateY = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 400, 0);
        tv1TranslateY.setDuration(50000000);
        tv1TranslateY.setInterpolator(new BounceInterpolator());

        ObjectAnimator tv2TranslateY = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0);
        tv2TranslateY.setDuration(5000);
        tv2TranslateY.setInterpolator(new AccelerateDecelerateInterpolator());

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.setDuration(2000);
        animatorSet.play(tv2TranslateY).with(tv1TranslateY);
        animatorSet.start();
    }

效果:

GIF17.gif
此时tv1TranslateY与tv2TranslateY都设置了时长同时AnimatorSet也设置了时长,发现只有animatorSet.setDuration(2000);生效,由于animatorSet没有设置加速器,所有动画执行各自的加速器。
<font color=#DC143C>
  1. AnimatorSet.setTarget()的作用就是将动画的目标统一设置为当前控件,AnimatorSet中的所有动画都将作用在所设置的target控件上
    </font>

如下:

 ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(mTv1, "BackgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff);
        ObjectAnimator tv2TranslateY = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0);

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(tv1BgAnimator,tv2TranslateY);
        animatorSet.setDuration(2000);
        animatorSet.setTarget(mTv2);
        animatorSet.start();
GIF18.gif

setStartDelay(long startDelay)

//设置延时开始动画时长
public void setStartDelay(long startDelay)

当AnimatorSet所拥有的函数与单个动画所拥有的函数冲突时,就以AnimatorSet设置为准。但唯一的例外就是setStartDelay。
setStartDelay函数不会覆盖单个动画的延时,而且仅针对性的延长AnimatorSet的激活时间,单个动画的所设置的setStartDelay仍对单个动画起作用。
遵循原则:
<font color=#DC143C>


<font color=#006400 size=6>XML实现属性动画</font>

ValueAnimator、ObjectAnimator和AnimatorSet属性动画也可以在SML中设置。在res/animator/目录下创建XML属性动画详细可以参照
属性动画官方文档
下面只记录简单实用:

XML标签含义:

<animator
    android:duration="int"
    android:valueFrom="float | int | color"
    android:valueTo="float | int | color"
    android:startOffset="int"
    android:repeatCount="int"
    android:repeatMode=["repeat" | "reverse"]
    android:valueType=["intType" | "floatType"]
    android:interpolator=["@android:interpolator/XXX"]/>

XML:

<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
          android:duration="1000"
          android:interpolator="@android:anim/bounce_interpolator"
          android:valueFrom="0"
          android:valueType="intType"
          android:valueTo="300">
</animator>

代码:

ValueAnimator valueAnimator= (ValueAnimator) AnimatorInflater.loadAnimator(this,R.animator.animator);
                valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator valueAnimator) {
                        int value= (int) valueAnimator.getAnimatedValue();
                        tv_text.layout(value,value,tv_text.getWidth()+value,tv_text.getHeight()+value);
                    }
                });
                valueAnimator.start();

效果:

GIF19.gif
<objectAnimator
    android:propertyName="string"
    android:duration="int"
    android:valueFrom="float | int | color"
    android:valueTo="float | int | color"
    android:startOffset="int"
    android:repeatCount="int"
    android:repeatMode=["repeat" | "reverse"]
    android:valueType=["intType" | "floatType"]
    android:interpolator=["@android:interpolator/XXX"]/>

参数:

XML:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
                android:propertyName="TranslationY"
                android:duration="2000"
                android:valueFrom="0.0"
                android:valueTo="400.0"
                android:interpolator="@android:anim/accelerate_interpolator"
                android:valueType="floatType"
                android:repeatCount="1"
                android:repeatMode="reverse"
                android:startOffset="2000"
    >
</objectAnimator>

代码:

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

效果:

GIF20.gif

XML:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
                android:propertyName="BackgroundColor"
                android:duration="5000"
                android:valueFrom="#ffff00ff"
                android:valueTo="#ffffff00"/>

代码:

ObjectAnimator objectAnimator= (ObjectAnimator) AnimatorInflater.loadAnimator(this,R.animator.animatorcolor);
        objectAnimator.setTarget(mTv1);
        objectAnimator.start();

效果:

GIF.gif
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="together">
    <objectAnimator
        android:propertyName="x"
        android:duration="500"
        android:valueFrom="0"
        android:valueTo="400"
        android:valueType="floatType"/>
    <objectAnimator
        android:propertyName="y"
        android:duration="500"
        android:valueFrom="0"
        android:valueTo="300"
        android:valueType="floatType"/>
</set>
<!--这里有两个objectAnimator动画,一个改变值x坐标,一个改变值y坐标;取值分别为0-400和0-300;然后在代码中加载-->

代码:

 ObjectAnimator objectAnimator= (ObjectAnimator) AnimatorInflater.loadAnimator(this,R.animator.animatorcolor);
        objectAnimator.setEvaluator(new ArgbEvaluator());
        objectAnimator.setTarget(mTv1);
        objectAnimator.start();

效果:

GIF.gif

更多动画


结语

到此动画部分到此结束。后期根据自身的理解如果学习到了新得关于动画的知识会继续记录动画相关的知识点。这是本人的学习笔记。十分感谢启航大神。也希望大家多多支持。下篇文章将会讲解Acitvity启动布局的加载。


感谢

站在巨人的肩膀上可以让我们看的更远。
Android自定义控件三部曲文章

上一篇下一篇

猜你喜欢

热点阅读