繁体   English   中英

如何使用 Android Jetpack Compose 实现 BottomAppBar 和 BottomDrawer 模式?

[英]How to implement BottomAppBar and BottomDrawer pattern using Android Jetpack Compose?

我正在使用 Jetpack Compose 构建 Android 应用程序。 尝试使用 BottomDrawer 模式实现 BottomAppBar 时卡住了。

底部导航抽屉是固定在屏幕底部而不是左边缘或右边缘的模式抽屉。 它们仅与底部应用栏一起使用。 这些抽屉在点击底部应用程序栏中的导航菜单图标时打开。

材料说明。io,并直接链接到视频

我试过使用 Scaffold,但它只支持侧抽屉。 附加到 Scaffold 内容的 BottomDrawer 显示在内容区域,并且 BottomDrawer 打开时不覆盖 BottomAppBar。 在脚手架 function 之后移动 BottomDrawer 也无济于事:BottomAppBar 被一些不可见的块覆盖并阻止单击按钮。

我也尝试过使用 BottomSheetScaffold,但它没有 BottomAppBar 插槽。

如果 Scaffold 不支持这种模式,那么实现它的正确方法是什么? 是否可以扩展 Scaffold 组件? 我担心从头开始的不正确实现可能会在以后尝试实现导航和小吃栏时产生问题。

我认为最新版本的脚手架确实有一个底部应用栏参数

您可能需要执行如下所示的操作。请注意在 BottomDrawer() 中如何调用 Scaffold。 尽管文档中说“它们(BottomDrawer)仅用于底部应用栏”,但令人困惑。 这让我觉得我必须在 Scaffold 中寻找一个 BottomDrawer() 插槽,或者我必须在 BottomAppBar() 中调用 BottomDrawer()。 在这两种情况下,我都经历了奇怪的行为。 这就是我解决这个问题的方法。 我希望它对某人有所帮助,特别是如果您正在尝试 Jetpack Compose 课程中的 Jetpack Compose 布局模块 5 中的代码实验室练习。

@ExperimentalMaterialApi
    @Composable
    fun MyApp() {
           var selectedItem by rememberSaveable { mutableStateOf(1)}

    BottomDrawer(
        modifier = Modifier.background(MaterialTheme.colors.onPrimary),
        drawerShape = Shapes.medium,
        drawerContent = {
            Column(Modifier.fillMaxWidth()) {
        for(i in 1..6) {
            when (i) {
                1 -> Row(modifier = Modifier.clickable {  }.padding(16.dp)){
                    Icon(imageVector = Icons.Rounded.Inbox, contentDescription = null)
                    Text(text = "Inbox")
                }
                2 -> Row(modifier = Modifier.clickable {  }.padding(16.dp)){
                    Icon(imageVector = Icons.Rounded.Outbox, contentDescription = null)
                    Text(text = "Outbox")
                }
                3 -> Row(modifier = Modifier.clickable {  }.padding(16.dp)){
                    Icon(imageVector = Icons.Rounded.Archive, contentDescription = null)
                    Text(text = "Archive")
                }
            }
        }
    }
        },
        gesturesEnabled = true
    ) {
        Scaffold(
            topBar = {
                TopAppBar(
                    title = {
                        Text(text = "Learning Compose Layouts" )
                    },
                    actions = {
                        IconButton(onClick = { /*TODO*/ }) {
                            Icon(Icons.Filled.Favorite, contentDescription = null)
                        }
                    }
                )
            },
            bottomBar = { BottomAppBar(cutoutShape = CircleShape, contentPadding = PaddingValues(0.dp)) {
                for (item in 1..4) {
                    BottomNavigationItem(
                        modifier = Modifier.clipToBounds(),
                        selected = selectedItem == item ,
                        onClick = { selectedItem = item },
                        icon = {
                            when (item) {
                                1 -> { Icon(Icons.Rounded.MusicNote, contentDescription = null) }
                                2 -> { Icon(Icons.Rounded.BookmarkAdd, contentDescription = null) }
                                3 -> { Icon(Icons.Rounded.SportsBasketball, contentDescription = null) }
                                4 -> { Icon(Icons.Rounded.ShoppingCart, contentDescription = null) }
                            }
                        }
                    )
                }
            } 
         }
      ) { innerPadding -> BodyContent(
                    Modifier
                    .padding(innerPadding)
                    .padding(8.dp))
        }
    }
}

他们(Google 开发人员)邀请您在 Jetpack Compose Layouts 路径中尝试将其他 Material Design 组件(例如BottomNavigationBottomDrawer到其各自的Scaffold插槽中,但并未为您提供解决方案。

BottomAppBarScaffold中确实有自己的插槽(即bottomBar ),但BottomDrawer没有 - 并且似乎专门设计用于明确地与 BottomAppBar 一起使用(请参阅BottomDrawer 的 API 文档)。

在 Jetpack Compose 路径的这一点上,我们已经介绍了state 提升插槽修改器,并且已经彻底解释了我们必须不时尝试看看如何最好地堆叠和组织可组合物——它们几乎总是有一种自然可表达的方式,使它们在实际中发挥最佳作用。

让我进行设置,以便我们在同一页面上:

class MainActivity : ComponentActivity() {
    @ExperimentalMaterialApi
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            LayoutsCodelabTheme {
                // A surface container using the 'background' color from the theme
                Surface(color = MaterialTheme.colors.background) {
                    LayoutsCodelab()
                }
            }
        }
    }
}

这是调用我们的主要/核心可组合的主要活动。 这就像在代码实验室中一样,除了@ExperimentalMaterialApi注释。

接下来是我们的主要/核心可组合:

@ExperimentalMaterialApi
@Composable
fun LayoutsCodelab() {
    val ( gesturesEnabled, toggleGesturesEnabled ) = remember { mutableStateOf( true ) }
    val scope = rememberCoroutineScope()
    val drawerState = rememberBottomDrawerState( BottomDrawerValue.Closed )

    // BottomDrawer has to be the true core of our layout
    BottomDrawer(
        gesturesEnabled = gesturesEnabled,
        drawerState = drawerState,
        drawerContent = {
            Button(
                modifier = Modifier.align( Alignment.CenterHorizontally ).padding( top = 16.dp ),
                onClick = { scope.launch { drawerState.close() } },
                content = { Text( "Close Drawer" ) }
            )
            LazyColumn {
                items( 25 ) {
                    ListItem(
                        text = { Text( "Item $it" ) },
                        icon = {
                            Icon(
                                Icons.Default.Favorite,
                                contentDescription = "Localized description"
                            )
                        }
                    )
                }
            }
        },
        // The API describes this member as "the content of the
        // rest of the UI"
        content = {
            // So let's place the Scaffold here
            Scaffold(
                topBar = {
                    AppBarContent()
                },
                //drawerContent = { BottomBar() }  // <-- Will implement a side drawer
                bottomBar = {
                    BottomBarContent(
                        coroutineScope = scope,
                        drawerState = drawerState
                    )
                            },
            ) {
                innerPadding ->
                BodyContent( Modifier.padding( innerPadding ).fillMaxHeight() )
            }
        }
    )
}

在这里,我们完全按照 compose 路径中的代码实验室所建议的那样利用了Scaffold 请注意我的评论, drawerContent是侧抽屉的自动实现。 这是一种直接使用[各自的] Composable(s)(材料设计的模态抽屉/工作表)绕过的相当不错的方法,但是,它不适用于我们的BottomDrawer 我认为 API 对于BottomDrawer实验性的,因为他们将来会进行更改以添加对像Scaffold这样的 Composables 的支持。

我基于将BottomDrawer与 Scaffold 一起使用的难度,该Scaffold设计为仅与BottomAppBar一起使用,其中明确包含一个用于BottomAppBar的插槽。

为了支持BottomDrawer ,我们必须了解它是一个底层布局drawerContent ,它包装了整个应用程序的 UI,当抽屉打开时,除了它的抽屉内容之外,它会阻止与任何东西的交互。 这要求它包含Scaffold ,并且要求我们将必要的 state 控制委托给包含我们的BottomAppBar实现的BottomBarContent组合:

@ExperimentalMaterialApi
@Composable
fun BottomBarContent( modifier: Modifier = Modifier, coroutineScope: CoroutineScope, drawerState: BottomDrawerState   ) {
    BottomAppBar{
        // Leading icons should typically have a high content alpha
        CompositionLocalProvider( LocalContentAlpha provides ContentAlpha.high ) {
            IconButton(
                onClick = {
                    coroutineScope.launch { drawerState.open() }
                }
            ) {
                Icon( Icons.Filled.Menu, contentDescription = "Localized description" )
            }
        }
        // The actions should be at the end of the BottomAppBar. They use the default medium
        // content alpha provided by BottomAppBar
        Spacer( Modifier.weight( 1f, true ) )
        IconButton( onClick = { /* doSomething() */ } ) {
            Icon( Icons.Filled.Favorite, contentDescription = "Localized description" )
        }
        IconButton( onClick = { /* doSomething() */ } ) {
            Icon( Icons.Filled.Favorite, contentDescription = "Localized description" )
        }
    }
}

结果告诉我们:

  • 顶部TopAppBar
  • 底部BottomAppBar
  • 单击BottomAppBar中的菜单图标将打开我们的BottomDrawer ,在打开时适当地覆盖 BottomAppBar 和整个内容空间。
  • BottomDrawer被正确隐藏,直到使用上面提到的按钮单击或手势来打开底部抽屉。
  • BottomAppBar中的菜单图标会在中途打开抽屉。
  • 手势通过快速短扫来中途打开底部抽屉,但只要你引导它。

暂无
暂无

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

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