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

如何使用Jetpack Compose实现搜索框场景过渡动画

在Jetpack Compose中实现搜索框跨页面缩小重叠过渡动画

我有两个Compose页面:

  • 第一个页面顶部是静态宽搜索框,点击后跳转至第二个实际搜索界面
  • 第二个页面的搜索框样式相近,但宽度更小

期望实现的效果:跳转时第一个页面的搜索框快速缩小,与第二个页面的搜索框平滑重叠。

在传统View体系中,我会用以下方式实现:

val bundle = ActivityOptionsCompat
                .makeSceneTransitionAnimation(requireActivity(), toolbar, "llSearch").toBundle()

现在我用navController.navigateTo(page2)进行页面跳转,请问怎么用Jetpack Compose实现这个搜索框的过渡动画?


实现方案

1. 配置NavHost启用共享元素过渡

首先要确保NavHost支持共享元素过渡,通过rememberSharedTransitionScope关联导航控制器,并配置过渡动画参数:

@Composable
fun MainNavHost(navController: NavHostController) {
    rememberSharedTransitionScope()
    NavHost(
        navController = navController,
        startDestination = "page1",
        // 基础页面过渡(可选,用于非共享元素部分)
        enterTransition = { fadeIn(tween(200)) },
        exitTransition = { fadeOut(tween(200)) },
        // 共享元素进入/退出动画配置
        sharedElementsEnterTransition = {
            scaleIn(
                initialScale = 1f,
                animationSpec = tween(durationMillis = 300)
            ) + fadeIn(tween(300))
        },
        sharedElementsExitTransition = {
            scaleOut(
                targetScale = 0.7f,
                animationSpec = tween(durationMillis = 300)
            ) + fadeOut(tween(300))
        }
    ) {
        composable("page1") { Page1(navController) }
        composable("page2") { Page2() }
    }
}

2. 为两个页面的搜索框标记共享元素

使用相同的共享元素Key,用SharedTransitionLayout包裹搜索框组件,让Compose跟踪元素的尺寸和位置变化:

页面1(宽搜索框)

@Composable
fun Page1(navController: NavHostController) {
    val searchBarSharedState = rememberSharedContentState(key = "search_bar")
    
    Column(modifier = Modifier.fillMaxSize()) {
        SharedTransitionLayout(sharedContentState = searchBarSharedState) {
            TextField(
                value = "",
                onValueChange = {},
                modifier = Modifier
                    .fillMaxWidth()
                    .height(56.dp)
                    .sharedContent(key = "search_bar"),
                placeholder = { Text("搜索") }
            )
        }
        
        Button(
            modifier = Modifier.padding(top = 20.dp),
            onClick = { navController.navigate("page2") }
        ) {
            Text("跳转到搜索页")
        }
    }
}

页面2(窄搜索框)

@Composable
fun Page2() {
    val searchBarSharedState = rememberSharedContentState(key = "search_bar")
    
    Column(modifier = Modifier.fillMaxSize()) {
        SharedTransitionLayout(sharedContentState = searchBarSharedState) {
            TextField(
                value = "",
                onValueChange = {},
                modifier = Modifier
                    .width(300.dp)
                    .height(56.dp)
                    .align(Alignment.CenterHorizontally)
                    .sharedContent(key = "search_bar"),
                placeholder = { Text("搜索") }
            )
        }
    }
}

3. 自定义动画细节(可选)

如果需要更贴合需求的"快速缩小"效果,可以调整动画的时长、曲线等参数:

// 自定义快速缩小的进入动画
val customEnterAnim = remember {
    sharedElementEnterTransition {
        scaleIn(
            initialScale = 1f,
            targetScale = 0.7f,
            // 缩短时长实现快速效果
            animationSpec = tween(durationMillis = 200, easing = FastOutLinearInEasing)
        ) + fadeIn(tween(200))
    }
}

// 对应退出动画
val customExitAnim = remember {
    sharedElementExitTransition {
        scaleOut(
            initialScale = 1f,
            targetScale = 0.7f,
            animationSpec = tween(durationMillis = 200, easing = FastOutLinearInEasing)
        ) + fadeOut(tween(200))
    }
}

将上述动画替换到NavHost的sharedElementsEnterTransition和sharedElementsExitTransition参数中即可。


核心注意点

  • 两个页面的共享元素必须使用完全相同的Key(示例中为"search_bar"),这是Compose识别共享元素的唯一标识
  • SharedTransitionLayout负责绑定共享元素的状态,必须包裹目标组件
  • 动画参数可以根据需求灵活调整,tween的durationMillis控制动画速度,easing控制动画曲线

内容的提问来源于stack exchange,提问作者有朋自远方来

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 03:43:25