【问题标题】:Toolbar structured behavior for Jetpack ComposeJetpack Compose 的工具栏结构化行为
【发布时间】:2021-08-27 09:57:49
【问题描述】:

想象一下 Android 中工具栏的常见行为。

您在Activity 中定义了一个Toolbar 小部件,并且可以在片段中使用onCreateOptionsMenuonOptionsItemSelected 访问它。

但是,普通的 Jetpack Compose 无法实现类似的操作,因为无法访问在 ActivityScaffold 中定义的 Toolbar

所以想想这个场景。您有一个Activity,其中定义了Scaffold,并在其中定义了NavHostScaffoldNavHost 包含应用程序的所有子页面(其他可组合项)。标题可以处理查看导航目的地监听器,剩下的就是工具栏的动作。

您将如何根据您所在的当前页面/可组合项更改工具栏操作?并处理对这些操作的点击?

PS : 在每个页面中使用工具栏并不是一个解决方案,因为它会在动画页面之间切换时带来糟糕的用户体验,工具栏会在每个页面上消失并重新出现。

【问题讨论】:

    标签: android android-jetpack-compose


    【解决方案1】:

    我使用了一个名为 ToolbarController 的接口,其中包含回调方法,这些方法可以为调用脚手架的TopAppBar 时使用的变量设置值:

    @Composable  
    fun MyApp(){  
      
     var toolbarTitle by remember{ mutableStateOf("") }  
      
     // ToolbarController would be some interface you have defined  
     val toolbarController = object: ToolbarController {  
            override fun setTitle(title: String){  
                toolbarTitle = title  
            }  
        }  
      
     Scaffold(  
        topBar = { 
           TopAppBar( title = { Text(text = toolbarTitle) }  )  
        }  
     ){  
        SomeScreen(toolbarController = toolbarController)  
     }  
    }  
      
    @Composable  
    fun SomeScreen(  
        toolbarController: ToolbarController  
    ) {  
        //I'm not 100% sure I need to use an effect here, but I think so...
        //And I'm not sure this is the right one. It is not a coroutine I call,
        //but it of course works with normal calls. Also SideEffect runs on every
        //recompose according to the docs, and that's not what we want.
        //https://developer.android.com/jetpack/compose/side-effects
        LaunchedEffect(true){
           toolbarController.setTitle("Some screen title")  
        }
    }
    

    编辑: 并且很容易将它用于任何工具栏属性,您可以像这样创建界面:

    interface ToolbarController{
        fun configToolbar(
            title: String = "",
            navigationIcon: IconButton? = null,
            actions: List<IconButton> = listOf()
        )
    }
    

    关键是您只需创建回调函数并在 LaunchedEffect 中运行它们。这是从脚手架中的可组合项中设置工具栏属性的一种方法。接口的东西只是将这些回调分组的一种方式,所以它不会变得太乱。

    【讨论】:

    • 标题是简单的部分,我更想知道工具栏的操作
    • @AhmadSattout 您可以对操作使用完全相同的方法。实际上,我创建了一个ScaffoldController,在那里我保留了所有工具栏内容、FAB 和快餐栏的回调。
    • 你可以使用rememberUpdatedState,这样LaunchedEffect就不会在每次重组时都被调用
    【解决方案2】:

    您可以在Scaffold 级别使用TopAppBar,并使用您当前的目标来自定义topBar

    类似:

    topBar = {
    
        // Use your logic here
        val currentDestination = navBackStackEntry?.destination
        if (currentDestination == ....) {
          CustomAppBar()
        } else {
           TopAppBar(
              title = { /*...*/ },
              actions = {
                    if (currentDestination == ....) {
                        IconButton(onClick = { /* doSomething() */ }) {
                            Icon(Icons.Filled.Favorite, contentDescription = "")
                        }
                    }
                    IconButton(onClick = { /* doSomething() */ }) {
                        Icon(Icons.Filled.Add, contentDescription = "")
                    }
    
                }){ //... }
        }
    }
    

    否则,只需在每个屏幕中使用单独的TopAppBar

    【讨论】:

    • 是的,但是想象一下有 10 页,每页都有自己的逻辑。 1- 您必须为when 创建 10 个分支 2- 每个页面都应该使用它自己的状态来处理它的操作。例如,想象一个列表页面,其中有排序、过滤和搜索。无法从主要可组合访问这些状态
    • @AhmadSattout 在这种情况下在每个屏幕中使用单独的TopAppBar
    • 这在用户体验中看起来很糟糕,工具栏将消失并重新出现在页面之间的过渡或动画中
    猜你喜欢
    • 2021-07-17
    • 1970-01-01
    • 2020-05-10
    • 2021-10-27
    • 2023-01-22
    • 1970-01-01
    • 1970-01-01
    • 2021-07-05
    • 1970-01-01
    相关资源
    最近更新 更多