属性动画详细介绍(一)
文章目录
- 一、属性动画基础内容
- 二、ValueAnimator
- 2.1、使用ValueAnimator
- 2.2、ValueAnimator的常用方法
- 2.1.1、其它静态工厂方法
- 2.1.2、常用对象方法
- 2.1.3、常用监听方法
- 2.3、Interpolator插值器
- 2.4、Evaluator
- 2.5、ofObject
- 三、ObjectAnimator
- 3.1、这是什么
- 3.2、使用ObjectAnimator
- 3.2.1、什么时候需要提供对应属性的get方法?
- 四、AnimatorSet
- 4.1、playSequentially()
- 4.2、playTogether()
- 4.3、AnimatorSet.Builder
属性动画我决定用两篇文章做总结
一、属性动画基础内容
二、ValueAnimator
从名字就可以看出,这是针对值的动画,它并不会对View做出任何动画效果。使用ValueAnimator可以让某一个值在设定时间内平滑过渡成另一个值,根据值的变换过程,自己操作View的变换。
2.1、使用ValueAnimator
要使用ValueAnimator非常简单,首先通过ValueAnimator提供的静态方法创建其对象,然后设置动画的时长,最后调用start()方法开启动画即可。下面我们来创建一个ValueAnimator,它能在2秒内从0变换到500:
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 500);
valueAnimator.setDuration(2000);
valueAnimator.start();
没错就是这么简单,运行后的确在两秒内值从0一直平滑过渡到了500。可是我们需要过程而不是结果呀,如果没办法监听到值变换的过程,就没办法利用变换中的值给View设置动画了。要想监听值变化的过程,我们可以使用addUpdateListener()
方法给valueAnimator加上监听事件:
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {int value = (int) animation.getAnimatedValue();Log.i("HurryYu", "value:" + value);}
});
在回调中,animation表示当前ValueAnimator对象,使用animation.getAnimatedValue()
就可以获取到当前变换的值(默认是返回Object类型,由于使用的是ofInt()创建的ValueAnimator对象,因此可以直接强转为int类型)。此时观察Logcat,得到如下输出:
2019-05-11 15:19:36.858 14906-14906/com.hurryyu.viewdemo I/HurryYu: value:0
2019-05-11 15:19:36.960 14906-14906/com.hurryyu.viewdemo I/HurryYu: value:0
2019-05-11 15:19:37.253 14906-14906/com.hurryyu.viewdemo I/HurryYu: value:26
2019-05-11 15:19:37.626 14906-14906/com.hurryyu.viewdemo I/HurryYu: value:121
2019-05-11 15:19:37.654 14906-14906/com.hurryyu.viewdemo I/HurryYu: value:134
2019-05-11 15:19:37.669 14906-14906/com.hurryyu.viewdemo I/HurryYu: value:140
2019-05-11 15:19:37.695 14906-14906/com.hurryyu.viewdemo I/HurryYu: value:147
2019-05-11 15:19:37.705 14906-14906/com.hurryyu.viewdemo I/HurryYu: value:153
.
.
.
2019-05-11 15:19:38.885 14906-14906/com.hurryyu.viewdemo I/HurryYu: value:498
2019-05-11 15:19:38.903 14906-14906/com.hurryyu.viewdemo I/HurryYu: value:499
2019-05-11 15:19:38.941 14906-14906/com.hurryyu.viewdemo I/HurryYu: value:499
2019-05-11 15:19:38.958 14906-14906/com.hurryyu.viewdemo I/HurryYu: value:500
现在明白ValueAnimator了吧,实际上就是对给定区间的值进行平滑变换,我们需要自己监听值的变换过程,然后自己对View执行相应的动画效果。就拿上面的这个从0平滑过渡到500的ValueAnimator做实验,现在我们准备利用它让View在X轴方向上向右平移,首先我们完成布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><Viewandroid:id="@+id/view"android:layout_width="50dp"android:layout_height="50dp"android:background="@color/colorPrimary" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="HurryYu" />
</LinearLayout>
效果如下图:
下面完成代码部分:
public class MainActivity extends AppCompatActivity {private View view;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);view = findViewById(R.id.view);view.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(MainActivity.this, "看看有效果吗?",Toast.LENGTH_SHORT).show();}});startAnim();}private void startAnim() {ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 500);valueAnimator.setDuration(2000);valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {int value = (int) animation.getAnimatedValue();view.setTranslationX(value);}});valueAnimator.start();}
}
请注意看ValueAnimator的回调中,我们使用了view.setTranslationX(value)
来改变view在X轴上的偏移量,其中value会在两秒内从0变换到500。最终动画完成后的效果如图:
点击动画完成后的View,仍然可以响应点击事件。不过奇怪的是,父布局是水平线性布局,View向右移动后,TextView应该也会跟着向右移动呀,可是为什么没有呢?因为我们使用的是setTranslationX()
去改变view的偏移量,它的确能改变view的位置,但它并不会改变view的LayoutParams中的margin属性值,即不会影响getLeft()与getRight()。
2.2、ValueAnimator的常用方法
2.1.1、其它静态工厂方法
之前我们已经使用过ValueAnimator.ofInt()方法创建了对象,除了ofInt()以外,它还提供了如下静态工程方法:
①、来看ofArgb,ofInt是对int类型的数字进行变换,而它的作用是可以对颜色进行过度变换,我们还注意到,这些方法接收的都是可变参数,意味可以传入任意个参数,它将依次变换。现在我想把按钮的颜色从红色过度到绿色再过度到蓝色,首先创建ValueAnimator:
private void changeColor() {ValueAnimator valueAnimator = ValueAnimator.ofArgb(0xFFFF5454, 0xFF5DDE5D, 0xFF5DBEDE);valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {int color = (int) animation.getAnimatedValue();button.setBackgroundColor(color);}});valueAnimator.setDuration(2000);valueAnimator.start();
}
这里注意ofArgb需要接收ARGB的颜色。效果如下所示:
②、ofFloat就不必多说了吧。
③、ofObject这个我们后面再来研究,因为它需要我们提供自定义的Evaluator。
2.1.2、常用对象方法
前面我们已经接触过了几个ValueAnimator提供的方法,比如setDuration用来设置动画时长,getAnimatedValue用来获取当前变换中的值,start用来开始动画。下面我们再来学习几个常用的方法:
①、setRepeatCount用于设置动画的重复次数,传入0表示不重复,传入ValueAnimator.INFINITE表示无限重复,默认是不重复,这个方法比较好理解。
②、setRepeatMode用于设置动画重复的模式,有两种选择,一种是正序重复(ValueAnimator.RESTART),另一种是倒序重复(ValueAnimator.REVERSE),默认是正序重复。这两种有什么区别呢?拿按钮变颜色的例子来说,如果我们把重复次数设定为无限重复,那么每一次重复时,都会从最后的蓝色突然变成开始的红色然后继续新一轮动画,这就是正序:
而如果将RepeatMode设置为ValueAnimator.REVERSE,将使用倒序的方式进行动画的重复,即:
第一轮:红-绿-蓝
第二轮:蓝-绿-红
第三轮:红-绿-蓝
以此类推,这样就不存在突然从第三种颜色变为第一种颜色的情况:
③、cancel用于取消当前动画。
2.1.3、常用监听方法
我们已经使用过一个监听(addUpdateListener),它用于监听动画执行过程中值的变化。ValueAnimator除了能监听值的变化以外,还能监听动画执行过程中的状态,可以使用addListener进行添加:
valueAnimator.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {}@Overridepublic void onAnimationEnd(Animator animation) {}@Overridepublic void onAnimationCancel(Animator animation) {}@Overridepublic void onAnimationRepeat(Animator animation) {}
});
onAnimationStart:显然是在开始执行动画的时候调用。
onAnimationRepeat:在每次重复执行时调用。
onAnimationCancel:在取消动画的时候调用。
onAnimationEnd:在动画结束的时候调用,注意,无论是正常结束还是调用cancel结束,此方法都会被回调。
如果我们只想监听这四种动画状态中的其中一个或是少数几个,实现Animator.AnimatorListener这个接口的匿名内部类代价就太大了,因此我们可以使用AnimatorListenerAdapter这个抽象类,实际上它对Animator.AnimatorListener里面的方法都做了空实现,我们需要用到哪个方法就去重写哪个方法就行了:
valueAnimator.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationStart(Animator animation) {}
});
顺便提一句,为何对Animator.AnimatorListener里的方法都提供了空实现,还要把类声明成abstract的呢?这是因为abstract类不能被直接实例化,这样就能逼迫调用者至少去重写里面的一个方法。
2.3、Interpolator插值器
插值器的作用是控制动画在执行过程中的速度,比如有一个valueAnimator,它能使一个值在两秒内从0过渡到200,那过渡中的速度到底是怎么样的呢?先快后慢?匀速?先慢后快?控制值在变化中的速度,就是靠Interpolator来完成的。
系统已经为我们提供了非常多的插值器供我们选择,这里选择最为简单的一个插值器(LinearInterpolator)来研究下Interpolator:
public class LinearInterpolator implements Interpolator {public LinearInterpolator() {}@overridepublic float getInterpolation(float input) {return input;}
}
我对LinearInterpolator类的代码稍作删减,便于查看。首先它实现了Interpolator接口,只要实现了Interpolator(TimeInterpolator)接口的类就可以是一个插值器。我们来看看Interpolator接口的代码:
public interface Interpolator extends TimeInterpolator {}
这个接口只是继承了TimeInterpolator接口,除此之外什么都没做。来看最终的TimeInterpolator接口:
public interface TimeInterpolator {float getInterpolation(float input);
}
因此在插值器中只需要实现getInterpolation()方法就可以了。重点也就是在这个方法上,下面我将详细说明一下这个方法的作用:它接收一个float类型的参数,这个参数的意思是表示当前动画执行到的进度,取值范围是0~1,在动画刚刚开始时,值为0,在动画完成时,值为1。这个进度值时系统帮我们计算出的,它永远是匀速增加的,不受任何设置的影响。比如一秒内值从1变换成10,在0.5秒时,值应该为5,在0.8秒时,值应该为8,这就是匀速变化。我们可以参考系统计算出的当前动画的进度值,计算并返回另一个进度值,这个进度值将会影响到AnimatorUpdateListener中数值的取值,最终达到控制数值变化速度的作用。LinearInterpolator是一个线性的插值器,因此它能使动画在执行过程中始终保持匀速执行,因此它在getInterpolation方法中直接返回了参数input。我们可以使用setInterpolator()方法给动画设置插值器,**在ValueAnimator中,如果没有显式设置插值器,会默认使用AccelerateDecelerateInterpolator作为默认插值器。**AccelerateDecelerateInterpolator会使动画在开始和结束时变化缓慢,在中间部分变化较快。除此之外还有
AccelerateDecelerateInterpolator, AccelerateInterpolator, AnticipateInterpolator, AnticipateOvershootInterpolator, BaseInterpolator, BounceInterpolator, CycleInterpolator, DecelerateInterpolator, LinearInterpolator, OvershootInterpolator, PathInterpolator
这些插值器,具体效果可查阅相关资料。
2.4、Evaluator
Evaluator的作用就是将插值器返回的进度值转换成对应的数值。在详细介绍Evaluator前,我们先来梳理一下AnimatorUpdateListener中是怎么得到当前变化的值?
通过上图相信已经能很清晰的区别出Interpolator与Evaluator的区别了,前者是返回数值区间变化的进度,范围只能是0~1之间,而后者是返回当前进度对应的具体值,这个就跟我们使用哪种静态工厂有关了,如果我们使用ofInt,那么Evaluator计算后的返回值应该是int类型;如果使用ofFloat,那么Evaluator计算后的返回值应该是float类型。所以说Interpolator是可以通用的,而Evaluator是专用的。ValueAnimator中提供了设置Evaluator的方法:setEvaluator(),下面我们以ofFloat为例,分析一下这个Evaluator:
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 200);
valueAnimator.setDuration(2000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {}
});
valueAnimator.start();
我们知道,如果没有显式指定Interpolator,它将默认使用AccelerateDecelerateInterpolator,但是Evaluator呢?在PropertyValuesHolder这个类中,找到了如下内容:
private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();void init() {if (mEvaluator == null) {mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :(mValueType == Float.class) ? sFloatEvaluator :null;}if (mEvaluator != null) {mKeyframes.setEvaluator(mEvaluator);}
}
如果是ofFloat,就会使用FloatEvaluator,如果是ofInt,就会使用IntEvaluator。如果之前设置过自定义Evaluator,就会使用自定义的Evaluator,如:
public static ValueAnimator ofArgb(int... values) {ValueAnimator anim = new ValueAnimator();anim.setIntValues(values);anim.setEvaluator(ArgbEvaluator.getInstance());return anim;
}
ofArgb()就在静态工厂方法中给ValueAnimator设置了ArgbEvaluator。下面我将详细分析FloatEvaluator:
public class FloatEvaluator implements TypeEvaluator<Number> {public Float evaluate(float fraction, Number startValue, Number endValue) {float startFloat = startValue.floatValue();return startFloat + fraction * (endValue.floatValue() - startFloat);}
}
可以发现,它实现了TypeEvaluator接口:
public interface TypeEvaluator<T> {public T evaluate(float fraction, T startValue, T endValue);
}
下面对参数作出解释:
- fraction:这个参数就是插值器返回的值,表示当前动画对应的值进度(0~1)
- startValue:表示我们所设置的值区间的开始值(例如ofFloat(0, 200),则startValue为0)
- endValue:表示我们所设置的值区间的结束值(例如ofFloat(0, 200),则startValue为200)
- 返回值表示计算后的具体值,也就是我们在AnimatorUpdateListener->onAnimationUpdate中通过
animation.getAnimatedValue()
所得到的值
现在我们回过头来看FloatEvaluator中的evaluate()方法:
public Float evaluate(float fraction, Number startValue, Number endValue) {float startFloat = startValue.floatValue();return startFloat + fraction * (endValue.floatValue() - startFloat);
}
这就应该很好理解了,为了计算出在某一进度时对应的值,采用了如下公式:
当前值 = 开始值 + 进度值 * (结束值 - 开始值)
2.5、ofObject
学习完了Evaluator以后,我们在来看下ValueAnimator提供的静态工厂方法:
其中大部分我们都已经使用过了,但是还有一个ofObject似乎我们从未提起过,之所以之前没有提起,是因为还没有学习Evaluator,而现在是时候了解一下它了!
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)
这就是ofObject()方法的原型,可以看到它接收一个Evaluator和一个Object类型的可变参数。这就意味着我们可以处理任意类型的值。可是为什么还要传入Evaluator呢?这是因为Evaluator的作用是根据进度计算出当前进度所对应的值,而现在这个Object是我们自己传入的,系统并不知道如何去转换,因此必须要我们手动提供一个自定义Evaluator。下面我将使用ofObject()方法来实现模拟小球下落(X坐标与Y坐标都会发生变化),首先完成布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><Viewandroid:id="@+id/view_ball"android:layout_width="50dp"android:layout_height="50dp"android:background="@drawable/shape_ball" /></LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"><solid android:color="#35A9F7"/>
</shape>
因为我们准备实现的小球下落是X坐标与Y坐标都会发生变化,因此我们需要借助Point类来保存X坐标与Y坐标的值,在这里,我将自己定义Point类,而不使用android.graphics.Point,自定义Point如下:
public class Point {private int x;private int y;public Point() {}public Point(int x, int y) {this.x = x;this.y = y;}public int getX() {return x;}public void setX(int x) {this.x = x;}public int getY() {return y;}public void setY(int y) {this.y = y;}
}
好了,基本的东西都准备好了,下面创建ValueAnimator:
ValueAnimator.ofObject(???, new Point(0, 0), new Point(300, 500));
一开始就卡住了,原因是还没有提供自定义Evaluator。下面我们定义一个BallEvaluator:
public class BallEvaluator implements TypeEvaluator<Point> {private Point point = new Point();@Overridepublic Point evaluate(float fraction, Point startValue, Point endValue) {point.setX((int) (startValue.getX() + fraction * (endValue.getX() - startValue.getX())));point.setY((int) (startValue.getY() + fraction * (endValue.getY() - startValue.getY())));return point;}
}
现在我们可以继续实现代码了:
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);final View viewBall = findViewById(R.id.view_ball);ValueAnimator valueAnimator = ValueAnimator.ofObject(new BallEvaluator(),new Point(0, 0), new Point(300, 500));valueAnimator.setDuration(2000);valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {Point point = (Point) animation.getAnimatedValue();viewBall.layout(point.getX(), point.getY(), point.getX() + viewBall.getWidth(),point.getY() + viewBall.getHeight());}});valueAnimator.start();}
}
运行效果:
三、ObjectAnimator
3.1、这是什么
前面我们学习了ValueAnimator,难道大家没发现一个问题吗?ValueAnimator只能针对值进行动画改变,如果我们需要关联到View的变化,就需要设置监听事件,根据值得变化手动去操作这个View变化。这相比补间动画要麻烦很多。为了能让动画直接作用于View,Google基于ValueAnimator编写了ObjectAnimator。也就是说ObjectAnimator继承自ValueAnimator,ValueAnimator能用的方法在ObjectAnimator中照样可以用。但是ObjectAnimator重新编写了几个方法,例如:ofInt()、ofFloat()等。
3.2、使用ObjectAnimator
既然说ObjectAnimator能直接作用于View,想必一定很好用吧。下面我将使用ObjectAnimator实现让TextView从不透明->透明->不透明的alpha动画。按照惯例,先编写界面:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".MainActivity"><TextViewandroid:id="@+id/tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="HurryYu"android:textSize="18sp" /><Buttonandroid:id="@+id/btn_start"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="开始动画" /></LinearLayout>
然后编写业务代码:
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);final TextView tv = findViewById(R.id.tv);findViewById(R.id.btn_start).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(tv,"alpha",1,0,1);objectAnimator.setDuration(2000);objectAnimator.start();}});}
}
关键部分就是ObjectAnimator了:
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(tv, "alpha", 1, 0, 1);
objectAnimator.setDuration(2000);
objectAnimator.start();
ObjectAnimator的静态工厂方法比ValueAnimator的多出了两个参数,下面我将介绍多出的两个参数的含义:
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
- target:指定操作的是哪个控件
- propertyName:指定要操作这个控件的哪个属性
我们去TextView查找是否真的存在alpha这个属性,发现并没有。接着我们去它的父类中查找,也没有!那ObjectAnimator是如何改变透明度的呢?实际上它并不是直接修改第二个参数传入的属性,而是查找它对应的set方法来设置值。例如上面的例子中,我们传入“alpha”,则它会去TextView中找setAlpha()方法。那TextView中有这个方法吗?确实是有的,继承自View。
因此我们要使用ObjectAnimator实现动画,必须保证如下两点:
- 在要操作的控件中,必须存在对应属性的set方法,且该方法接收的参数类型要与静态工厂方法所用类型一致,例如ofInt()就要求set方法中接收的参数类型为int类型
- set方法的命名必须满足驼峰命名法,例如属性名为rotate,则对应的set方法必须为setRotate()
注意ObjectAnimator会在设定时间内不断调用对应属性的set方法,就像ValueAnimator中加监听后会不断回调onAnimationUpdate()方法一样。不过它也是仅仅调用对应属性的set方法,而set方法中对控件的操作还是要我们自己去实现的,只不过常用的操作系统已经为我们实现好。
3.2.1、什么时候需要提供对应属性的get方法?
如果我们只给第三个参数传入一个值,系统在执行动画以前就会先去调用对应属性的get方法获取初始值,如果没有提供get方法,则会使用默认值。通过ofInt()构造出的ObjectAnimator,属性的默认值为0;ofFloat()构造出来的,属性的默认值为0.0;ofObject()构造出来的,属性的默认值为null,这就有问题了,著名异常NullPointerException。因此,当动画只传入了一个过渡值时,系统会调用属性对应的get方法来获取初始值,如果没有提供get方法,则会使用属性类型对应的默认值,当无法正常获取到属性的初始值时,会直接报异常。
四、AnimatorSet
之前我们使用的ObjectAnimator和ValueAnimator都只能同时播放一种动画,能不能让多个动画同时播放或者按顺序依次播放呢?答案是使用AnimatorSet。我们先来看看最基本的使用方法:
4.1、playSequentially()
AnimatorSet为我们提供了playSequentially()方法,此方法可以接收一个可变长度的Animator或是一个List集合:
public void playSequentially(Animator... items)
public void playSequentially(List<Animator> items)
当然我认为一般情况下使用可变参数的那个更爽,省去了创建集合的麻烦。这个方法的作用是能依次执行传入的动画,一般情况下我们会传入ObjectAnimator而不是ValueAnimator,原因相信大家看过前面的内容后都能懂。需要特别说明的是:**它只能依次执行传入的动画,如果其中一个动画是无限重复的,那么它后面的动画将都不会执行。必须确保执行完成一个动画之后,才回去执行下一个。**它的完整用法如下:
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);final TextView tv = findViewById(R.id.tv);findViewById(R.id.btn_start).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(tv, "alpha", 1, 0, 1);ObjectAnimator rotateAnimator = ObjectAnimator.ofFloat(tv, "rotation", 0, 360, 0);ObjectAnimator scaleAnimator = ObjectAnimator.ofFloat(tv, "scaleX", 1, 2, 1);ObjectAnimator translateAnimator = ObjectAnimator.ofFloat(tv, "translationX", 0, 100, 0);AnimatorSet animatorSet = new AnimatorSet();animatorSet.playSequentially(alphaAnimator, rotateAnimator, scaleAnimator, translateAnimator);animatorSet.setDuration(2000);animatorSet.start();}});}
}
当然也可以单独给里面的每一个动画设置执行时间。但是请注意,如果单独给每个动画设置了执行时间,就不要再去调用AnimatorSet的setDuration(),否则单个动画设置的时间会被覆盖。
4.2、playTogether()
这个看名字就应该能猜出是让动画一起执行的方法,同样它也有一个重载:
public void playTogether(Animator... items)
public void playTogether(Collection<Animator> items)
其实这个方法**只负责同时开始传入的所有动画,至于里面的动画执行时间是多长?是不是一直重复?等等这些问题都与它没有关系。**它的完整用法和上面的playSequentially()是一模一样的。
4.3、AnimatorSet.Builder
Builder是AnimatorSet类中的内部类,它里面提供了一些方法,我们可以使用这些方法来组合出一组动画,它可以控制这组动画中先执行什么,后执行什么,什么与什么一起执行。下面我将列举Builder中每个方法的含义:
方法名 | 说明 |
---|---|
with | 设置当前动画与前一个动画一起执行 |
before | 设置当前动画在之前所有动画之后执行,也可理解为之前的动画都在这个动画之前执行 |
after | 设置当前动画在之前所有动画之前执行,也可理解为之前的所有动画都在这个动画之后执行 |
关于before与after的说明,我个人的理解与网上很多文章的理解有一些不同,但实验结果却是我这个说法是对的。如果读者发现我的理解错误,请指出,谢谢!
要得到AnimatorSet.Builder对象,只能使用AnimatorSet的play()方法,下面我将使用AnimatorSet.Builder实现一个组合动画:
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);final TextView tv = findViewById(R.id.tv);findViewById(R.id.btn_start).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {ObjectAnimator alphaAnimator =ObjectAnimator.ofFloat(tv, "alpha", 1, 0, 1);ObjectAnimator rotateAnimator =ObjectAnimator.ofFloat(tv, "rotation", 0, 360, 0);rotateAnimator.setDuration(15000);ObjectAnimator scaleAnimator =ObjectAnimator.ofFloat(tv, "scaleX", 1, 2, 1);ObjectAnimator translateAnimator =ObjectAnimator.ofFloat(tv, "translationX", 0, 100, 0);AnimatorSet animatorSet = new AnimatorSet();animatorSet.play(alphaAnimator).with(rotateAnimator).after(scaleAnimator).before(translateAnimator);animatorSet.setDuration(2000).start();}});}
}
这个动画的执行过程是:先执行scaleAnimator,当scaleAnimator执行完成后,alphaAnimator与rotateAnimator一起执行,等它们两个执行完成后,执行translateAnimator。
属性动画详细介绍(一)相关推荐
- vuex的计算属性_Vuex详细介绍
1. 什么是Vuex Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.这是官网的说法,其实很简单:就是一个加强版的data! 在单页应用中会有一个data函数,里面就存放了当前页面的一 ...
- Cadence OrCAD Capture管脚Passive和Power属性功能详细介绍图文教程
⏪<上一篇>
- Android --- AndroidManifest.xml文件内容详细介绍
文章目录 1.android:label="@string/app_name" 2. android:icon="@mipmap/ic_launcher"与an ...
- android开发笔记之属性动画
属性动画简单介绍 作用对象:任意 Java 对象 不再局限于 视图View对象 实现的动画效果:可自定义各种动画效果 不再局限于4种基本变换:平移.旋转.缩放 & 透明度 特点 作用对象进行了 ...
- Raft算法详细介绍
raft是一个共识算法(consensus algorithm),所谓共识,就是多个节点对某个事情达成一致的看法,即使是在部分节点故障.网络延时.网络分割的情况下.这些年最为火热的加密货币(比特币.区 ...
- Android属性动画实战教程开篇
本系列博客会分俩篇 本篇博客主要是会介绍属性动画代码使用和xml中使用 关于View动画和属性动画的区别不做过多的介绍,当然涉及到的地方会简单的提一下. 好了废话不多说,直接上内容 首先介绍代码中使用 ...
- Android动画完全解析--属性动画
一.概述 上篇博客介绍了View动画的简单使用和基本工作原理原理,这篇来学习下属性动画.和View动画不同的是,属性动画不再简单的使用平移.旋转.缩放.透明度这4种变换,代替它们的是ValueAnim ...
- Android动画(帧动画、补间动画、属性动画)讲解
Android动画(帧动画.补间动画.属性动画)讲解 首先我们来看看啥是帧动画.补间动画.属性动画. 介绍: 帧动画:是一种常见的动画形式(Frame By Frame),其原理是在"连续的 ...
- 三谈属性动画——Keyframe以及ViewPropertyAnimator
Android动画和Transition系列文章 初识属性动画--使用Animator创建动画 再谈属性动画--介绍以及自定义Interpolator插值器 三谈属性动画--Keyframe以及Vie ...
最新文章
- matlab训练神经网络模型并导入simulink详细步骤
- LOJ2195 旅行
- 1到n阶乘算法的改进
- 链表(Linked List)之双向链表
- 黑科技!当会爬虫的Python遇上会画图的FineBI……
- 初识Python(二)
- linux下mongodb的安装及启动
- 【第三方软件】利用WIN8系统自带的绘图软件获取图像信息(位置和颜色信息)
- 解除webservice上下传文件大小限制
- 行业认证标准:ISO 26262-汽车软件功能安全标准
- 用IDEA构建Vue项目(主要指令)
- Windows平台通过CMD查询域名的Whois信息
- 旧手机改电脑外挂,文本补充
- X书app数美-sid分析
- linux系统文件夹
- 解决“试图加载格式不正确的程序”问题
- 从大数据挖掘大智慧,华为创造AI时代速度新高度
- SEOER必备的经典外链知识
- chroot的作用及详解
- Go (Golang) 工具之自动化版本工具 gsemver | semver 语义化版本规范