Activity&Fragment过渡动画

在Android5.0以前,切换Activity或者Fragment时可以通过Activity#overridePendingTransition()或FragmentTransaction#setCustomAnimation()方法增加过场动画,不过效果比较单调,并且只能针对页面中的所有元素,具体使用可参考Android转场动画

在Android5.0以后,切换Activity或者Fragment时使用过渡动画(Transition Animation),它是Material Design的一部分,能为页面元素在不同状态之间的移动和转换提供视觉切换效果。过渡动画不但能够提供更加丰富的切换效果,并且在进行切换时,排除特定的页面元素不参与动画,以及对两个页面的共享元素设置动画。

Android5.0提供了三种过渡类型:

  • Enter Transition:进入过渡,决定Activity&Fragment中的视图如何进入屏幕;
  • Exit Transition:退出过渡,决定Activity&Fragment中的视图如何退出屏幕;
  • Shared Elements Transition:共享元素过渡,决定两个Activity&Fragment之间的共享视图元素如何转换;

进入和退出效果包括:

  • explode:分解,视图从屏幕中间进入或退出;
  • slide:滑动,视图从屏幕边缘进入或退出;
  • fade:淡出,通过改变视图的透明度,控制视图显示或消失;

共享元素效果包括:

  • changeBounds:改变目标视图的布局边界;
  • changeClipBounds:裁剪目标视图边界;
  • changeTransform:改变目标视图的缩放比例和旋转角度;
  • changeImageTransform:改变目标图片的大小和缩放比例;

Enter/Exit Transition 确定了非共享元素如何 进入/退出 页面视图;
Shared Elements Transition 确定了两个页面共享元素的动画效果。

Activity进入和退出动画

通过XML文件设置

  • 定义Transition
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<!-- res/transition/transition_explode.xml -->
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<explode xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:interpolator="@android:interpolator/bounce"/>

<!-- 顶部的状态栏以及底部的导航栏不执行动画 -->
<targets>
<target android:excludeId="@android:id/statusBarBackground"/>
<target android:excludeId="@android:id/navigationBarBackground"/>
</targets>
</transitionSet>
1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<!-- res/transition/transition_fade.xml -->
<fade xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:interpolator="@android:interpolator/bounce"/>
  • 设置Transition
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<style name="BaseAppTheme" parent="android:Theme.Material">
<!-- enable activity transitions -->
<item name="android:windowActivityTransitions">true</item>

<!-- transitions -->
<!-- 启动新Activity ,当前页面退出的动画 -->
<item name="android:windowExitTransition">@transition/transition_explode</item>
<!-- Activity首次进入显示的动画 -->
<item name="android:windowEnterTransition">@transition/transition_fade</item>
<!-- 调用finishAfterTransition()时,当前页面返回退出的动画 -->
<!-- 如果没有定Return动画,Return时会使用“反Enter”动画 -->
<item name="android:windowReturnTransition">@transition/transition_explode</item>
<!-- 从其它页面返回,重新进入的动画,即第二次进入,可以和首次进入不一样 -->
<!-- 如果没有定Reenter动画,Reenter时会使用“反Exit”动画 -->
<item name="android:windowReenterTransition">@transition/transition_fade</item>

<!-- 是否覆盖执行,可以理解成前后两个页面的Transition是同步执行还是顺序执行 -->
<item name="android:windowAllowEnterTransitionOverlap">false</item>
<item name="android:windowAllowReturnTransitionOverlap">false</item>
</style>

通过Java代码设置

  • 定义Transition
1
2
3
4
5
6
7
8
9
10
Explode explode = new Explode();
explode.setDuration(2000);
explode.setInterpolator(new BounceInterpolator());
// 顶部的状态栏以及底部的导航栏不执行动画
explode.excludeTarget(android.R.id.navigationBarBackground, true);
explode.excludeTarget(android.R.id.statusBarBackground, true);

Fade fade = new Fade();
fade.setDuration(2000);
fade.setInterpolator(new BounceInterpolator());
  • 设置Transition

在第一个页面中:

1
2
3
4
5
6
7
8
9
10
// inside your activity (if you did not enable transitions in your theme)
getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);

// set transition
getWindow().setExitTransition(explode);
getWindow().setReenterTransition(fade);

// set overlap
getWindow().setAllowEnterTransitionOverlap(false);
getWindow().setAllowReturnTransitionOverlap(false);

在第二个页面中:

1
2
3
4
5
6
7
8
9
10
// inside your activity (if you did not enable transitions in your theme)
getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);

// set transition
getWindow().setEnterTransition(fade);
getWindow().setReturnTransition(explode);

// set overlap
getWindow().setAllowEnterTransitionOverlap(false);
getWindow().setAllowReturnTransitionOverlap(false);

这样可以达到上面XML文件同样的效果,可以看出,使用代码方式相对比较繁琐,不过,代码方式可以为Transition提供更多属性,比如为Transition添加Listener等。

为Activity添加动画

1
2
3
// 第一个页面启动第二个页面
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this);
startActivity(intent, options.toBundle());
1
2
3
// 第二个页面从第一个页面返回
// 按系统Back键时,会默认调用这个方法
finishAfterTransition();

Activity共享元素动画

共享元素的过渡动画用于给两个Activity中共享的View添加过渡效果,其使用和进入和退出过的渡动画类似,这里只介绍XML方式添加共享元素过渡动画。

  • 定义Transition
1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<!-- res/transition/transition_change_bounds.xml -->
<changeBounds xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:interpolator="@android:interpolator/bounce"/>
  • 设置Transition
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<style name="BaseAppTheme" parent="android:Theme.Material">
<!-- Enable Activity transitions in your theme -->
<item name="android:windowActivityTransitions">true</item>

<!-- specify shared element transitions -->
<item name="android:windowSharedElementEnterTransition">
@transition/transition_change_bounds
</item>
<item name="android:windowSharedElementExitTransition">
@transition/transition_change_bounds
</item>

<!-- 共享元素Enter和Exit的Transition动画是否叠加执行 -->
<item name="android:windowSharedElementsUseOverlay">true</item>
</style>
  • 为两个Activity的layout文件中的共享View添加同样android:transitionName属性

  • 添加过渡动画

1
2
3
4
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,
Pair.create(view1, "shared_elements1"),
Pair.create(view2, "shared_elements2"));
startActivity(intent, options.toBundle());

Fragment过渡动画

Fragment的过渡动画使用与Activity基本相同,但是会有一些小区别:

  • 进入和退出效果过渡动画应该在Fragment的.java文件中调用对应的方法或者在xml属性声明里设置。

  • 共享元素的过渡动画应该在Fragment的.java文件中调用对应的方法或者在xml属性声明里设置。

  • Activity的Transition是通过调用startActivity()和finishAfterTransition()直接启动的,Fragment的Transition是在Fragment被add, remove, attach, detach, show或hidden后由FragmentTransaction自动启动的。

  • 共享元素应该在transaction(事务)提交前调用addSharedElement(View, String)方法,声明为FragmentTransaction的一部分。

参考资料

Getting Started with Activity & Fragment Transitions

坚持原创技术分享,您的支持将鼓励我继续创作!