Destination 这个库是维护屏幕的救星,因为它删除了很多样板,但现在我想使用它的共享元素转换,并且我正在努力构建它,因为我应该在参数中传递什么。
我们可以传递字符串、布尔值、序列化类,但是需要将什么传递到下一个屏幕以实现过渡动画
只要两个屏幕中的
Int
或String
中的Double
键匹配,您就可以通过Float
、rememberSharedContentState(key: Any)
、sharedElement
或sharedBounds
或任何课程。
但是使用唯一的密钥是谷歌建议的
在处理复杂的共享元素时,最好的做法是 创建一个不是字符串的键,因为字符串可能容易出错 匹配。每个键必须是唯一的才能发生匹配。例如, 在 Jetsnack 中,我们有以下共享元素:
data class SnackSharedElementKey(
val snackId: Long,
val origin: String,
val type: SnackSharedElementType
)
enum class SnackSharedElementType {
Bounds,
Image,
Title,
Tagline,
Background
}
@Composable
fun SharedElementUniqueKey() {
// ...
Box(
modifier = Modifier
.sharedElement(
rememberSharedContentState(
key = SnackSharedElementKey(
snackId = 1,
origin = "latest",
type = SnackSharedElementType.Image
)
),
animatedVisibilityScope = this@AnimatedVisibility
)
)
// ...
}
我用 String 键制作了一个示例,通过导航从 LazyColumn 转换到 HorizontalPager,但正如您所看到的,两个屏幕都不依赖于 NavController。
@Composable
private fun DetailsScreen(
snack: SnackItem,
sharedTransitionScope: SharedTransitionScope,
animatedContentScope: AnimatedContentScope,
onClick: (String) -> Unit,
) {
var selectedIndex by remember {
mutableStateOf(listSnacks.indexOf(snack).coerceAtLeast(0))
}
with(sharedTransitionScope) {
val pagerState = rememberPagerState(
initialPage = selectedIndex,
pageCount = {
listSnacks.size
}
)
HorizontalPager(
state = pagerState,
pageSpacing = 16.dp
) { page ->
selectedIndex = page
val currentSnack = listSnacks[page]
Column(
Modifier
.fillMaxSize()
.clickable(
interactionSource = remember {
MutableInteractionSource()
},
indication = null
) {
onClick("home")
}
) {
Image(
painterResource(id = currentSnack.image),
contentDescription = currentSnack.description,
contentScale = ContentScale.Crop,
modifier = Modifier
.sharedElement(
sharedTransitionScope.rememberSharedContentState(key = "image-$selectedIndex"),
animatedVisibilityScope = animatedContentScope
)
.aspectRatio(1f)
.fillMaxWidth()
)
Text(
currentSnack.name, fontSize = 38.sp,
modifier =
Modifier
.sharedBounds(
sharedTransitionScope.rememberSharedContentState(key = "text-$selectedIndex"),
animatedVisibilityScope = animatedContentScope,
)
)
}
}
}
}
@Composable
private fun HomeScreen(
scrollState: LazyListState,
sharedTransitionScope: SharedTransitionScope,
animatedContentScope: AnimatedContentScope,
onClick: (String) -> Unit,
) {
var isDetailSelected by remember {
mutableStateOf(false)
}
with(sharedTransitionScope) {
Column {
TopAppBar(
title = {
Text("Title")
},
backgroundColor = Color.Black,
contentColor = Color.White,
modifier = if (isDetailSelected) Modifier
else Modifier.renderInSharedTransitionScopeOverlay()
)
LazyColumn(
modifier = Modifier
.fillMaxSize(),
contentPadding = PaddingValues(8.dp),
state = scrollState,
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
itemsIndexed(listSnacks) { index, item ->
Row(
Modifier.clickable(
interactionSource = remember {
MutableInteractionSource()
},
indication = null
) {
isDetailSelected = true
onClick("details/$index")
}
) {
Spacer(modifier = Modifier.width(8.dp))
Image(
painterResource(id = item.image),
contentDescription = item.description,
contentScale = ContentScale.Crop,
modifier = Modifier
.sharedElement(
sharedTransitionScope.rememberSharedContentState(key = "image-$index"),
animatedVisibilityScope = animatedContentScope
)
.size(120.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text(
item.name, fontSize = 18.sp,
modifier = Modifier
.align(Alignment.CenterVertically)
.sharedBounds(
sharedTransitionScope.rememberSharedContentState(key = "text-$index"),
animatedVisibilityScope = animatedContentScope
)
)
}
}
}
}
}
}