Android jetpack compose 中共享元素如何工作?

问题描述 投票:0回答:1

我正在尝试在 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 时。

kotlin android-jetpack-compose shared-element-transition
1个回答
© www.soinside.com 2019 - 2024. All rights reserved.