如需自定义共享元素转换动画的运行方式,您可以使用一些参数来更改共享元素的转换方式。
动画规范
如需更改用于大小和位置移动的动画规范,您可以在 Modifier.sharedElement() 上指定不同的 boundsTransform 参数。
这会提供初始 Rect 位置和目标 Rect 位置。
例如,如需使上一个示例中的文本以弧形运动移动,请指定 boundsTransform 参数以使用 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 ) )
您可以使用任何 AnimationSpec。此示例使用 keyframes 规范。
boundsTransform 参数的示例调整大小模式
在两个共享边界之间添加动画效果时,您可以将 resizeMode 参数设置为 RemeasureToBounds 或 ScaleToBounds。此参数决定了共享元素在两种状态之间的转换方式。ScaleToBounds 首先使用前瞻(或目标)约束条件测量子布局。然后,子项的稳定布局会缩放以适应共享边界。
您可以将 ScaleToBounds 视为状态之间的“图形缩放”。
相比之下,RemeasureToBounds 会根据目标大小,使用动画固定约束条件重新测量和重新布局 sharedBounds 的子布局。重新测量由边界大小变化触发,这可能会发生在每一帧。
对于 Text 可组合项,建议使用 ScaleToBounds,因为它避免了重新布局和将文本重新排版到不同的行。对于宽高比不同的边界,以及如果您希望在两个共享元素之间实现流畅的连续性,建议使用 RemeasureToBounds。
您可以在以下示例中看到两种调整大小模式之间的区别:
|
|
|---|---|
动态启用和停用共享元素
默认情况下,sharedElement() 和 sharedBounds() 配置为在目标状态中找到匹配的键时,为布局更改添加动画效果。但是,您可能希望根据特定条件(例如导航方向或当前界面状态)动态停用此动画。
如需控制是否发生共享元素转换,您可以自定义传递给 rememberSharedContentState() 的 SharedContentConfig。isEnabled 属性决定了共享元素是否处于活动状态。
以下示例演示了如何定义一个配置,该配置仅在特定屏幕之间导航时(例如,仅从 A 到 B)启用共享转换,而在其他情况下停用该转换。
SharedTransitionLayout { val transition = updateTransition(currentState) transition.AnimatedContent { targetState -> // Create the configuration that depends on state changing. fun animationConfig() : SharedTransitionScope.SharedContentConfig { return object : SharedTransitionScope.SharedContentConfig { override val SharedTransitionScope.SharedContentState.isEnabled: Boolean // For this example, we only enable the transition in one direction // from A -> B and not the other way around. get() = transition.currentState == "A" && transition.targetState == "B" } } when (targetState) { "A" -> Box( modifier = Modifier .sharedElement( rememberSharedContentState( key = "shared_box", config = animationConfig() ), animatedVisibilityScope = this ) // ... ) { // Your content } "B" -> { Box( modifier = Modifier .sharedElement( rememberSharedContentState( key = "shared_box", config = animationConfig() ), animatedVisibilityScope = this ) // ... ) { // Your content } } } } }
默认情况下,如果在正在进行的动画期间停用共享元素,它仍会完成当前正在进行的动画,以防止意外移除正在进行的动画。如果您需要在动画进行期间移除元素,可以在 SharedContentConfig 接口中替换 shouldKeepEnabledForOngoingAnimation 以返回 false。
跳到最终布局
默认情况下,在两个布局之间转换时,布局大小会在其开始状态和最终状态之间添加动画效果。在为文本等内容添加动画效果时,这可能是不需要的行为。
以下示例说明了说明文本“Lorem Ipsum”以两种不同的方式进入屏幕。在第一个示例中,文本在进入时会随着容器大小的增大而自动重排。在第二个示例中,文本不会随着容器大小的增大而自动重排。添加 Modifier.skipToLookaheadSize() 可防止文本随着容器大小的增大而重新排版。
没有 |
|
|---|---|
剪辑和叠加层
为了使共享元素能够在不同的可组合项之间共享,当转换开始时,可组合项的渲染会提升 到图层叠加层,以匹配目标中的可组合项。这样做的效果是,它会脱离父级的边界及其图层转换(例如,Alpha 和缩放)。
它将渲染在其他非共享界面元素的顶部。转换完成后,该元素将从叠加层中移除到其自己的 DrawScope。
如需将共享元素剪辑为形状,请使用标准 Modifier.clip() 函数。将其放在 sharedElement() 之后:
Image( painter = painterResource(id = R.drawable.cupcake), contentDescription = "Cupcake", modifier = Modifier .size(100.dp) .sharedElement( rememberSharedContentState(key = "image"), animatedVisibilityScope = this@AnimatedContent ) .clip(RoundedCornerShape(16.dp)), contentScale = ContentScale.Crop )
如果您需要确保共享元素永远不会在父级容器之外渲染,可以在 sharedElement() 上设置 clipInOverlayDuringTransition。默认情况下,对于嵌套的共享边界,clipInOverlayDuringTransition 使用父级 sharedBounds() 中的剪辑路径。
如需支持在共享元素转换期间始终将特定界面元素(例如底部栏或悬浮操作
按钮)保持在顶部,请使用
Modifier.renderInSharedTransitionScopeOverlay()。默认情况下,此修饰符会在共享转换处于活动状态期间将内容保留在叠加层中。
例如,在 Jetsnack 中,BottomAppBar 需要放置在共享元素的顶部,直到屏幕不可见为止。将修饰符添加到可组合项会使其保持提升状态。
没有 |
有 |
|---|---|
您可能希望非共享可组合项在转换之前也添加动画效果并保持在其他可组合项的顶部。在这种情况下,请使用 renderInSharedTransitionScopeOverlay().animateEnterExit() 在共享元素转换运行时为可组合项添加动画效果:
JetsnackBottomBar( modifier = Modifier .renderInSharedTransitionScopeOverlay( zIndexInOverlay = 1f, ) .animateEnterExit( enter = fadeIn() + slideInVertically { it }, exit = fadeOut() + slideOutVertically { it } ) )
在极少数情况下,如果您希望共享元素不在叠加层中渲染,可以将 sharedElement() 上的 renderInOverlayDuringTransition 设置为 false。
将共享元素大小的更改通知同级布局
默认情况下,sharedBounds() 和 sharedElement() 不会在布局转换时将任何大小更改通知父级容器。
如需在转换时将大小更改传播到父级容器,请将 placeholderSize 参数更改为 PlaceholderSize.AnimatedSize。这样做会导致项增大或缩小。布局中的所有其他项都会响应此更改。
|
(请注意列表中的其他项如何响应一项的增大而向下移动) |
|---|---|