我正在尝试在 jetpack compose 中进行共享元素转换以显示用户的图像和名称。我显示用户的图像和名称,但我的代码中没有动画,当用户的图片和名称与共享元素一起出现在屏幕上时,背景中的组件(即 ProfileScreen)不会消失。我不太了解共享元素转换,也没有太多资源,我在 github 上找到了一些存储库并尝试实现它。我应该做什么来在我的代码中添加动画。我的代码中可能有错误或缺失。
以前实现过或理解共享元素的人可以帮忙吗?
我试过这个:
https://github.com/mxalbert1996/compose-shared-elements
听到的是我的代码:
private var selectedUser: Int by mutableStateOf(-1)
@Composable
fun ProfileScreenRoute(
navHostController: NavHostController,
sharedViewModel: SharedViewModel,
viewModel: ProfileScreenViewModel = hiltViewModel()
) {
val state by viewModel.state.collectAsState()
ProfileScreen(
navHostController,
sharedViewModel,
state,
onChangeSelectedUser = viewModel::onChangeSelectedUser
)
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun ProfileScreen(
navHostController: NavHostController,
sharedViewModel: SharedViewModel,
state: ProfileScreenState,
onChangeSelectedUser: (User) -> Unit
) {
var isShowSharedElementRoute by remember { mutableStateOf(false) }
val modalBottomSheetState =
rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden)
val scope = rememberCoroutineScope()
if (isShowSharedElementRoute) {
SharedElementsRoot {
val user = selectedUser
val gridState = rememberLazyGridState()
DelayExit(visible = user >= 0) {
UserDetailsScreen(
user = User(
userImage = sharedViewModel.userProfileImg,
userName = state.userName
)
)
}
}
}
ModalBottomSheetLayout(
sheetContent = {
ImagePickerSheet(
onClickImagePickerItem = { imagePickerClickedIndex ->
isShowSharedElementRoute = !isShowSharedElementRoute
selectedUser = selectedUser + 1
scope.launch {
modalBottomSheetState.animateTo(ModalBottomSheetValue.Hidden)
}
})
},
sheetState = modalBottomSheetState,
sheetShape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
) {
Scaffold(
backgroundColor = Color.Transparent,
topBar = {
AccountTopBar(
onBackClick = {
navHostController.navigate(DASHBOARD_ROUTE)
},
onSettingsClick = {
navHostController.navigate(ACCOUNT_SETTINGS_SCREEN_ROUTE)
},
screenDescription = "Profil",
backImage = R.drawable.ic_back,
settingsImage = R.drawable.ic_account_settings
)
}, bottomBar = {
DYTAppVersion()
}) { padding ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(top = 20.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
ProfileImage(
userProfileImg = sharedViewModel.userProfileImg ?: Constant.DEFAULT_PROFILE_IMG,
userName = state.userName,
chooseImage = {
scope.launch {
modalBottomSheetState.animateTo(ModalBottomSheetValue.Expanded)
}
}
)
Spacer(modifier = Modifier.padding(bottom = 30.dp))
AccountScreenItem().accountScreenItemList.forEachIndexed { index, accountItem ->
AccountScreenItemComponent(
imgId = accountItem.imgId,
imgContentDescription = accountItem.imgContentDescription,
textContent = accountItem.textContent
) {
accountItem.route?.let { it ->
navHostController.navigate(it)
}
}
Divider(modifier = Modifier.padding(start = 15.dp, end = 15.dp))
}
}
}
}
}
@Composable
private fun UserDetailsScreen(user: User) {
Scaffold() { padding->
Column(
Modifier.fillMaxSize().padding(padding),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
SharedMaterialContainer(
key = "userAvatar",
screenKey = "DetailsScreen",
shape = MaterialTheme.shapes.medium,
color = Color.Transparent,
elevation = 10.dp,
transitionSpec = FadeOutTransitionSpec
) {
val scope = LocalSharedElementsRootScope.current!!
Image(
painter = rememberAsyncImagePainter(model = user.userImage),
contentDescription = "",
modifier = Modifier
.size(200.dp)
.clickable(enabled = !scope.isRunningTransition) { scope.changeUser(-1) },
contentScale = ContentScale.Crop
)
}
SharedElement(
key = "userName",
screenKey = "DetailsScreen",
transitionSpec = CrossFadeTransitionSpec
) {
Text(text = user.userName ?: "", style = MaterialTheme.typography.h1)
}
}
}
}
private fun SharedElementsRootScope.changeUser(user: Int) {
val currentUser = selectedUser
if (currentUser != user) {
val targetUser = if (user >= 0) user else currentUser
if (targetUser >= 0) {
}
selectedUser = user
}
}
private val FadeOutTransitionSpec = MaterialContainerTransformSpec(
durationMillis = 1000,
fadeMode = FadeMode.Out
)
private val CrossFadeTransitionSpec = SharedElementsTransitionSpec(
durationMillis = 1000,
fadeMode = FadeMode.Cross,
fadeProgressThresholds = ProgressThresholds(0.10f, 0.40f)
)
ImagePickerSheetItem
@Composable
fun ImagePickerSheetItem(
title: String,
onClickImagePickerListItem: (Int) -> Unit
) {
Column(modifier = Modifier.fillMaxWidth()) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 15.dp, bottom = 10.dp, top = 25.dp)
) {
Text(
text = title,
style = MaterialTheme.typography.subtitle1,
)
}
Divider()
Column(
modifier = Modifier
.fillMaxWidth()
.padding(15.dp),
verticalArrangement = Arrangement.spacedBy(40.dp)
) {
ProfileBottomSheetItemList().profileBottomSheetList.forEachIndexed { index, profileBottomSheetItem ->
Row(modifier = Modifier
.fillMaxWidth()
.clickable {
onClickImagePickerListItem(index)
}) {
Text(
text = profileBottomSheetItem.title,
fontSize = 15.sp
)
}
}
}
}
}
图像选择器表
@Composable
fun ImagePickerSheet(
onClickImagePickerItem:(Int)->Unit
) {
BaseBottomSheet(
content = {
ImagePickerSheetItem(
title = "Options",
onClickImagePickerListItem = { clickedIndex ->
onClickImagePickerItem(clickedIndex)
})
},
boxCornerRadius = 5.dp)
}
BaseBottomSheet
@Composable
fun BaseBottomSheet(
boxCornerRadius: Dp = 16.dp,
content: @Composable () -> Unit
) {
Box(
modifier = Modifier
.wrapContentHeight()
.fillMaxWidth()
.background(MaterialTheme.colors.bottomSheetBackgroundColor)
.clip(RoundedCornerShape(topEnd = boxCornerRadius, topStart = boxCornerRadius)),
) {
Divider(
color = Color.Gray,
thickness = 5.dp,
modifier = Modifier
.padding(top = 15.dp)
.align(Alignment.TopCenter)
.width(80.dp)
.clip(RoundedCornerShape(50.dp))
)
content()
}
}
个人资料图片
@Composable
fun ProfileImage(
userProfileImg: String,
userName: String?,
chooseImage: () -> Unit
) {
val painter = userProfileImg
val img = rememberAsyncImagePainter(painter)
Box {
Image(
painter = img,
contentScale = ContentScale.Crop,
contentDescription = "",
modifier = Modifier
.size(96.dp)
.clip(CircleShape)
)
Box(
modifier = Modifier
.align(Alignment.BottomEnd)
.offset(y = 10.dp, x = 15.dp)
) {
Image(
painter = painterResource(id = R.drawable.ic_camera),
contentDescription = "",
modifier = Modifier.clickable {
chooseImage()
})
}
}
Spacer(modifier = Modifier.padding(bottom = 20.dp))
Text(
text = "userName" ?: "",
style = MaterialTheme.typography.subtitle1
)
Text(text = "01.02.2022 ")
}
当用户选择BottomSheet的第一个项目时(我只是出于试用目的制作了用户选择的任何项目,以免暂时混淆工作。我将分享下面的代码,您可以看到。没有检查所选项目。) 用户的图片和名称将使用共享元素动画显示到 UserDetailsScreen。
ModalBottomSheetLayout(
sheetContent = {
ImagePickerSheet(
onClickImagePickerItem = { imagePickerClickedIndex ->
isShowSharedElementRoute = !isShowSharedElementRoute
selectedUser = selectedUser + 1
scope.launch {
modalBottomSheetState.animateTo(ModalBottomSheetValue.Hidden)
}
})
}
当我单击 BottomSheet 项目开始共享元素转换并导航 UserDetailsScreen 时。