8、View动画之Activity转场动画:overridePendingTransition、ActivityOptionsCompat、共享元素转场
说实话,Activity之间的跳转动画,是很多Android开发者容易忽略的细节。大家往往把精力放在界面内容上,觉得转场嘛,能跳过去就行了。但我要说,一个流畅、优雅的转场动画,真的能让App的质感提升一个档次。
我记得刚入行那会儿,带我的老大哥跟我说过一句话:「用户第一眼看到的东西,决定了ta对App的印象。」而Activity转场,恰恰就是那个「第一眼」。今天我们就来聊聊View时代的三种转场方式,它们虽然老了,但思想至今仍在Compose中延续。
8.1 overridePendingTransition:最朴素的转场方式
这是Android 2.0就引入的老方法。你调用startActivity()或finish()之后,紧跟着调用它就行。它接受两个参数:进入动画和退出动画。
// 启动新Activity
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
// 返回时,在SecondActivity中调用
@Override
public void onBackPressed() {
super.onBackPressed();
overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right);
}
这里有个坑,我踩过好几次——overridePendingTransition必须在startActivity或finish之后立即调用,不能延迟。否则动画就不生效了。你想想看,系统在Activity切换时有一个短暂的窗口期,错过了就没了。
动画资源文件一般放在res/anim/目录下,比如这样:
<!-- slide_in_right.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="100%"
android:toXDelta="0%"
android:duration="300"/>
</set>
我个人习惯把常用的转场动画封装成工具类,比如ActivityAnimUtils.slideInFromRight(activity),这样团队里所有人都用同一套动画,风格统一。
android:interpolator使用。我常用@android:anim/decelerate_interpolator,让进入的Activity先快后慢,视觉上更舒服。
8.2 ActivityOptionsCompat:更灵活的转场控制
到了Android 5.0(API 21),Google推出了ActivityOptionsCompat。它比overridePendingTransition强大得多,能让你控制更多细节。
最基本的用法是这样:
Intent intent = new Intent(this, SecondActivity.class);
ActivityOptionsCompat options = ActivityOptionsCompat
.makeCustomAnimation(this, R.anim.slide_in_right, R.anim.slide_out_left);
startActivity(intent, options.toBundle());
看起来和之前差不多?别急,它真正的威力在后面。比如你可以指定启动时的坐标和大小:
// 从某个View的位置展开新Activity
ActivityOptionsCompat options = ActivityOptionsCompat
.makeScaleUpAnimation(view, 0, 0, view.getWidth(), view.getHeight());
startActivity(intent, options.toBundle());
我曾经在一个图片浏览器的项目里用过这个。点击缩略图时,新Activity从缩略图的位置放大展开,效果特别棒。用户会觉得图片「飞」出来了,而不是生硬地跳转。
makeScaleUpAnimation在Android 12以上已经被标记为deprecated了。Google推荐使用共享元素转场来替代。但如果你要兼容老版本,它仍然是个好选择。
8.3 共享元素转场:真正的「无缝」体验
共享元素转场,是我个人最喜欢的View动画特性。它让两个Activity中相同的UI元素「连接」起来,用户看到的是元素在平滑移动,而不是整个页面在切换。
实现它需要三步:
- 在布局中标记共享元素:给两个Activity中对应的View设置相同的
transitionName。 - 启动时传递选项:使用
ActivityOptionsCompat.makeSceneTransitionAnimation()。 - 在主题中启用:确保Activity的主题使用了
@style/Theme.AppCompat.Light.NoActionBar等支持转场的主题。
来看代码:
// 第一个Activity
ImageView imageView = findViewById(R.id.hero_image);
imageView.setTransitionName("hero_image");
Intent intent = new Intent(this, DetailActivity.class);
ActivityOptionsCompat options = ActivityOptionsCompat
.makeSceneTransitionAnimation(this, imageView, "hero_image");
startActivity(intent, options.toBundle());
// 第二个Activity的布局
<ImageView
android:id="@+id/detail_image"
android:transitionName="hero_image"
android:layout_width="match_parent"
android:layout_height="300dp"/>
嗯,这里要注意:transitionName必须完全一致,大小写都不能错。我曾经因为一个下划线的问题排查了半天,最后发现是hero_image和heroImage没对上。
如果你有多个共享元素,可以这样:
ActivityOptionsCompat options = ActivityOptionsCompat
.makeSceneTransitionAnimation(this,
Pair.create(view1, "transition_1"),
Pair.create(view2, "transition_2"));
imageView.setTransitionName("hero_" + position),确保每个item唯一。
8.4 三种方式的对比
我把它们整理成了一个表格,方便你对比选择:
| 特性 | overridePendingTransition | ActivityOptionsCompat | 共享元素转场 |
|---|---|---|---|
| 最低API | 2.0 (API 1) | 5.0 (API 21) | 5.0 (API 21) |
| 动画类型 | 整个窗口动画 | 窗口动画 + 缩放/位置 | 元素级平滑过渡 |
| 实现复杂度 | 低 | 中 | 高 |
| 视觉效果 | 一般 | 较好 | 优秀 |
| 适用场景 | 简单跳转 | 需要定位展开 | 图片/卡片详情页 |
8.5 知识体系总览
下面这张图,帮你理清这三种转场方式的关系和演进路径:
从这张图可以看得很清楚:转场动画的演进,本质上是从「整体控制」走向「元素级控制」。早期的overridePendingTransition只能对整个窗口做动画,到了ActivityOptionsCompat可以控制位置和缩放,而共享元素转场则精细到了单个UI元素。
这种思想后来被Compose继承并发扬光大——在Compose中,你甚至可以用AnimatedContent和SharedTransitionLayout实现更细腻的转场效果。不过那是后话了,先把View时代的这些基础打牢,你后面学Compose时会发现很多概念是相通的。
overridePendingTransition。如果已经全面拥抱Android 5.0+,优先考虑共享元素转场,尤其是图片、卡片、头像这类场景。用户真的能感知到这种细节上的用心。
公众号:蓝海资料掘金营,微信deep3321