You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose中Composable的进出动画实现咨询

Jetpack Compose 屏幕进出动画实现方案

一、初始入场动画实现

Activity启动时的元素入场动画,无需依赖AnimatedContent(它更适配内容切换场景),用animateOffsetAsState或AnimatedVisibility即可快速实现:

1. 居中图片从上滑入

通过状态控制偏移量,让图片从上方初始位置滑至屏幕中心:

val isEntered by remember { mutableStateOf(false) }
val imageOffsetY by animateOffsetAsState(
    targetValue = if (isEntered) Offset(0f, 0f) else Offset(0f, -200.dp.toPx()),
    animationSpec = tween(500, easing = FastOutSlowInEasing)
)

Image(
    painter = painterResource(id = R.drawable.center_img),
    contentDescription = "Center Image",
    modifier = Modifier
        .offset { IntOffset(0, imageOffsetY.y.roundToInt()) }
        .align(Alignment.Center)
)

// 在LaunchedEffect中触发入场
LaunchedEffect(Unit) {
    delay(100) // 可选延迟优化启动体验
    isEntered = true
}

2. 底部按钮从下滑入

同理,控制按钮的垂直偏移量实现入场:

val buttonOffsetY by animateOffsetAsState(
    targetValue = if (isEntered) Offset(0f, 0f) else Offset(0f, 100.dp.toPx()),
    animationSpec = tween(500, easing = FastOutSlowInEasing)
)

Row(
    modifier = Modifier
        .fillMaxWidth()
        .offset { IntOffset(0, buttonOffsetY.y.roundToInt()) }
        .padding(bottom = 32.dp),
    horizontalArrangement = Arrangement.SpaceEvenly
) {
    Button(onClick = { /* 取消逻辑 */ }) { Text("Cancel") }
    Button(onClick = { /* 切换页面逻辑 */ }) { Text("Continue") }
}

二、页面切换动画(点击Continue)

这里AnimatedContent完全适用,它专门处理内容切换过渡,且支持自定义动画逻辑,方便快速切换效果:

1. 基础切换实现

用状态变量控制当前显示页面,AnimatedContent自动处理过渡:

enum class Screen { First, Second }
var currentScreen by remember { mutableStateOf(Screen.First) }

AnimatedContent(
    targetState = currentScreen,
    transitionSpec = {
        // 旧内容上滑退场,新内容下滑入场
        slideOutVertically(targetOffsetY = { -it }) + fadeOut() with
        slideInVertically(initialOffsetY = { it }) + fadeIn()
    }
) { screen ->
    when (screen) {
        Screen.First -> FirstScreen(onContinueClick = { currentScreen = Screen.Second })
        Screen.Second -> SecondScreen(onBackClick = { currentScreen = Screen.First })
    }
}

2. 便捷切换动画效果

将过渡动画封装为可复用函数,需要切换时直接替换:

// 垂直滑动过渡
fun verticalSlideTransition(): ContentTransform {
    return slideOutVertically(targetOffsetY = { -it }) + fadeOut() with
            slideInVertically(initialOffsetY = { it }) + fadeIn()
}

// 水平滑动过渡
fun horizontalSlideTransition(): ContentTransform {
    return slideOutHorizontally(targetOffsetX = { -it }) + fadeOut() with
            slideInHorizontally(initialOffsetX = { it }) + fadeIn()
}

// 使用时替换transitionSpec即可
AnimatedContent(
    targetState = currentScreen,
    transitionSpec = { horizontalSlideTransition() }
) { /* 页面内容 */ }

三、关于AnimatedContentScope的说明

AnimatedContentScope是AnimatedContent的内部作用域,提供animate*系列函数(如animateFloat、animateOffset)和initialSize、targetSize等属性,用于精细化控制过渡。比如结合大小变化的动画:

AnimatedContent(targetState = currentScreen) { screen ->
    val scale by animateFloatAsState(
        targetValue = if (targetState == screen) 1f else 0.8f,
        animationSpec = tween(300)
    )
    when (screen) {
        Screen.First -> FirstScreen(
            modifier = Modifier.scale(scale),
            onContinueClick = { currentScreen = Screen.Second }
        )
        // ...
    }
}

附:《自定义Jetpack Compose中的AnimatedContent》译文

自定义Jetpack Compose中的AnimatedContent

Jetpack Compose的AnimatedContent是处理内容切换动画的核心工具,但默认过渡效果未必能满足所有场景需求。本文将介绍如何自定义AnimatedContent的过渡逻辑,实现更个性化的视觉效果。

1. 理解ContentTransform

ContentTransform定义了内容退场与入场的动画组合,由initialContentExit(旧内容退场动画)和targetContentEnter(新内容入场动画)两部分组成。你可以用+组合多种动画,比如滑动+淡入淡出:

val transition = slideInHorizontally(initialOffsetX = { it }) + fadeIn()

2. 利用AnimatedContentScope的属性

在AnimatedContent的内容Lambda中,可访问AnimatedContentScope的属性(如initialSize旧内容尺寸、targetSize新内容尺寸),实现基于尺寸变化的动画:

AnimatedContent(targetState = isExpanded) { expanded ->
    val height by animateDpAsState(
        targetValue = if (expanded) 200.dp else 50.dp,
        animationSpec = spring()
    )
    Box(modifier = Modifier.height(height).fillMaxWidth().background(Color.Gray))
}

3. 封装自定义过渡动画

将复杂动画逻辑封装为可复用函数,方便在多处调用:

fun expandCollapseTransition(): ContentTransform {
    return fadeOut(tween(150)) with
            fadeIn(tween(150)) using
            SizeTransform(clip = false) { initialSize, targetSize ->
                if (targetSize.height > initialSize.height) {
                    keyframes {
                        durationMillis = 300
                        initialSize at 0 with LinearOutSlowInEasing
                        targetSize at 300 with FastOutLinearInEasing
                    }
                } else {
                    keyframes {
                        durationMillis = 300
                        initialSize at 0 with FastOutLinearInEasing
                        targetSize at 300 with LinearOutSlowInEasing
                    }
                }
            }
}

4. 处理复杂内容切换

当切换内容结构差异较大时,可通过contentKey指定唯一标识确保动画触发正确,同时结合rememberUpdatedState避免不必要的重组:

AnimatedContent(
    targetState = selectedItem,
    contentKey = { it.id },
    transitionSpec = { expandCollapseTransition() }
) { item ->
    val currentItem by rememberUpdatedState(item)
    ItemDetail(item = currentItem)
}

通过这些技巧,你可以完全掌控AnimatedContent的动画效果,为应用添加更流畅、更具特色的过渡体验。


内容的提问来源于stack exchange,提问作者StuartDTO

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 08:47:47