【问题标题】:Jetpack Compose TopAppBar with dynamic actionsJetpack 使用动态操作撰写 TopAppBar
【发布时间】:2022-06-25 06:44:26
【问题描述】:
@Composable
fun TopAppBar(
    title: @Composable () -> Unit,
    modifier: Modifier = Modifier,
    navigationIcon: @Composable (() -> Unit)? = null,
    actions: @Composable RowScope.() -> Unit = {},
    backgroundColor: Color = MaterialTheme.colors.primarySurface,
    contentColor: Color = contentColorFor(backgroundColor),
    elevation: Dp = AppBarDefaults.TopAppBarElevation
)

动作:@Composable RowScope.() -> Unit = {}

使用场景: 使用 Compose Navigation 切换到不同的“屏幕”,因此 TopAppBar 操作将相应更改。例如。内容屏幕的分享按钮,列表屏幕的过滤按钮

尝试将状态作为状态传递给 TopAppBar 的 actions 参数,但无法为 remember 函数保存 lambda 块。

val (actions, setActions) = rememberSaveable { mutableStateOf( appBarActions ) }

想要动态更改应用栏操作内容。有什么办法吗?

【问题讨论】:

标签: android kotlin navigation android-jetpack-compose


【解决方案1】:

这是我使用的方法,但我对撰写还很陌生,所以我不能确定它是正确的方法。

假设我有 2 个屏幕:ScreenA 和 ScreenB 它们由 MainActivity 屏幕处理。 这是我们的 MainActivity:

@ExperimentalComposeUiApi
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    @OptIn(ExperimentalMaterial3Api::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            CoolDrinksTheme {
                val navController = rememberNavController()

                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    var appBarState by remember {
                        mutableStateOf(AppBarState())
                    }

                    Scaffold(
                        topBar = {
                            SmallTopAppBar(
                                title = {
                                    Text(text = appBarState.title)
                                },
                                actions = {
                                    appBarState.actions?.invoke(this)
                                }
                            )
                        }
                    ) { values ->
                        NavHost(
                            navController = navController,
                            startDestination = "screen_a",
                            modifier = Modifier.padding(
                                values
                            )
                        ) {
                            composable("screen_a") {
                                ScreenA(
                                    onComposing = {
                                        appBarState = it
                                    },
                                    navController = navController
                                )
                            }
                            composable("screen_b") {
                                ScreenB(
                                    onComposing = {
                                        appBarState = it
                                    },
                                    navController = navController
                                )
                            }
                        }
                    }
                }
            }
        }
    }
}

如您所见,我正在使用一个类的可变状态,它表示我们的 MainActivity 的状态(声明和组成 TopAppBar 的地方),在此示例中,有我们的操作栏的标题和操作。

这种可变状态是通过在每个屏幕组成中调用的回调函数设置的。

这里你可以看到ScreenA

@Composable
fun ScreenA(
    onComposing: (AppBarState) -> Unit,
    navController: NavController
) {

    LaunchedEffect(key1 = true) {
        onComposing(
            AppBarState(
                title = "My Screen A",
                actions = {
                    IconButton(onClick = { }) {
                        Icon(
                            imageVector = Icons.Default.Favorite,
                            contentDescription = null
                        )
                    }
                    IconButton(onClick = { }) {
                        Icon(
                            imageVector = Icons.Default.Filter,
                            contentDescription = null
                        )
                    }
                }
            )
        )
    }


    Column(
        modifier = Modifier
            .fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(
            text = "Screen A"
        )
        Button(onClick = {
            navController.navigate("screen_b")
        }) {
            Text(text = "Navigate to Screen B")
        }
    }
}

还有屏幕B

@Composable
fun ScreenB(
    onComposing: (AppBarState) -> Unit,
    navController: NavController
) {

    LaunchedEffect(key1 = true) {
        onComposing(
            AppBarState(
                title = "My Screen B",
                actions = {
                    IconButton(onClick = { }) {
                        Icon(
                            imageVector = Icons.Default.Home,
                            contentDescription = null
                        )
                    }
                    IconButton(onClick = { }) {
                        Icon(
                            imageVector = Icons.Default.Delete,
                            contentDescription = null
                        )
                    }
                }
            )
        )
    }


    Column(
        modifier = Modifier
            .fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(
            text = "Screen B"
        )
        Button(onClick = {
            navController.popBackStack()
        }) {
            Text(text = "Navigate back to Screen A")
        }
    }
}

最后这是我们状态的数据类:

data class AppBarState(
    val title: String = "",
    val actions: (@Composable RowScope.() -> Unit)? = null
)

通过这种方式,您在主活动中声明了一个动态 appbar,但每个屏幕都负责处理 appbar 的内容。

【讨论】:

    【解决方案2】:

    首先你需要为你的jetpack compose项目添加导航依赖。

    您可以从此https://developer.android.com/jetpack/compose/navigation阅读文档

    def nav_version = "2.4.1"
    implementation "androidx.navigation:navigation-compose:$nav_version"
    

    然后在密封类中定义你的屏幕:

    sealed class Screen(var icon: ImageVector, var route: String) {
        object ContentScreen: Screen(Icons.Default.Home, "home")
        object ListingScreen: Screen(Icons.Default.List, "list")
    }
    

    这是导航功能的样子

    @Composable
    fun Navigation(paddingValues: PaddingValues, navController: NavHostController) {
        NavHost(navController, startDestination = Screen.ContentScreen.route, modifier = Modifier.padding(paddingValues)) {
            composable(Screen.ContentScreen.route) {
                //your screen content
            }
            composable(Screen.ListingScreen.route) {
                //your listing screen here
            }
        }
    }
    

    终于在你的 mainactivity 类中

    override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContent {
                TestAppTheme {
                    val navController = rememberNavController()
                    val navBackStackEntry by navController.currentBackStackEntryAsState()
                    val currentRoute = navBackStackEntry?.destination?.route
                    Scaffold(
                        topBar = {
                            TopAppBar(title = { Text(text = "main screen") }, actions = {
                                if (currentRoute == Screen.ContentScreen.route) {
                                    //your share button action here
                                } else if (currentRoute == Screen.ListingScreen.route) {
                                    //your filter button here
                                } else {
                                    //other action
                                }
                            })
                        }
                    ) {
                        Navigation(paddingValues = it, navController = navController)
                    }
                }
            }
        }
    

    很抱歉如果解释排序,因为我的英语限制

    【讨论】:

    • 这可能可行,但我正在寻找一种方法将操作按钮逻辑分散到每个相应的屏幕类中。想象一下导航栏标题场景,尽量避免在根级屏幕定义所有屏幕标题。
    猜你喜欢
    • 2022-11-22
    • 2023-02-17
    • 1970-01-01
    • 2021-07-12
    • 1970-01-01
    • 1970-01-01
    • 2020-03-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多