官方介绍页一、概念共享元素过渡是一种在内容之间具有一致的可组合项之间的无缝过渡方式。它们通常用于导航当用户在这些屏幕之间导航时可以直观地将不同的屏幕连接起来。SharedTransitionLayoutfun SharedTransitionLayout(modifier: Modifier Modifier,content: Composable SharedTransitionScope.() - Unit)作为顶层容器本质上包装了 LookaheadScope负责协调其内部所有共享元素的坐标空间映射和动画执行。提供了 SharedTransitionScope可组合项需要位于该作用域中才能使用以下两个共享元素修饰符子布局如果是通过调用而不是直接写在作用域内需要将作用域传递给子布局或将子布局定义成SharedTransitionScope的扩展函数。Modifier.sharedElement()fun Modifier.sharedElement(sharedContentState: SharedContentState, //元素的识别标记animatedVisibilityScope: AnimatedVisibilityScope, //共享元素动画必须伴随组件的出现或消失一般是AnimatedContent() 、AnimatedVisibility() 或 NavigationboundsTransform: BoundsTransform DefaultBoundsTransform, //用于更改大小和位置移动的动画规范 AnimationSpecplaceHolderSize: PlaceHolderSize contentSize,renderInOverlayDuringTransition: Boolean true, //见下方1.2说明zIndexInOverlay: Float 0f,clipInOverlayDuringTransition: OverlayClip ParentClip): Modifier适用于内容相同的元素之间过渡元素在下个页面只是尺寸或位置发生了变化。只有该元素在页面跳转时可见过渡其它的元素效果跟随AnimatedContent() 或 AnimatedVisibility()默认效果淡入淡出。Modifier.sharedBounds()fun Modifier.sharedBounds(sharedContentState: SharedContentState,animatedVisibilityScope: AnimatedVisibilityScope,enter: EnterTransition fadeIn(), //入场动画exit: ExitTransition fadeOut(), //出场动画boundsTransform: BoundsTransform DefaultBoundsTransform, //用于更改大小和位置移动的动画规范 AnimationSpecresizeMode: ResizeMode ScaleToBounds(ContentScale.FillWidth, Center), //调整大小模式确定共享元素如何在两种状态之间转换placeHolderSize: PlaceHolderSize contentSize,renderInOverlayDuringTransition: Boolean true,zIndexInOverlay: Float 0f,clipInOverlayDuringTransition: OverlayClip ParentClip): Modifier适用于代表同一区域的容器之间过渡容器在下个页面内容不一定相同但代表了相关内容容器的边界在页面跳转时会有大小变化的过渡其它的元素效果跟随 AnimatedContent() 或 AnimatedVisibility()默认效果淡入淡出。SharedContentStateComposablepublic fun rememberSharedContentState(key: Any): SharedContentState用于标记和匹配共享的元素。注意共享元素修饰符的顺序 如果匹配的两个元素设置尺寸的修饰符顺序不同如padding会造成大小不一失去了“同一元素”的初衷。1.1 Text() 的特别说明sharedElement() 在过渡期间只渲染目标内容sharedBounds() 在过渡期间会同时渲染初始内容和目标内容从函数中有 enter 和 exit 参数也可以看出。因此使用 sharedElement() 的话在过渡期间 Text 的内容是瞬间改变的而尺寸是逐渐变化的看起来不连贯还可能导致中途显示不全字号瞬间变大而尺寸慢慢变大的场景另外文字存在样式不同的情况粗细、颜色、斜体这不属于“视觉上相同”的概念需要使用 sharedBounds() 。1.2 参数 renderInOverlayDuringTransition 的说明在执行共享元素动画时一个常见的渲染问题是动画元素可能会被其他同层级或更高层级的组件遮挡。SharedTransitionLayout 通过Overlay覆盖层机制解决了这个问题。当动画开始时参与 sharedElement 的组件实际上会被从原来的层级树位置“提取”出来绘制在 SharedTransitionLayout 创建的顶层 Overlay 中。可通过 renderInOverlayDuringTransition 参数控制这一行为。对于需要保留在原层级中例如被圆角容器裁剪的特殊场景可以将其设置为 false。二、基本使用结合 AnimatedContent() 或 AnimatedVisibility() 使用将内容变化的动画交由系统处理。implementation androidx.compose.animation:animation:1.7.0-beta012.1 共享元素 sharedElement共享了 PageOne 和 PageTwo 中的图片从 PageOne 切换至 PageTwo 能够以这张图片作为过渡元素展示动画。Composable fun Screen() { var isShowDetail by remember { mutableStateOf(true) } //使用共享元素布局 SharedTransitionLayout { //将界面切换动画交给系统处理 AnimatedContent(targetState isShowDetail) { targetState - if (targetState) { PageOne( onClick { isShowDetail false }, //共享作用域传递给子布局为了使用sharedElement sharedTransitionScope thisSharedTransitionLayout, //动画作用于传递给子布局用于协调动画 animatedVisibilityScope thisAnimatedContent ) } else { PageTwo( onClick { isShowDetail true }, sharedTransitionScope thisSharedTransitionLayout, animatedVisibilityScope thisAnimatedContent ) } } } }两个子页面采用了不同的写法建议用 PageTwo 的扩展函数写法毕竟非屏幕级的可组合项都会用 private 修饰使得该扩展函数只在该 kt 文件下可访问不会造成代码污染。//页面一需要传入SharedTransitionScope Composable private fun PageOne( onClick: () - Unit, sharedTransitionScope: SharedTransitionScope, animatedVisibilityScope: AnimatedVisibilityScope ) { Box( modifier Modifier.fillMaxSize().clickable { onClick() }, contentAlignment Alignment.TopStart //图在左上角 ) { with(sharedTransitionScope) { Image( painter painterResource(R.drawable.ic_launcher_foreground), contentDescription null, modifier Modifier.sharedElement( //sharedElement标记用于共享的元素 sharedContentState rememberSharedContentState(key image), //key用于区分和匹配元素 animatedVisibilityScope animatedVisibilityScope ) ) } } } //页面二声明成SharedTransitionScope的扩展函数 Composable private fun SharedTransitionScope.PageTwo( onClick: () - Unit, animatedVisibilityScope: AnimatedVisibilityScope ) { Box( modifier Modifier.fillMaxSize().clickable { onClick() }, contentAlignment Alignment.BottomEnd //图在右下角 ) { Image( painter painterResource(R.drawable.ic_launcher_foreground), contentDescription null, modifier Modifier.sharedElement( //sharedElement标记用于共享的元素 sharedContentState rememberSharedContentState(key image), //key用于区分和匹配元素 animatedVisibilityScope animatedVisibilityScope ) ) } }2.2 共享边界 sharedBounds三、自定义3.1 “调整大小”模式 resizeModeScaleToBounds默认RemeasureToBounds首先会使用先行或目标约束条件测量子布局。然后系统会缩放子项的稳定布局以适应共享边界。可以视为状态之间的“图形缩放”。会根据目标尺寸通过固定的动画约束条件重新测量并重新布局 sharedBounds 的子布局。边界大小变化可能是每一帧的变化都会触发重新测量。对于 Text 可组合项建议使用 ScaleToBounds因为它可以避免对文本进行重新布局和重排到不同的行。对于宽高比不同的边界以及如果您希望两个共享元素之间具有流畅的连续性建议使用 RemeasureToBounds。3.2 使用动画规范 boundsTransform使用 AnimationSpec 来更改大小和位置移动如文本随弧形运动而移动使用 keyframes。val textBoundsTransform BoundsTransform { initialBounds, targetBounds - keyframes { durationMillis boundsAnimationDurationMillis initialBounds at 0 using ArcMode.ArcBelow using FastOutSlowInEasing targetBounds at boundsAnimationDurationMillis } } Text( Cupcake, fontSize 28.sp, modifier Modifier.sharedBounds( rememberSharedContentState(key title), animatedVisibilityScope animatedVisibilityScope, boundsTransform textBoundsTransform ) )3.3 跳至最终布局避免文字重排版Modifier.skipToLookahead()默认情况下在两个布局之间转换时布局尺寸会在其起始状态和最终状态之间以动画形式显示。在为文本等内容添加动画效果时这可能是不希望出现的行为。使用未使用四、测试Compose v1.11.0 引入了可视化调试工具使开发者能够直观观察动画执行的内部机制。使用 LookaheadAnimationVisualDebugging() 包裹 SharedTransitionLayout()。目标边界可视化实时显示动画目标边界。动画轨迹追踪展示元素运动路径。匹配项计数显示找到的匹配元素数量。Composablepublic fun LookaheadAnimationVisualDebugging(isEnabled: Boolean true,overlayColor: Color Color(0x8034A853),multipleMatchesColor: Color Color(0xFFEA4335),unmatchedElementColor: Color Color(0xFF9AA0A6),isShowKeyLabelEnabled: Boolean false,content: Composable () - Unit,)参数 overlayColor覆盖在抬升层下方所有内容即共享元素以及叠加层中渲染的其他元素所在区域的半透明薄膜的颜色。参数 multipleMatchesColor用于指示存在多个匹配项的共享元素 key 的颜色。参数 unmatchedElementColor用于指示没有匹配项的共享元素 key 的颜色。参数 isShowKeyLabelEnabled指定是否打印动画元素的 key。LookaheadAnimationVisualDebugging( overlayColor Color(0x4AE91E63), isEnabled true, multipleMatchesColor Color.Green, isShowKeylabelEnabled false, unmatchedElementColor Color.Red, ) { SharedTransitionLayout { CompositionLocalProvider( LocalSharedTransitionScope provides this, ) { // 应用内容 } } }
Compose 动画 - 共享元素过渡动画 SharedTransitionLayout
发布时间:2026/5/27 18:41:25
官方介绍页一、概念共享元素过渡是一种在内容之间具有一致的可组合项之间的无缝过渡方式。它们通常用于导航当用户在这些屏幕之间导航时可以直观地将不同的屏幕连接起来。SharedTransitionLayoutfun SharedTransitionLayout(modifier: Modifier Modifier,content: Composable SharedTransitionScope.() - Unit)作为顶层容器本质上包装了 LookaheadScope负责协调其内部所有共享元素的坐标空间映射和动画执行。提供了 SharedTransitionScope可组合项需要位于该作用域中才能使用以下两个共享元素修饰符子布局如果是通过调用而不是直接写在作用域内需要将作用域传递给子布局或将子布局定义成SharedTransitionScope的扩展函数。Modifier.sharedElement()fun Modifier.sharedElement(sharedContentState: SharedContentState, //元素的识别标记animatedVisibilityScope: AnimatedVisibilityScope, //共享元素动画必须伴随组件的出现或消失一般是AnimatedContent() 、AnimatedVisibility() 或 NavigationboundsTransform: BoundsTransform DefaultBoundsTransform, //用于更改大小和位置移动的动画规范 AnimationSpecplaceHolderSize: PlaceHolderSize contentSize,renderInOverlayDuringTransition: Boolean true, //见下方1.2说明zIndexInOverlay: Float 0f,clipInOverlayDuringTransition: OverlayClip ParentClip): Modifier适用于内容相同的元素之间过渡元素在下个页面只是尺寸或位置发生了变化。只有该元素在页面跳转时可见过渡其它的元素效果跟随AnimatedContent() 或 AnimatedVisibility()默认效果淡入淡出。Modifier.sharedBounds()fun Modifier.sharedBounds(sharedContentState: SharedContentState,animatedVisibilityScope: AnimatedVisibilityScope,enter: EnterTransition fadeIn(), //入场动画exit: ExitTransition fadeOut(), //出场动画boundsTransform: BoundsTransform DefaultBoundsTransform, //用于更改大小和位置移动的动画规范 AnimationSpecresizeMode: ResizeMode ScaleToBounds(ContentScale.FillWidth, Center), //调整大小模式确定共享元素如何在两种状态之间转换placeHolderSize: PlaceHolderSize contentSize,renderInOverlayDuringTransition: Boolean true,zIndexInOverlay: Float 0f,clipInOverlayDuringTransition: OverlayClip ParentClip): Modifier适用于代表同一区域的容器之间过渡容器在下个页面内容不一定相同但代表了相关内容容器的边界在页面跳转时会有大小变化的过渡其它的元素效果跟随 AnimatedContent() 或 AnimatedVisibility()默认效果淡入淡出。SharedContentStateComposablepublic fun rememberSharedContentState(key: Any): SharedContentState用于标记和匹配共享的元素。注意共享元素修饰符的顺序 如果匹配的两个元素设置尺寸的修饰符顺序不同如padding会造成大小不一失去了“同一元素”的初衷。1.1 Text() 的特别说明sharedElement() 在过渡期间只渲染目标内容sharedBounds() 在过渡期间会同时渲染初始内容和目标内容从函数中有 enter 和 exit 参数也可以看出。因此使用 sharedElement() 的话在过渡期间 Text 的内容是瞬间改变的而尺寸是逐渐变化的看起来不连贯还可能导致中途显示不全字号瞬间变大而尺寸慢慢变大的场景另外文字存在样式不同的情况粗细、颜色、斜体这不属于“视觉上相同”的概念需要使用 sharedBounds() 。1.2 参数 renderInOverlayDuringTransition 的说明在执行共享元素动画时一个常见的渲染问题是动画元素可能会被其他同层级或更高层级的组件遮挡。SharedTransitionLayout 通过Overlay覆盖层机制解决了这个问题。当动画开始时参与 sharedElement 的组件实际上会被从原来的层级树位置“提取”出来绘制在 SharedTransitionLayout 创建的顶层 Overlay 中。可通过 renderInOverlayDuringTransition 参数控制这一行为。对于需要保留在原层级中例如被圆角容器裁剪的特殊场景可以将其设置为 false。二、基本使用结合 AnimatedContent() 或 AnimatedVisibility() 使用将内容变化的动画交由系统处理。implementation androidx.compose.animation:animation:1.7.0-beta012.1 共享元素 sharedElement共享了 PageOne 和 PageTwo 中的图片从 PageOne 切换至 PageTwo 能够以这张图片作为过渡元素展示动画。Composable fun Screen() { var isShowDetail by remember { mutableStateOf(true) } //使用共享元素布局 SharedTransitionLayout { //将界面切换动画交给系统处理 AnimatedContent(targetState isShowDetail) { targetState - if (targetState) { PageOne( onClick { isShowDetail false }, //共享作用域传递给子布局为了使用sharedElement sharedTransitionScope thisSharedTransitionLayout, //动画作用于传递给子布局用于协调动画 animatedVisibilityScope thisAnimatedContent ) } else { PageTwo( onClick { isShowDetail true }, sharedTransitionScope thisSharedTransitionLayout, animatedVisibilityScope thisAnimatedContent ) } } } }两个子页面采用了不同的写法建议用 PageTwo 的扩展函数写法毕竟非屏幕级的可组合项都会用 private 修饰使得该扩展函数只在该 kt 文件下可访问不会造成代码污染。//页面一需要传入SharedTransitionScope Composable private fun PageOne( onClick: () - Unit, sharedTransitionScope: SharedTransitionScope, animatedVisibilityScope: AnimatedVisibilityScope ) { Box( modifier Modifier.fillMaxSize().clickable { onClick() }, contentAlignment Alignment.TopStart //图在左上角 ) { with(sharedTransitionScope) { Image( painter painterResource(R.drawable.ic_launcher_foreground), contentDescription null, modifier Modifier.sharedElement( //sharedElement标记用于共享的元素 sharedContentState rememberSharedContentState(key image), //key用于区分和匹配元素 animatedVisibilityScope animatedVisibilityScope ) ) } } } //页面二声明成SharedTransitionScope的扩展函数 Composable private fun SharedTransitionScope.PageTwo( onClick: () - Unit, animatedVisibilityScope: AnimatedVisibilityScope ) { Box( modifier Modifier.fillMaxSize().clickable { onClick() }, contentAlignment Alignment.BottomEnd //图在右下角 ) { Image( painter painterResource(R.drawable.ic_launcher_foreground), contentDescription null, modifier Modifier.sharedElement( //sharedElement标记用于共享的元素 sharedContentState rememberSharedContentState(key image), //key用于区分和匹配元素 animatedVisibilityScope animatedVisibilityScope ) ) } }2.2 共享边界 sharedBounds三、自定义3.1 “调整大小”模式 resizeModeScaleToBounds默认RemeasureToBounds首先会使用先行或目标约束条件测量子布局。然后系统会缩放子项的稳定布局以适应共享边界。可以视为状态之间的“图形缩放”。会根据目标尺寸通过固定的动画约束条件重新测量并重新布局 sharedBounds 的子布局。边界大小变化可能是每一帧的变化都会触发重新测量。对于 Text 可组合项建议使用 ScaleToBounds因为它可以避免对文本进行重新布局和重排到不同的行。对于宽高比不同的边界以及如果您希望两个共享元素之间具有流畅的连续性建议使用 RemeasureToBounds。3.2 使用动画规范 boundsTransform使用 AnimationSpec 来更改大小和位置移动如文本随弧形运动而移动使用 keyframes。val textBoundsTransform BoundsTransform { initialBounds, targetBounds - keyframes { durationMillis boundsAnimationDurationMillis initialBounds at 0 using ArcMode.ArcBelow using FastOutSlowInEasing targetBounds at boundsAnimationDurationMillis } } Text( Cupcake, fontSize 28.sp, modifier Modifier.sharedBounds( rememberSharedContentState(key title), animatedVisibilityScope animatedVisibilityScope, boundsTransform textBoundsTransform ) )3.3 跳至最终布局避免文字重排版Modifier.skipToLookahead()默认情况下在两个布局之间转换时布局尺寸会在其起始状态和最终状态之间以动画形式显示。在为文本等内容添加动画效果时这可能是不希望出现的行为。使用未使用四、测试Compose v1.11.0 引入了可视化调试工具使开发者能够直观观察动画执行的内部机制。使用 LookaheadAnimationVisualDebugging() 包裹 SharedTransitionLayout()。目标边界可视化实时显示动画目标边界。动画轨迹追踪展示元素运动路径。匹配项计数显示找到的匹配元素数量。Composablepublic fun LookaheadAnimationVisualDebugging(isEnabled: Boolean true,overlayColor: Color Color(0x8034A853),multipleMatchesColor: Color Color(0xFFEA4335),unmatchedElementColor: Color Color(0xFF9AA0A6),isShowKeyLabelEnabled: Boolean false,content: Composable () - Unit,)参数 overlayColor覆盖在抬升层下方所有内容即共享元素以及叠加层中渲染的其他元素所在区域的半透明薄膜的颜色。参数 multipleMatchesColor用于指示存在多个匹配项的共享元素 key 的颜色。参数 unmatchedElementColor用于指示没有匹配项的共享元素 key 的颜色。参数 isShowKeyLabelEnabled指定是否打印动画元素的 key。LookaheadAnimationVisualDebugging( overlayColor Color(0x4AE91E63), isEnabled true, multipleMatchesColor Color.Green, isShowKeylabelEnabled false, unmatchedElementColor Color.Red, ) { SharedTransitionLayout { CompositionLocalProvider( LocalSharedTransitionScope provides this, ) { // 应用内容 } } }