如何将一个可组合项作为其参数传递给另一个可组合项,并在 Jetpack Compose 中显示/运行它

How to pass a Composable to another Composable as its parameter and display/run it in Jetpack Compose

提问人:Dolev Dublon 提问时间:12/3/2022 最后编辑:z.g.yDolev Dublon 更新时间:2/16/2023 访问量:5522

问:

我有这个抽屉,我在 youtube 上学会了制作

https://www.youtube.com/watch?v=JLICaBEiJS0&list=PLQkwcJG4YTCSpJ2NLhDTHhi6XBNfk9WiC&index=31菲利普·拉克纳

SwipeScreen enter image description here

我想将抽屉添加到我的应用程序中,该应用程序具有多个屏幕,其中一些不需要抽屉,因此我实现了带有屏幕的导航,并且某些屏幕还需要将抽屉包裹在它们顶部。

这是抽屉的代码


val scaffoldState = rememberScaffoldState()
                val scope = rememberCoroutineScope()
                Scaffold(
                    drawerGesturesEnabled = scaffoldState.drawerState.isOpen,
                    scaffoldState = scaffoldState, topBar = {
                    AppBar(onNavigationIconClick = {
                        scope.launch {
                            scaffoldState.drawerState.open()
                        }

                    })
                }, drawerContent = {
                    DrawerHeader()
                    DrawerBody(items = listOf(
                        MenuItem(
                            id = "home",
                            title = "Home",
                            contentDescription = "Go to home screen",
                            icon = Icons.Default.Home
                        ),
                        MenuItem(
                            id = "settings",
                            title = "Settings",
                            contentDescription = "Go to Settings screen",
                            icon = Icons.Default.Settings
                        ),
                        MenuItem(
                            id = "help",
                            title = "Help",
                            contentDescription = "Go to help screen",
                            icon = Icons.Default.Info
                        ),
                    ), onItemClick = {
                        println("Clicked on ${it.title}")

                        when (it.id) {
                            "home" -> {
                                println("Clicked on ${it.title}")
                            }
                            "settings" -> {
                                println("Clicked on ${it.title}")
                            }
                            "help" -> {
                                println("Clicked on ${it.title}")
                            }
                        }
                    })

                }) {

                    Text(text = "Hello World")

                }

文本 = Hellow 世界是我想传递屏幕参数的地方,我不知道该怎么做。我想添加一个参数,该参数采用可组合函数并在其中运行它

我按照这个关于如何在 kotlin 中导航的导航视频进行了操作

https://www.youtube.com/watch?v=4gUeyNkGE3g&list=PLQkwcJG4YTCSpJ2NLhDTHhi6XBNfk9WiC&index=18

它有 3 个大文件,所以如果你问我,我会把它们发布在这里,但我会尽量更具体地说明需要什么

composable(route = Screen.RegisterScreen.route) {
            RegisterScreen(navController = navCotroller)
        }

如果我把代码放在抽屉里,它工作得很好,但我想拆分代码以更干净,因为我在更多地方使用抽屉

代码的工作方式如下

composable(route = Screen.PreferenceScreen.route) {
            val scaffoldState = rememberScaffoldState()
            val scope = rememberCoroutineScope()
            Scaffold(

                drawerGesturesEnabled = scaffoldState.drawerState.isOpen,
                scaffoldState = scaffoldState,
                topBar = {
                    AppBar(onNavigationIconClick = {
                        scope.launch {
                            scaffoldState.drawerState.open()
                        }

                    })
                },
                drawerContent = {
                    DrawerHeader()
                    DrawerBody(items = listOf(
                        MenuItem(
                            id = "swipe",
                            title = "Swipe",
                            contentDescription = "Go to Swipe screen",
                            icon = Icons.Default.Home
                        ),
                        MenuItem(
                            id = "settings",
                            title = "Settings",
                            contentDescription = "Go to Settings screen",
                            icon = Icons.Default.Settings
                        ),
                        MenuItem(
                            id = "profile",
                            title = "Profile",
                            contentDescription = "Go to profile screen",
                            icon = Icons.Default.Info
                        ),
                    ), onItemClick = {
                        when (it.id) {
                            "swipe" -> {
                                navCotroller.navigate(Screen.SwipeScreen.route)
                            }
                            "settings" -> {
                                navCotroller.navigate(Screen.PreferenceScreen.route)
                            }
                            "profile" -> {
                                navCotroller.navigate(Screen.CelebProfileScreen.route)
                            }
                        }
                    })

                }) {

         ----->        PreferenceScreen(navController = navCotroller)

            }

        }

但这不是干净的代码!!我怎样才能使用函数指针来使它工作?

enter image description here

Android Kotlin Lambda android-jetpack-compose 函数参数

评论

1赞 z.g.y 12/3/2022
嗨,我添加了另一个答案,以防万一您需要将带有对齐方式的内容传递到布局中,例如.感谢您接受答案Column

答:

6赞 z.g.y 12/3/2022 #1

我无法编译您的代码,我不确定这是否会解决您的问题,但基于此

...我想添加一个参数,该参数采用可组合函数并运行 它里面

您可以将其用作参考。考虑这个可组合项,它接受任何可组合项并在其中运行它

@Composable
fun AnyComposable(
    anyComposable: @Composable () -> Unit
) {
    anyComposable()
}

另一个可组合项使用上面的可组合项,并将任何其他可组合项传递给它,如下所示

@Composable
fun MyScreen() {

    // big red Box composable
    val composable1 = @Composable {
        Box (
            modifier = Modifier
                .size(200.dp)
                .background(Color.Red)
        )
    }

    // small blue Box composable
    val composable2 = @Composable {
        Box(
            modifier = Modifier
                .size(80.dp)
                .background(Color.Blue)
        )
    }

    // small green rectangular Box composable
    val composable3 = @Composable {
        Box(
            modifier = Modifier
                .size(width = 100.dp, height = 30.dp)
                .background(Color.Green)
        )
    }

    var dynamicComposable by remember { mutableStateOf<@Composable () -> Unit> (@Composable { composable1() }) }

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {

        Row(
            modifier = Modifier.fillMaxWidth(),
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.SpaceBetween
        ) {
            Button(onClick = {
                dynamicComposable = composable1
            }) {
                Text("Comp 1")
            }

            Button(onClick = {
                dynamicComposable = composable2
            }) {
                Text("Comp 2")
            }

            Button(onClick = {
                dynamicComposable = composable3
            }) {
                Text("Comp 3")
            }
        }

        AnyComposable {
            dynamicComposable()
        }
    }
}

和输出:

enter image description here

评论

0赞 Dolev Dublon 12/24/2022
嘿,我最终做了一些不同的事情,这对我来说更简单易懂,我将发布我的答案和我的答案,作为为其他人解决问题的更受欢迎的方式
0赞 Dolev Dublon 12/24/2022
如果 U 可以的话,我会标记我的答案
5赞 z.g.y 12/3/2022 #2

或者,如果接收的可组合项有,比如说一个 ,并且你想事先设置参数可组合项,你可以考虑这个。ColumnAlignment

接受任何可组合项的同一可组合接收器,但预期的布局是 。Column

@Composable
fun AnyComposable(
    anyComposable: @Composable () -> Unit
) {
    Column(
        modifier = Modifier.fillMaxSize()
    ) {
        anyComposable()
    }
}
@Composable
fun MyScreen() {

    // big red box at Start of Column
    val composable1 : @Composable ColumnScope.() -> Unit = @Composable {
        Box (
            modifier = Modifier
                .align(Alignment.Start)
                .size(200.dp)
                .background(Color.Red)
        )
    }

    // small blue box at Center of Column
    val composable2 : @Composable ColumnScope.() -> Unit = @Composable {
        Box(
            modifier = Modifier
                .align(Alignment.CenterHorizontally)
                .size(80.dp)
                .background(Color.Blue)
        )
    }

    // small green rectangle at end of column
    val composable3 : @Composable ColumnScope.() -> Unit = @Composable {
        Box(
            modifier = Modifier
                .align(Alignment.End)
                .size(width = 100.dp, height = 30.dp)
                .background(Color.Green)
        )
    }

    var dynamicComposable by remember { mutableStateOf<@Composable ColumnScope.() -> Unit> (@Composable { composable1() }) }

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {

        Row(
            modifier = Modifier.fillMaxWidth(),
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.SpaceBetween
        ) {
            Button(onClick = {
                dynamicComposable = composable1
            }) {
                Text("Comp 1")
            }

            Button(onClick = {
                dynamicComposable = composable2
            }) {
                Text("Comp 2")
            }

            Button(onClick = {
                dynamicComposable = composable3
            }) {
                Text("Comp 3")
            }
        }

        AnyComposable {
            dynamicComposable()
        }
    }
}

输出:

enter image description here

评论

1赞 Dolev Dublon 12/3/2022
非常感谢!花时间解释所有这些代码!
0赞 Dolev Dublon 12/3/2022
嗨,我对您上传的 GIF 有疑问,我也想上传一个 gif,但它高于 2mb 的最大大小,您是如何录制视频并使 gif 小于 2mb 的
1赞 z.g.y 12/3/2022
哦,我使用 ezgif,使用第一个选项卡 -> ,页面将重新加载并向您显示预览视频,查找名为“”的选项(这是一个下拉菜单),选择“”(我认为这是最小的),然后按 在右下角的某个选项中有一个按钮可以下载文件。顺便说一句,我建议您在 Logcat 中记录您的操作时将其降低一点,我的设置是和GIF MakerChoose File and UploadSize320-AutoConvert to GIFDisketteBit Rate (Mbps): 4Resolution (% of native): 50
0赞 Dolev Dublon 2/16/2023 #3

嘿,伙计们,如果你想知道我是如何解决我自己的应用程序中的问题,请参阅此代码

最终结果

enter image description here

https://github.com/dolev146/Politi-Cal/tree/main/app/src/main/java/com/example/politi_cal

这是导航文件 我只是使用答案中描述的概念来传递屏幕作为参数

类似的东西,抽屉包裹内容屏幕

 composable(route = Screen.AdminOnlyScreen.route) {
            DrawerTopBar(navController = navCotroller, screen = { navController ->
                // this is the screen that will be drawn after the drawer
                // swipe screen
                AdminOnlyScreen(navController = navController, auth = auth)

            })
        }

        composable(route = Screen.UserAnalyticsScreen.route) {
            DrawerTopBar(navController = navCotroller, screen = { navController ->
                // this is the screen that will be drawn after the drawer
                // swipe screen


                UserAnalyticsScreen(
                    navController = navController,
                    auth = auth

                )

            })
        }

抽屉看起来像 https://github.com/dolev146/Politi-Cal/tree/main/app/src/main/java/com/example/politi_cal/screens/NavDrawer

// the drawer TopBar should get the navController as a parameter ,
// and the composable screen function that it needs to draw after the drawer
@Composable
fun DrawerTopBar(
    navController: NavController,
    auth: FirebaseAuth = FirebaseAuth.getInstance(),
    screen: @Composable (navController: NavController) -> Unit
) {
    var isAdminState by remember {
        mutableStateOf(false)
    }


    val scaffoldState = rememberScaffoldState()
    val scope = rememberCoroutineScope()
    val context = LocalContext.current
    isAdmin(context) {
        isAdminState = true
    }
    var menuContent = mutableListOf(
        MenuItem(
            id = "search",
            title = "Search",
            contentDescription = "Go to Search screen",
            icon = Icons.Default.Search
        ),

//        MenuItem(
//            id = "swipe",
//            title = "Swipe",
//            contentDescription = "Go to Swipe screen",
//            icon = Icons.Default.Home
//        ),
//        MenuItem(
//            id = "preferences",
//            title = "Preferences",
//            contentDescription = "Go to Preferences screen",
//            icon = Icons.Default.Settings
//        ),
//        MenuItem(
//            id = "logout",
//            title = "Logout",
//            contentDescription = "Go to Logout screen",
//            icon = Icons.Default.ExitToApp
//        ),
//        MenuItem(
//            id = "user analytics",
//            title = "User Analytics",
//            contentDescription = "Go to User Analytics screen",
//            icon = Icons.Default.MoreVert
//        )


    )

    if (isAdminState) {

        menuContent.add(
            MenuItem(
                id = "user analytics",
                title = "Analytics",
                contentDescription = "Go to User Analytics screen",
                icon = Icons.Default.MoreVert
            )
        )



        menuContent.add(
            MenuItem(
                id = "add celeb",
                title = "Add Celeb",
                contentDescription = "Go to User profile screen",
                icon = Icons.Default.Add
            )
        )
        menuContent.add(
            MenuItem(
                id = "admin analytics",
                title = "Admin Analytics",
                contentDescription = "Go to Admin Analytics screen",
                icon = Icons.Default.MoreVert
            )
        )
        menuContent.add(
            MenuItem(
                id = "admin user management",
                title = "User Search",
                contentDescription = "Go to Admin User Management screen",
                icon = Icons.Default.MoreVert
            )
        )











        menuContent.add(
            MenuItem(
                id = "logout",
                title = "Logout",
                contentDescription = "Go to Logout screen",
                icon = Icons.Default.ExitToApp
            )
        )


    } else {
        // this is user menu
        menuContent.add(
            MenuItem(
                id = "swipe",
                title = "Swipe",
                contentDescription = "Go to Swipe screen",
                icon = Icons.Default.Home
            )
        )

        menuContent.add(
            MenuItem(
                id = "user analytics",
                title = "Analytics",
                contentDescription = "Go to User Analytics screen",
                icon = Icons.Default.MoreVert
            )
        )

        menuContent.add(
            MenuItem(
                id = "preferences",
                title = "Preferences",
                contentDescription = "Go to Preferences screen",
                icon = Icons.Default.Settings
            )
        )

        menuContent.add(
            MenuItem(
                id = "my profile",
                title = "My Profile",
                contentDescription = "Go to User profile screen",
                icon = Icons.Default.Person
            )
        )














        menuContent.add(
            MenuItem(
                id = "logout",
                title = "Logout",
                contentDescription = "Go to Logout screen",
                icon = Icons.Default.ExitToApp
            )
        )


    }



    Scaffold(

        drawerGesturesEnabled = scaffoldState.drawerState.isOpen,
        scaffoldState = scaffoldState,
        topBar = {
            AppBar(onNavigationIconClick = {
                scope.launch {
                    scaffoldState.drawerState.open()
                }

            })
        },
        drawerContent = {
            DrawerHeader()
            DrawerBody(items = menuContent, onItemClick = {
                when (it.id) {
                    "swipe" -> {
                        navController.navigate(Screen.SwipeScreen.route)
                    }
                    "preferences" -> {
                        navController.navigate(Screen.PreferenceScreen2.route)
                    }
                    "user profile" -> {
                        navController.navigate(Screen.UserProfileScreen.route)
                    }
                    "add celeb" -> {
                        navController.navigate(Screen.AddCelebScreen.route)
                    }
                    "user analytics" -> {
                        navController.navigate(Screen.UserAnalyticsScreen.route)
                    }
                    "logout" -> {
//                        auth.signOut()
                        navController.navigate(Screen.SplashScreen.route)
                    }
                    "search" -> {
                        navController.navigate(Screen.SearchScreen.route)
                    }
                    "my profile" ->{

                        navController.navigate(Screen.UserProfileScreen.route)
                    }


                    "admin only" -> {
                        // check that the user is admin
                        // if not, show a message
                        // if yes, navigate to the admin only screen
                        if (isAdminState) {
                            navController.navigate(Screen.AdminOnlyScreen.route)
                        } else {
                            // show a message
                            Toast.makeText(context, "You are not an admin", Toast.LENGTH_SHORT)
                                .show()
                        }
                    }
                    "admin analytics" -> {
                        navController.navigate(Screen.AdminAnalyticsMenuScreen.route)
                    }
                    "admin user management" -> {
                        navController.navigate(Screen.AdminUserManagementScreen.route)
                    }


                }
            })

        }) {

        screen(navController = navController)

    }
}

观看链接中的完整代码,如果您喜欢我的解决方案,请点赞:)享受编码的乐趣