要自定义共享元素过渡动画的运行方式,可以使用一些参数来更改共享元素的过渡方式。
动画规范
要更改用于大小和位置移动的动画规范,可以在 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
规范。
调整大小模式
在两个共享边界之间进行动画处理时,您可以将 resizeMode
参数设置为 RemeasureToBounds
或 ScaleToBounds
。此参数决定共享元素如何在两种状态之间进行过渡。ScaleToBounds
首先使用前瞻(或目标)约束来测量子布局。然后,子元素的稳定布局将缩放以适应共享边界。ScaleToBounds
可以被认为是状态之间的“图形缩放”。
而 RemeasureToBounds
使用基于目标大小的动画固定约束重新测量和重新布局 sharedBounds
的子布局。重新测量是由边界大小更改触发的,这可能每帧都会发生。
对于 Text
可组合项,建议使用 ScaleToBounds
,因为它可以避免文本重新布局和重新换行。对于纵横比不同的边界,如果您希望两个共享元素之间具有流畅的连续性,则建议使用 RemeasureToBounds
。
以下示例显示了两种调整大小模式之间的区别
|
|
---|---|
跳过最终布局
默认情况下,在两个布局之间进行过渡时,布局大小会在其起始状态和最终状态之间进行动画处理。当对文本等内容进行动画处理时,这可能是不可取的行为。
以下示例说明描述文本“Lorem Ipsum”以两种不同的方式进入屏幕。第一个示例中,文本在容器大小增长的同时重新换行;第二个示例中,文本在增长时不会重新换行。添加 Modifier.skipToLookaheadSize()
可防止其在增长时重新换行。
无 Modifier.skipToLookahead() - 请注意“Lorem Ipsum”文本的重新换行 |
Modifier.skipToLookahead() - 请注意“Lorem Ipsum”文本在动画开始时保持其最终状态 |
---|---|
剪辑和叠加层
在 Compose 中创建共享元素时,一个重要的概念是,为了使它们可以在不同的可组合项之间共享,可组合项的渲染会在过渡开始时提升到一个图层叠加层,以匹配目标中的内容。这样做的效果是,它将脱离父边界及其图层变换(例如 alpha 和缩放)。
它将在其他非共享 UI 元素的顶部进行渲染,过渡完成后,该元素将从叠加层移动到它自己的 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()
的剪辑路径。
要支持在共享元素过渡期间始终保持特定 UI 元素(例如底部应用栏或浮动操作按钮)位于顶部,请使用 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
。这样做会导致项目增大或缩小。布局中的所有其他项目都会响应此更改。
|
(请注意列表中的其他项目如何响应一个项目增长而向下移动) |
---|---|