简体   繁体   English

Jetpack 使用动态操作撰写 TopAppBar

[英]Jetpack Compose TopAppBar with dynamic actions

@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
)

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

Usage Scenario: Using Compose Navigation to switch to different "screens", so the TopAppBar actions will be changed accordingly.使用场景:使用 Compose Navigation 切换到不同的“屏幕”,因此 TopAppBar 的动作会相应改变。 Eg.例如。 Share buttons for content screen, Filter button for listing screen内容屏幕的共享按钮,列表屏幕的过滤器按钮

Tried passing as a state to the TopAppBar's actions parameter, but having trouble to save the lambda block for the remember function.尝试将状态作为状态传递给 TopAppBar 的操作参数,但无法为remember函数保存 lambda 块。

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

Want to change the app bar actions content dynamically.想要动态更改应用栏操作内容。 Any way to do it?有什么办法吗?

This the approach I used but I'm pretty new on compose, so I cannot be sure it is the correct approach.这是我使用的方法,但我对撰写还很陌生,所以我不能确定它是正确的方法。

Let's assume I have 2 screens: ScreenA and ScreenB They are handled by MainActivity screen.假设我有 2 个屏幕: ScreenA 和 ScreenB 它们由 MainActivity 屏幕处理。 This is our 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
                                )
                            }
                        }
                    }
                }
            }
        }
    }
}

As you can see I'm using a mutable state of a class which represents the state of our MainActivity (where the TopAppBar is declared and composed), in this example there is the title and the actions of our TopAppBar.如您所见,我正在使用一个类的可变状态,它表示我们的 MainActivity 的状态(声明和组成 TopAppBar 的地方),在此示例中,有我们的 TopAppBar 的标题和操作。

This mutable state is set with a callback function called inside the composition of each screen.这种可变状态是通过在每个屏幕组成内部调用的回调函数设置的。

Here you can see the ScreenA在这里你可以看到 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")
        }
    }
}

And the ScreenB和屏幕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")
        }
    }
}

And finally this is the data class of our state:最后这是我们状态的数据类:

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

In this way you have a dynamic appbar declared in the main activity but each screen is responsable to handle the content of the appbar.通过这种方式,您在主活动中声明了一个动态 appbar,但每个屏幕都负责处理 appbar 的内容。

First you need to add navigation dependency on you jetpack compose projects.首先,您需要在您的 jetpack compose 项目中添加导航依赖项。

You can read the doc from this https://developer.android.com/jetpack/compose/navigation您可以从此https://developer.android.com/jetpack/compose/navigation阅读文档

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

Then define your screen in sealed class:然后在密封类中定义您的屏幕:

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

and this is the navigation function look like这是导航功能的样子

@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
        }
    }
}

Finally in your mainactivity class最后在你的 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)
                }
            }
        }
    }

I'm so sorry if the explanation to sort, because the limitation of my English我很抱歉如果解释排序,因为我的英语限制

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM