下面我将全面详细地介绍在 Compose 中实现 Activity 跳转动画的各种方法,包括基础实现、高级技巧和最佳实践。
一、基础 Activity 过渡动画
1. overridePendingTransition 传统方式
这是最基础且兼容性最好的方法,适用于所有 Android 版本。
实现步骤:
- 创建动画资源文件 (res/anim/)
xml
<!-- slide_in_right.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="300"
android:fromXDelta="100%p"
android:toXDelta="0%p"
android:interpolator="@android:anim/decelerate_interpolator"/>
</set>
<!-- slide_out_left.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="300"
android:fromXDelta="0%p"
android:toXDelta="-100%p"
android:interpolator="@android:anim/decelerate_interpolator"/>
</set>
<!-- fade_in.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:duration="300"
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:interpolator="@android:anim/accelerate_interpolator"/>
</set>
- 在 Compose 中触发跳转
kotlin
@Composable
fun FirstScreen() {
val context = LocalContext.current
Button(
onClick = {
context.startActivity(Intent(context, SecondActivity::class.java))
(context as Activity).overridePendingTransition(
R.anim.slide_in_right,
R.anim.fade_out
)
}
) {
Text("跳转到第二个Activity")
}
}
- 在目标 Activity 中设置返回动画
kotlin
class SecondActivity : ComponentActivity() {
override fun finish() {
super.finish()
overridePendingTransition(
R.anim.fade_in,
R.anim.slide_out_left
)
}
}
2. 进阶动画组合
你可以组合多种动画效果:
xml
<!-- slide_and_fade_in.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="400"
android:fromXDelta="20%"
android:toXDelta="0%"
android:interpolator="@android:anim/decelerate_interpolator"/>
<alpha
android:duration="400"
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:interpolator="@android:anim/accelerate_interpolator"/>
<scale
android:duration="400"
android:fromXScale="0.9"
android:toXScale="1.0"
android:fromYScale="0.9"
android:toYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"/>
</set>
二、共享元素过渡 (Android 5.0+)
1. 基本实现
共享元素过渡可以创建更流畅的视觉体验。
步骤1:在布局中定义过渡名称
kotlin
@Composable
fun FirstScreen() {
val context = LocalContext.current
val imageView = LocalView.current
Column {
Image(
painter = rememberImagePainter(data = "https://example.com/image.jpg"),
contentDescription = "共享图片",
modifier = Modifier
.size(200.dp)
.clickable {
val intent = Intent(context, DetailActivity::class.java)
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
context as Activity,
imageView,
"shared_image" // 必须与DetailActivity中的名称一致
)
context.startActivity(intent, options.toBundle())
}
.composed {
// 设置过渡名称
ViewCompat.setTransitionName(imageView, "shared_image")
Modifier
}
)
}
}
步骤2:在目标Activity中设置相同的过渡名称
kotlin
class DetailActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val imageView = LocalView.current
Column {
Image(
painter = rememberImagePainter(data = "https://example.com/image.jpg"),
contentDescription = "详情图片",
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f)
.composed {
ViewCompat.setTransitionName(imageView, "shared_image")
Modifier
}
)
}
}
}
}
2. 多个共享元素
kotlin
// 启动Activity时
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
activity,
Pair(view1, "transition_title"),
Pair(view2, "transition_image"),
Pair(view3, "transition_bg")
)
startActivity(intent, options.toBundle())
三、高级过渡动画技巧
1. 使用 Material Motion 规范
Google 推荐的 Material Motion 过渡模式:
kotlin
// 在styles.xml中定义主题继承
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight">
<item name="android:windowActivityTransitions">true</item>
<item name="android:windowEnterTransition">@transition/explode</item>
<item name="android:windowExitTransition">@transition/explode</item>
<item name="android:windowSharedElementEnterTransition">@transition/change_image_transform</item>
<item name="android:windowSharedElementExitTransition">@transition/change_image_transform</item>
</style>
// 过渡资源文件
<!-- change_image_transform.xml -->
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<changeImageTransform/>
<changeBounds/>
<changeTransform/>
</transitionSet>
2. 延迟共享元素过渡
有时需要等待数据加载完成再开始过渡:
kotlin
class DetailActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 延迟过渡直到数据加载完成
postponeEnterTransition()
setContent {
val imageView = LocalView.current
LaunchedEffect(Unit) {
// 模拟数据加载
delay(1000)
// 开始过渡动画
startPostponedEnterTransition()
}
// 界面内容...
}
}
}
四、常见问题解决方案
1. 状态栏和导航栏过渡
kotlin
// 在主题中添加
<item name="android:windowSharedElementsUseOverlay">false</item>
<item name="android:windowAllowEnterTransitionOverlap">true</item>
<item name="android:windowAllowReturnTransitionOverlap">true</item>
2. 修复共享元素位置问题
kotlin
// 在Compose中使用Modifier.clipToBounds()
Image(
// ...
modifier = Modifier.clipToBounds()
)
3. 自定义共享元素过渡
java
// 自定义SharedElementCallback
setExitSharedElementCallback(object : SharedElementCallback() {
override fun onMapSharedElements(
names: MutableList<String>,
sharedElements: MutableMap<String, View>
) {
// 自定义映射逻辑
}
})
五、性能优化建议
- 简化动画:避免过于复杂的动画效果
- 使用硬件加速:确保在manifest中启用了硬件加速
- 优化图片:共享元素中的图片应适当压缩
- 测试低端设备:确保动画在中低端设备上也能流畅运行
- 提供回退方案:为API<21的设备提供简单动画或直接跳转
六、完整示例代码
MainActivity.kt
kotlin
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyAppTheme {
FirstScreen()
}
}
}
}
@Composable
fun FirstScreen() {
val context = LocalContext.current
val imageView = LocalView.current
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
// 简单跳转按钮
Button(
onClick = {
context.startActivity(Intent(context, SimpleTransitionActivity::class.java))
(context as Activity).overridePendingTransition(
R.anim.slide_up,
R.anim.stay
)
}
) {
Text("简单过渡效果")
}
Spacer(modifier = Modifier.height(16.dp))
// 共享元素跳转
Image(
painter = rememberImagePainter(data = R.drawable.sample_image),
contentDescription = "共享图片",
modifier = Modifier
.size(200.dp)
.clip(RoundedCornerShape(8.dp))
.clickable {
val intent = Intent(context, SharedElementActivity::class.java)
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
context as Activity,
imageView,
"shared_image"
)
context.startActivity(intent, options.toBundle())
}
.composed {
ViewCompat.setTransitionName(imageView, "shared_image")
Modifier
}
)
}
}
SharedElementActivity.kt
kotlin
class SharedElementActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
setContent {
MyAppTheme {
val imageView = LocalView.current
Column(
modifier = Modifier.fillMaxSize()
) {
Image(
painter = rememberImagePainter(data = R.drawable.sample_image),
contentDescription = "详情图片",
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f)
.padding(16.dp)
.clip(RoundedCornerShape(12.dp))
.composed {
ViewCompat.setTransitionName(imageView, "shared_image")
Modifier
}
)
Spacer(modifier = Modifier.height(16.dp))
Button(
onClick = { finishAfterTransition() }
) {
Text("返回")
}
}
}
}
}
}
通过以上方法,你可以在 Compose 项目中实现各种精美的 Activity 跳转动画效果。根据项目需求选择合适的方式,并注意测试不同设备和 Android 版本的兼容性。