1 介绍
Android 应用开发过程中经常需要实现各种各样的动画效果,随着交互设计师给出的动画效果日趋复杂和高级,Android的动画框架也在不断的进化以满足需求,Android 发展到今天,有多少种可以使用的动画类型尼?在Android3.0之前,我们能使用的动画类型是逐帧动画和补间动画,在Android3.0发布时候,又出现了属性动画,而在Android 4.4时候又出现了android.transition框架,这使得开发者,可以通过更直观的方式定义动画效果。
2 逐帧动画(Frame Animation)
逐帧动画也叫做Drawable Animation,是最简单最直观的动画类型,就是由设计师给出一系列状态不断变化的图片,开发者可以指定动画中每一帧对应的图片和持续时间,然后就可以播放动画了,具体而言,有两种方式定义逐帧动画,分别是采用XML资源文件或代码实现。
2.1 案例-XML实现
<?xml version="1.0" encoding="utf-8" ?>
<animation-list xmlns:android="http://scheme.android.com/apk/res/android" android:oneshot="false">
<item
android:drawable="@drawable/common_loading_01"
android:duration="120"/>
<item
android:drawable="@drawable/common_loading_02"
android:duration="120"/>
<item
android:drawable="@drawable/common_loading_03"
android:duration="120"/>
</animation-list >
其中,android:oneshot=”false” 表示循环播放图片,取true表示不循环播放,android:duration=”120”指定每一帧的播放持续时间。
2.2 案例-代码实现
AnimationDrawable an=new AnimationDrawable();
for(int i=1;i<5;i++){
int id = getResources().getIdentifier("common_loading_" + i, "drawable", getPackageName());
Drawable drawable = getResources().getDrawable(id);
an.addFrame(drawable, 120);
}
ImageView img = new ImageView(this);
img.setBackgroundDrawable(an);
an.setOneShot(false);
定义好逐帧动画后,在某一个时刻触发该动画,代码如下:
AnimationDrawable animationDrawable = (AnimationDrawable) img.getBackground();
animationDrawable.start();
animationDrawable.stop();
3 补间动画(Tween Animation)
补间动画是指开发者无需定义动画过程中的每一帧,只需要定义动画的开发和结束这两个关键帧,并指定动画变化的时间和方式等,然后交由Android系统进行计算,通过在两个关键帧之间插入渐变值来实现平滑过渡,从而对View的内容完成一系列的图形变化来实现的动画效果,主要包括四种基本效果:透明变化Alpha、大小变化Scale、位移变化Translate以及旋转变化Rotate,这四种效果可以动态组合,从而实现复杂灵活的动画,同样,定义补间动画也可以分为XML资源文件和代码两种方式。
3.1 插值器Interpolator
前面所说的Android系统会在补间动画的开始和结束关键帧之间插入渐变值,它依据的便是Interpolator。具体说,Interpolator会根据类型的不同,选择不同算法计算出在补间动画期间所需要动态插入帧的密度和位置,Interpolator负责控制动画的变化速度,使得前面所说的四种基本动画效果能以匀速、加速、减速、抛物线等多种速度进行变化。
从代码中可以知道,Interpolator类其实是一个空接口,它继承自TimeInterpolator,TimeInterpolator时间插值器允许动画进行非线性动画变化,该接口中只有 float getInterpolation(float input)这个方法,输入参数0.0~1.0的值,返回值可以小于0.0也可以大于1.0。代码如下:
public interface Interpolator extends TimeInterpolator{
}
public interface TimeInterpolator{
float getInterpolation(float input);
}
当然我们也可以通过实现Interpolator接口来编写自己的插值器。
3.2 AlphaAnimation
3.2.1 xml实现方式
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_decelerate_interpolator">
<translate android:duration="200"
android:fromYDelta="100%p" android:toYDelta="0"/>
<!--toAlpha 1.0表示完全不透明 0.0表示完全透明-->
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="200"/>
</set>
3.2.2 代码实现方式
AlphaAnimation的构造函数只有两个参数,分别是初始化的透明度和结束的透明度:
public AlphaAnimation(float fromAlpha,float toAlpha){
mFromAlpha=fromAlpha;
mToAlpha=toAlpha;
}
代码中实现透明度,只需要创建一个AlphaAnimation实例,然后设置动画持续时间即可:
AlphaAnimation anim=new AlphaAnimation(0,1);
anim.setDuration(500);
anim.setFillAfter(true);
imgview.setAnimation(anim);
3.3 ScaleAnimation
3.3.1 XML实现案例
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator">
<scale
android:duration="2000"
android:fromXScale="0.2"
android:fromYScale="0.2"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1.5"
android:toYScale="1.5"
/>
</set>
3.3.2 代码实现方式
public ScaleAnimation(float fromX, float toX, float fromY, float toY,
float pivotX, float pivotY) {
}
public ScaleAnimation(float fromX, float toX, float fromY, float toY) {
}
public ScaleAnimation(float fromX, float toX, float fromY, float toY,
int pivotXType, float pivotXValue, int pivotYType, float pivotYValue) {
}
代码实现:
ScaleAnimation scaleAnimation=new ScaleAnimation(1.0f,4.0f,1.0f,4.0f, Animation.RELATIVE_TO_SELF,0.0f,Animation.RELATIVE_TO_SELF,0.0f);
scaleAnimation.setDuration(2000);
scaleAnimation.setFillAfter(true);
img.setAnimation(scaleAnimation);
含义说明:
3.4 TranslateAnimation
3.4.1 xml实现方式
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator">
<translate android:fromYDelta="0"
android:fromXDelta="0"
android:toXDelta="0"
android:toYDelta="1000"
android:duration="200"/>
</set>
3.4.2 代码实现方式
可用构造函数:
public TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) {
}
public TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,
int fromYType, float fromYValue, int toYType, float toYValue) {
}
含义说明:
代码实现:
TranslateAnimation animation=new TranslateAnimation( Animation.RELATIVE_TO_SELF,0f,Animation.RELATIVE_TO_SELF,2f,Animation.RELATIVE_TO_SELF,0f,Animation.RELATIVE_TO_SELF,2f);
animation.setDuration(3000);
animation.setFillAfter(true);
img.setAnimation(animation);
3.5 RotateAnimation
3.5.1 XML实现方式
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<rotate android:duration="1000"
android:fromDegrees="0"
android:pivotY="50%"
android:pivotX="50%"
android:startOffset="0"
android:repeatCount="-1"
android:repeatMode="restart"
android:toDegrees="360"
/>
</set>
3.5.2代码实现
public RotateAnimation(float fromDegrees, float toDegrees) {
}
public RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY) {
}
public RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,
}
说明:
代码实现:
RotateAnimation rotateAnimation = new RotateAnimation(0, -720, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation.setDuration(1000);
rotateAnimation.setFillAfter(true);
img.setAnimation(rotateAnimation);
3.5.3自定义补间动画
自定义补间动画只需要继承Animation,并重写这个抽象基类的applyTransfromation方法,在其中实现具体的动画变化逻辑,代码如下:
public class MyAnimation extends Animation{
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime,t);
}
}
4 属性动画(Property Animation)
属性动画是在Android 3.0中引入的,在补间动画中,我们只能改变View的绘制效果,View的真实属性是没有变化的,补间动画还有一个致命的缺陷,就是它只是改变了View的显示效果而已,而不会真正去改变View的属性。什么意思呢?比如说,现在屏幕的左上角有一个按钮,然后我们通过补间动画将它移动到了屏幕的右下角,现在你可以去尝试点击一下这个按钮,点击事件是绝对不会触发的,因为实际上这个按钮还是停留在屏幕的左上角,只不过补间动画将这个按钮绘制到了屏幕的右下角而已。而属性动画则可以直接改变View对象的属性值,同时属性动画几乎可以对任何对象执行动画,而不是局限在View对象上,比如我们有一个自定义的View,在这个View当中有一个Point对象用于管理坐标,然后在onDraw()方法当中就是根据这个Point对象的坐标值来进行绘制的。也就是说,如果我们可以对Point对象进行动画操作,那么整个自定义View的动画效果就有了。显然,补间动画是不具备这个功能的,这是它的第一个缺陷。从某种意义上讲,属性动画可以说是增强版的补间动画。属性动画的实现机制是通过对目标对象进行赋值并修改其属性来实现的。
属性动画的基类是Animator,他是一个抽象类,所以不会直接使用这个类,通常都是继承他并重写其中的相关方法,默认提供几个类:
4.1 Evaluator
在介绍Animator 的子类之前,我们首先来了解一个名为Evaluator 的概念,他是控制属性动画如何计算属性值的。他的接口定义是TypeEvaluator ,其中定义了evaluate方法,供不同不类型的子类实现。
public interface TypeEvaluator<T>{
public T evaluate(float fraction,T startValue,T endValue);
}
常见的实现类有IntEvaluator、FloatEvaluator、ArgbEvaluator等。下面我们来看一下ArgbEvaluator 的具体实现,可以知道就是根据初始值和结束值及一个对比度,计算出每一个进度对应的Argb值:
public class ArgbEvaluator implements TypeEvaluator {
private static final ArgbEvaluator sInstance = new ArgbEvaluator();
public static ArgbEvaluator getInstance() {
return sInstance;
}
public Object evaluate(float fraction, Object startValue, Object endValue) {
int startInt = (Integer) startValue;
float startA = ((startInt >> 24) & 0xff) / 255.0f;
float startR = ((startInt >> 16) & 0xff) / 255.0f;
float startG = ((startInt >> 8) & 0xff) / 255.0f;
float startB = ( startInt & 0xff) / 255.0f;
int endInt = (Integer) endValue;
float endA = ((endInt >> 24) & 0xff) / 255.0f;
float endR = ((endInt >> 16) & 0xff) / 255.0f;
float endG = ((endInt >> 8) & 0xff) / 255.0f;
float endB = ( endInt & 0xff) / 255.0f;
// convert from sRGB to linear
startR = (float) Math.pow(startR, 2.2);
startG = (float) Math.pow(startG, 2.2);
startB = (float) Math.pow(startB, 2.2);
endR = (float) Math.pow(endR, 2.2);
endG = (float) Math.pow(endG, 2.2);
endB = (float) Math.pow(endB, 2.2);
float a = startA + fraction * (endA - startA);
float r = startR + fraction * (endR - startR);
float g = startG + fraction * (endG - startG);
float b = startB + fraction * (endB - startB);
a = a * 255.0f;
r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;
return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
}
}
4.2 ValueAnimator
ValueAnimator 是属性动画中最重要的一个类,继承自Animator。它定义了属性动画大部分核心功能,包括计算各帧的属性值、处理更新事件、按照属性值的类型控制计算规则等。一个完整的属性动画由下面两部分组成:
- 计算动画各个帧的相关属性值
- 将这些属性值设置给指定的对象
而ValueAnimator为开发者实现了第一部分的功能,第二部分功能由开发者自行设置。ValueAnimator的构造函数是空实现,一般都是使用静态工厂方法来实例化。
public static ValueAnimator ofInt(int... values) {
ValueAnimator anim = new ValueAnimator();
anim.setIntValues(values);
return anim;
}
public static ValueAnimator ofArgb(int... values) {
ValueAnimator anim = new ValueAnimator();
anim.setIntValues(values);
anim.setEvaluator(ArgbEvaluator.getInstance());
return anim;
}
public static ValueAnimator ofFloat(float... values) {
ValueAnimator anim = new ValueAnimator();
anim.setFloatValues(values);
return anim;
}
public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values) {
ValueAnimator anim = new ValueAnimator();
anim.setValues(values);
return anim;
}
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) {
ValueAnimator anim = new ValueAnimator();
anim.setObjectValues(values);
anim.setEvaluator(evaluator);
return anim;
}
获取实例后,接着需要设置动画持续时间、插值方式、重复次数等属性值,然后启动动画,可以为ValueAnimator注册AnimatorUpdateListener监听器,并在这个监听器的onAnimatorUpdate方法中计算出来的属性值设置给指定对象,可以从下面案例中看出使用方式:
int curColor = this.getWindow().getStatusBarColor();
ValueAnimator valueAnimator=ValueAnimator.ofObject(new ArgbEvaluator(),curColor,color);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
MainActivity.this.getWindow().setStatusBarColor((Integer) animation.getAnimatedValue());
}
});
valueAnimator.setDuration(3000).setStartDelay(100);
valueAnimator.start();
setStartDelay()方法来设置动画延迟播放的时间,调用setRepeatCount()和setRepeatMode()方法来设置动画循环播放的次数以及循环播放的模式,循环模式包括RESTART和REVERSE两种,分别表示重新播放和倒序播放的意思。
4.3 ObjectAnimator
ObjectAnimator 是ValueAnimator的子类,封装实现了上面所说的第二部分的功能,因此在实际开发中用的最多的是ObjectAnimator,只有在ObjectAnimator 中实现不了的场景下才会考虑用ValueAnimator。ObjectAnimator 和 ValueAnimator 在构造实例时最大的不同是需要指定动画作用的具体对象和对象的属性名,而且一般不需要注册AnimatorUpdateListener监听器,示例如下:
ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);
animator.setDuration(5000);
animator.start();
ObjectAnimator的用法还算是相当简单吧,但是我相信肯定会有不少朋友现在心里都有同样一个疑问,就是ofFloat()方法的第二个参数到底可以传哪些值呢?目前我们使用过了alpha、rotation、translationX和scaleY这几个值,分别可以完成淡入淡出、旋转、水平移动、垂直缩放这几种动画,那么还有哪些值是可以使用的呢?其实这个问题的答案非常玄乎,就是我们可以传入任意的值到ofFloat()方法的第二个参数当中。任意的值?相信这很出乎大家的意料吧,但事实就是如此。因为ObjectAnimator在设计的时候就没有针对于View来进行设计,而是针对于任意对象的,它所负责的工作就是不断地向某个对象中的某个属性进行赋值,然后对象根据属性值的改变再来决定如何展现出来。那么比如说我们调用下面这样一段代码:
ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f);
其实这段代码的意思就是ObjectAnimator会帮我们不断地改变textview对象中alpha属性的值,从1f变化到0f。然后textview对象需要根据alpha属性值的改变来不断刷新界面的显示,从而让用户可以看出淡入淡出的动画效果。
那么textview对象中是不是有alpha属性这个值呢?没有,不仅textview没有这个属性,连它所有的父类也是没有这个属性的!这就奇怪了,textview当中并没有alpha这个属性,ObjectAnimator是如何进行操作的呢?其实ObjectAnimator内部的工作机制并不是直接对我们传入的属性名进行操作的,而是会去寻找这个属性名对应的get和set方法,因此alpha属性所对应的get和set方法应该就是:
public void setAlpha(float value);
public float getAlpha();
那么textview对象中是否有这两个方法呢?确实有,并且这两个方法是由View对象提供的,也就是说不仅TextView可以使用这个属性来进行淡入淡出动画操作,任何继承自View的对象都可以的。
既然alpha是这个样子,相信大家一定已经明白了,前面我们所用的所有属性都是这个工作原理,那么View当中一定也存在着setRotation()、getRotation()、setTranslationX()、getTranslationX()、setScaleY()、getScaleY()这些方法,不信的话你可以到View当中去找一下。
使用ObjectAnimator有以下几点需要注意:
- 需要为对象对应的属性提供setter方法。例如,上面的alpha属性在父类有相应的set方法。
- 如果动画的对象是View,那么为了能显示动画效果,在某些情况下,可能还需要注册AnimatorUpdateListener监听器,并在其回调方法onAnimatorUpdate中调用View的invalidate方法来刷新View的现实。
4.3.1 XML资源文件实现
在res/animator下配置xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
<objectAnimator
android:duration="2000"
android:propertyName="scaleX"
android:valueFrom="1"
android:valueTo="0.4"
android:valueType="floatType"/>
<objectAnimator
android:duration="2000"
android:propertyName="scaleY"
android:valueFrom="1"
android:valueTo="0.4"
android:valueType="floatType"/>
</set>
加载xml文件:
Animator animator = AnimatorInflater.loadAnimator(this, R.animator.scale);
animator.setTarget(img);
animator.start();
4.4 AnimatorSet
实现组合动画功能主要需要借助AnimatorSet这个类,这个类提供了一个play()方法,如果我们向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimator)将会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括以下四个方法:
- after(Animator anim) 将现有动画插入到传入的动画之后执行
- after(long delay) 将现有动画延迟指定毫秒后执行
- before(Animator anim) 将现有动画插入到传入的动画之前执行
- with(Animator anim) 将现有动画和传入的动画同时执行
好的,有了这四个方法,我们就可以完成组合动画的逻辑了,那么比如说我们想要让TextView先从屏幕外移动进屏幕,然后开始旋转360度,旋转的同时进行淡入淡出操作,就可以这样写:
ObjectAnimator moveIn = ObjectAnimator.ofFloat(textview, "translationX", -500f, 0f);
ObjectAnimator rotate = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);
ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);
AnimatorSet animSet = new AnimatorSet();
animSet.play(rotate).with(fadeInOut).after(moveIn);
animSet.setDuration(5000);
animSet.start();
可以看到,这里我们先是把三个动画的对象全部创建出来,然后new出一个AnimatorSet对象之后将这三个动画对象进行播放排序,让旋转和淡入淡出动画同时进行,并把它们插入到了平移动画的后面,最后是设置动画时长以及启动动画。
5 过渡动画(Transition Animation)
过渡动画实在Android 4.4中引入的动画框架其实质上是对属性动画框架的封装,从而方便开发者实现Activity或View的过渡动画效果。和属性动画相比,过渡动画最大的不同是需要为动画前后准备不同的布局,并通过对应的API实现两个布局的过渡动画,而属性动画只需要一个布局文件。
在使用Transition Animation 框架实现动画效果之前,我们先来了解下这个框架的几个基本概念。
- scene:定义了页面的当前状态信息,Scene的实例化一般通过静态工厂方法实现。
public static Scene getSceneForLayout(ViewGroup sceneRoot,int layoutId,Context context){
}
- Transition:定义了界面之间切换的动画信息,在使用TransitionManager时并没有指定使用哪一个Transition,那么会默认使用AutoTransition,源码如下,可以看出AutoTransition的动画效果就是先隐藏对象变透明,然后移动指定对象,最后显示出来。
public class AutoTransition extends TransitionSet {
public AutoTransition() {
init();
}
public AutoTransition(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
setOrdering(ORDERING_SEQUENTIAL);
addTransition(new Fade(Fade.OUT)).
addTransition(new ChangeBounds()).
addTransition(new Fade(Fade.IN));
}
}
- TransitionManager:控制Scene之间切换的控制器,切换常用的方法有以下两个,其中的sDefaultTransition就是前面说的AutoTransition的实例。
public static void go(Scene scene){
changeScene(scene,sDefaultTransition);
}
public static void go(Scene scene,Transition transition){
changeScene(scene,transition);
}
5.1 案例
过渡动画的使用很简单,首先定义同一个页面的两个布局,分别是动画前的布局和动画后的布局,我们将其分别命名为fragment_transition_scene_before.xml 和 fragment_transition_scene_after.xml,而这两个布局文件有一个相同Id的根布局,动画前的布局文件如下:
<!--动画前布局文件-->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="mgzxc.myapplication.MainActivity">
<Button
android:id="@+id/bton"
android:text="按钮"
android:textSize="20sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
<!--动画结束布局文件-->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="mgzxc.myapplication.MainActivity">
<Button
android:id="@+id/bton"
android:text="按钮"
android:textSize="20sp"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
代码实现:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_before);
/*代码实现动画方式*/
container = ((ViewGroup) findViewById(R.id.container));
scene = Scene.getSceneForLayout(container, R.layout.activity_main_after, this);
bton=((Button) findViewById(R.id.bton));
bton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ChangeBounds changeBounds = new ChangeBounds();
changeBounds.setDuration(2000);
Fade fadeout = new Fade(Fade.OUT);
fadeout.setDuration(50);
Fade fadein = new Fade(Fade.IN);
fadein.setDuration(50);
TransitionSet transitionSet = new TransitionSet();
transitionSet.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
transitionSet.addTransition(fadeout)
.addTransition(changeBounds)
.addTransition(fadein);
TransitionManager.go(scene, transitionSet);
}
});
// xml实现方式
/* bton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
TransitionInflater transitionInflater = TransitionInflater.from(MainActivity.this);
transitionmanager=transitionInflater.inflateTransitionManager(R.transition.transition_manager, container);
// scene1 = Scene.getSceneForLayout(container, R.layout.activity_main_before, MainActivity.this);
scene2 = Scene.getSceneForLayout(container, R.layout.activity_main_after, MainActivity.this);
transitionmanager.transitionTo(scene2);
}
});*/
}
transition_manager对应的xml文件须在transition里定义如下
<?xml version="1.0" encoding="utf-8"?>
<transitionManager xmlns:android="http://schemas.android.com/apk/res/android">
<transition
android:fromScene="@layout/fragment_transition_scene_before"
android:toScene="@layout/fragment_transition_scene_after"
android:transition="@transition/slow_auto_transition"/>
</transitionManager>
同理 slow_auto_transition 定义如下:
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android" android:transitionOrdering="sequential">
<fade
android:duration="2000"
android:fadingMode="fade_out"/>
<changeBounds
android:duration="9000"
android:interpolator="@android:interpolator/anticipate_overshoot"
/>
<fade
android:fadingMode="fade_in"
android:duration="2000" />
</transitionSet>
最终效果: