在组合中,用工具栏、底部栏和抽屉来实现素材应用程序的正确方法是什么



Jetpack Compose之前,我在View系统世界中的项目中使用导航组件。

应用程序只有一个活动——工具栏、底部栏和抽屉只添加到该活动中一次。

应用程序可能有许多屏幕(碎片(,只有顶部的目的地碎片显示底部栏和允许的抽屉,而其他碎片则被隐藏。

所有这些都是用活动中的导航组件处理的:

fun initNavigation() {
val topLevelDestinationFragments = setOf(R.id.homeFragment, R.id.photosFragment)

appBarConfiguration = AppBarConfiguration(
topLevelDestinationFragments,
binding.drawerLayout
)
setupActionBarWithNavController(navController, appBarConfiguration)
binding.drawerNavigationView.setupWithNavController(navController)
binding.bottomNavigationView.setupWithNavController(navController)

navController.addOnDestinationChangedListener { _, destination, _ ->
// don't change bars if a dialog fragment
if (destination is FloatingWindow) {
return@addOnDestinationChangedListener
}

// Google solution to hide navigation bars
// https://developer.android.com/guide/navigation/navigation-ui#listen_for_navigation_events

val allowBottomAndDrawerNavigation = destination.id in topLevelDestinationFragments

binding.bottomNavigationView.isVisible = allowBottomAndDrawerNavigation

binding.drawerLayout.setDrawerLockMode(
if (allowBottomAndDrawerNavigation) {
DrawerLayout.LOCK_MODE_UNLOCKED
} else {
DrawerLayout.LOCK_MODE_LOCKED_CLOSED
}
)

binding.toolbar.isVisible = // if we need to hide toolbar for specific fragments
}
}

override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}

很容易,不需要为每个片段单独添加工具栏等等(所以它只添加到活动的布局中,而不是添加到每个片段的每个布局中(

导航组件自动处理工具栏上的后退和菜单(抽屉(按钮,自动在它们之间切换,因为它知道的首选目的地

Compose有类似的东西吗?

因为我查了官方的谷歌样本";JetNews";来自CodeLabs githttps://github.com/googlecodelabs/android-compose-codelabs/tree/end/AccessibilityCodelab/app/src/main/java/com/example/jetnews/ui

他们在那里使用合成导航,但他们分别为每个合成屏幕添加了Scaffold

例如";主页";合成屏幕有自己的Scaffold

Scaffold(
scaffoldState = scaffoldState,
topBar = {
val title = stringResource(id = R.string.app_name)
InsetAwareTopAppBar(
title = { Text(text = title) },
navigationIcon = {
IconButton(onClick = { coroutineScope.launch { openDrawer() } }) {
Icon(
painter = painterResource(R.drawable.ic_jetnews_logo),
contentDescription = stringResource(R.string.cd_open_navigation_drawer)
)
}
}
)
}
)

并且";文章";合成屏幕有自己的Scaffold

Scaffold(
topBar = {
InsetAwareTopAppBar(
title = {
Text(
text = "Published in: ${post.publication?.name}",
style = MaterialTheme.typography.subtitle2,
color = LocalContentColor.current
)
},
navigationIcon = {
IconButton(onClick = onBack) {
Icon(
imageVector = Icons.Filled.ArrowBack,
// Step 4: Content descriptions
contentDescription = stringResource(
R.string.cd_navigate_up
)
)
}
}
)
}
)

因此,基本上,我们在这里复制代码,并为工具栏的navigationIcon(图标和动作(手动定义不同的逻辑

这是否意味着,如果我们有一些带有后退箭头按钮和底部栏的详细信息屏幕,那么我们就定义了一个单独的Scaffold,而不能对应用程序的所有组成屏幕只使用一个Scaffold

或者,我们可以像在View系统中使用导航组件一样,为所有组合屏幕实现相同的逻辑吗?同时设置顶部目的地,隐藏底部栏,并为非顶部目的地锁定抽屉。

我想正确的方法是用AppDrawerBottomBar设置顶级Scaffold(在这里我们定义主题和导航(,如果应该通过检查当前导航路线将其添加到屏幕上,则添加一些逻辑:

val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route

例如:

drawerContent = if (isTopLevelDestination) {
{
AppDrawer(
...
)
}
} else {
null
},
bottomBar = {
if (isTopLevelDestination) {
AppBottomBar(
...
)
}
}

但有了TopAppBar就没那么容易了。我们不能真正将其添加到顶级Scaffold中,以防工具栏可以对特定屏幕进行操作

这就是为什么每个屏幕都额外定义了Scaffold,并根据需要配置了TopAppBar

只有当一个应用程序非常简单,并且工具栏没有针对该应用程序的所有屏幕的任何操作,只有针对所有屏幕的硬编码标题时,我们才能在顶级Scaffold中定义TopAppBar,但对于更复杂的应用程序,每个屏幕都应该定义自己的工具栏

最新更新