在 Scaffold Jetpack Compose 中的特定屏幕上隐藏顶部和底部导航器
hide Top and Bottom Navigator on a specific screen inside Scaffold Jetpack Compose
我正在创建一个带有底部导航和抽屉的简单应用。
我将所有屏幕包裹在一个带有顶栏和底栏的脚手架中。
我想在特定屏幕上隐藏顶部栏和底部栏。有谁知道如何做到这一点
这里是设置导航的代码。
val navController = rememberNavController()
val scaffoldState = rememberScaffoldState(rememberDrawerState(DrawerValue.Closed))
Scaffold(
bottomBar = {
AppBottomBar(navController)
},
topBar = {
AppTopBar(scaffoldState)
},
drawerContent = {
DrawerContent(navController, scaffoldState)
},
scaffoldState = scaffoldState
) {
// ovoid bottom bar overlay content
Column(modifier = Modifier.padding(bottom = 58.dp)) {
AppNavigation(navController)
}
}
AppNavigation
包含 NavHost
用于导航到屏幕
目前,我可以通过检查当前路由来显示或隐藏 bottomBar、topBar 来实现。但我认为必须有更好的解决方案。我将所有屏幕包裹在 Scaffold 中的方式可能不正确。
val navController = rememberNavController()
val scaffoldState = rememberScaffoldState(rememberDrawerState(DrawerValue.Closed))
Scaffold(
bottomBar = {
if (currentRoute(navController) != "Example Screen") {
AppBottomBar(navController)
}
},
topBar = {
AppTopBar(scaffoldState)
},
drawerContent = {
DrawerContent(navController, scaffoldState)
},
floatingActionButton = {
FloatingButton(navController)
},
scaffoldState = scaffoldState
) {
// ovoid bottom bar overlay content
Column(modifier = Modifier.padding(bottom = 58.dp)) {
AppNavigation(navController)
}
}
@Composable
public fun currentRoute(navController: NavHostController): String? {
val navBackStackEntry by navController.currentBackStackEntryAsState()
return navBackStackEntry?.arguments?.getString(KEY_ROUTE)
}
我建议您将 AnimatedVisibility
用于 BottomNavigation
小部件和 TopAppBar
小部件,我认为这是最清晰的组合方式。
- 您应该使用
remeberSaveable
来存储 BottomBar 和 TopAppBar 的状态:
// State of bottomBar, set state to false, if current page route is "car_details"
val bottomBarState = rememberSaveable { (mutableStateOf(true)) }
// State of topBar, set state to false, if current page route is "car_details"
val topBarState = rememberSaveable { (mutableStateOf(true)) }
- 在可组合函数中,我们使用
when
来控制BottomBar和TopAppBar的状态,下面我们将bottomBarState
和topBarState
设置为true
,如果我们想显示BottomBar 和 TopAppBar,否则我们将 bottomBarState
和 topBarState
设置为 false
:
val navController = rememberNavController()
// Subscribe to navBackStackEntry, required to get current route
val navBackStackEntry by navController.currentBackStackEntryAsState()
// Control TopBar and BottomBar
when (navBackStackEntry?.destination?.route) {
"cars" -> {
// Show BottomBar and TopBar
bottomBarState.value = true
topBarState.value = true
}
"bikes" -> {
// Show BottomBar and TopBar
bottomBarState.value = true
topBarState.value = true
}
"settings" -> {
// Show BottomBar and TopBar
bottomBarState.value = true
topBarState.value = true
}
"car_details" -> {
// Hide BottomBar and TopBar
bottomBarState.value = false
topBarState.value = false
}
}
com.google.accompanist.insets.ui.Scaffold(
bottomBar = {
BottomBar(
navController = navController,
bottomBarState = bottomBarState
)
},
topBar = {
TopBar(
navController = navController,
topBarState = topBarState
)
},
content = {
NavHost(
navController = navController,
startDestination = NavigationItem.Cars.route,
) {
composable(NavigationItem.Cars.route) {
CarsScreen(
navController = navController,
)
}
composable(NavigationItem.Bikes.route) {
BikesScreen(
navController = navController
)
}
composable(NavigationItem.Settings.route) {
SettingsScreen(
navController = navController,
)
}
composable(NavigationItem.CarDetails.route) {
CarDetailsScreen(
navController = navController,
)
}
}
}
)
重要提示: 来自 Accompanist 的脚手架,在 build.gradle 中初始化。我们使用来自 Accompanist 的 Scaffold,因为我们需要完全控制填充,例如,在来自 Compose 的默认 Scaffold 中,如果我们有 TopAppBar,我们不能禁用顶部内容的填充。在我们的例子中,这是必需的,因为我们有 TopAppBar 的动画,内容应该在 TopAppBar 下,我们手动控制每个页面的填充。来自伴奏者的文档:https://google.github.io/accompanist/insets/.
- 将
BottomNavigation
放入AnimatedVisibility
,从bottomBarState
设置visible
值并设置enter
和exit
动画,在我的例子中我slideInVertically
用于 enter
动画,slideOutVertically
用于 exit
动画:
AnimatedVisibility(
visible = bottomBarState.value,
enter = slideInVertically(initialOffsetY = { it }),
exit = slideOutVertically(targetOffsetY = { it }),
content = {
BottomNavigation {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
items.forEach { item ->
BottomNavigationItem(
icon = {
Icon(
painter = painterResource(id = item.icon),
contentDescription = item.title
)
},
label = { Text(text = item.title) },
selected = currentRoute == item.route,
onClick = {
navController.navigate(item.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
)
}
}
}
)
- 将
TopAppBar
放入AnimatedVisibility
,设置topBarState
的visible
值并设置enter
和exit
动画,在我的例子中我slideInVertically
用于 enter
动画,slideOutVertically
用于 exit
动画:
AnimatedVisibility(
visible = topBarState.value,
enter = slideInVertically(initialOffsetY = { -it }),
exit = slideOutVertically(targetOffsetY = { -it }),
content = {
TopAppBar(
title = { Text(text = title) },
)
}
)
MainActivity 的完整代码:
package codes.andreirozov.bottombaranimation
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.res.painterResource
import androidx.navigation.NavController
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import codes.andreirozov.bottombaranimation.screens.BikesScreen
import codes.andreirozov.bottombaranimation.screens.CarDetailsScreen
import codes.andreirozov.bottombaranimation.screens.CarsScreen
import codes.andreirozov.bottombaranimation.screens.SettingsScreen
import codes.andreirozov.bottombaranimation.ui.theme.BottomBarAnimationTheme
@ExperimentalAnimationApi
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
BottomBarAnimationApp()
}
}
}
@ExperimentalAnimationApi
@Composable
fun BottomBarAnimationApp() {
// State of bottomBar, set state to false, if current page route is "car_details"
val bottomBarState = rememberSaveable { (mutableStateOf(true)) }
// State of topBar, set state to false, if current page route is "car_details"
val topBarState = rememberSaveable { (mutableStateOf(true)) }
BottomBarAnimationTheme {
val navController = rememberNavController()
// Subscribe to navBackStackEntry, required to get current route
val navBackStackEntry by navController.currentBackStackEntryAsState()
// Control TopBar and BottomBar
when (navBackStackEntry?.destination?.route) {
"cars" -> {
// Show BottomBar and TopBar
bottomBarState.value = true
topBarState.value = true
}
"bikes" -> {
// Show BottomBar and TopBar
bottomBarState.value = true
topBarState.value = true
}
"settings" -> {
// Show BottomBar and TopBar
bottomBarState.value = true
topBarState.value = true
}
"car_details" -> {
// Hide BottomBar and TopBar
bottomBarState.value = false
topBarState.value = false
}
}
// IMPORTANT, Scaffold from Accompanist, initialized in build.gradle.
// We use Scaffold from Accompanist, because we need full control of paddings, for example
// in default Scaffold from Compose we can't disable padding for content from top if we
// have TopAppBar. In our case it's required because we have animation for TopAppBar,
// content should be under TopAppBar and we manually control padding for each pages.
com.google.accompanist.insets.ui.Scaffold(
bottomBar = {
BottomBar(
navController = navController,
bottomBarState = bottomBarState
)
},
topBar = {
TopBar(
navController = navController,
topBarState = topBarState
)
},
content = {
NavHost(
navController = navController,
startDestination = NavigationItem.Cars.route,
) {
composable(NavigationItem.Cars.route) {
// show BottomBar and TopBar
LaunchedEffect(Unit) {
bottomBarState.value = true
topBarState.value = true
}
CarsScreen(
navController = navController,
)
}
composable(NavigationItem.Bikes.route) {
// show BottomBar and TopBar
LaunchedEffect(Unit) {
bottomBarState.value = true
topBarState.value = true
}
BikesScreen(
navController = navController
)
}
composable(NavigationItem.Settings.route) {
// show BottomBar and TopBar
LaunchedEffect(Unit) {
bottomBarState.value = true
topBarState.value = true
}
SettingsScreen(
navController = navController,
)
}
composable(NavigationItem.CarDetails.route) {
// hide BottomBar and TopBar
LaunchedEffect(Unit) {
bottomBarState.value = false
topBarState.value = false
}
CarDetailsScreen(
navController = navController,
)
}
}
}
)
}
}
@ExperimentalAnimationApi
@Composable
fun BottomBar(navController: NavController, bottomBarState: MutableState<Boolean>) {
val items = listOf(
NavigationItem.Cars,
NavigationItem.Bikes,
NavigationItem.Settings
)
AnimatedVisibility(
visible = bottomBarState.value,
enter = slideInVertically(initialOffsetY = { it }),
exit = slideOutVertically(targetOffsetY = { it }),
content = {
BottomNavigation {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
items.forEach { item ->
BottomNavigationItem(
icon = {
Icon(
painter = painterResource(id = item.icon),
contentDescription = item.title
)
},
label = { Text(text = item.title) },
selected = currentRoute == item.route,
onClick = {
navController.navigate(item.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
)
}
}
}
)
}
@ExperimentalAnimationApi
@Composable
fun TopBar(navController: NavController, topBarState: MutableState<Boolean>) {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val title: String = when (navBackStackEntry?.destination?.route ?: "cars") {
"cars" -> "Cars"
"bikes" -> "Bikes"
"settings" -> "Settings"
"car_details" -> "Cars"
else -> "Cars"
}
AnimatedVisibility(
visible = topBarState.value,
enter = slideInVertically(initialOffsetY = { -it }),
exit = slideOutVertically(targetOffsetY = { -it }),
content = {
TopAppBar(
title = { Text(text = title) },
)
}
)
}
结果:
不要忘记为组合函数使用@ExperimentalAnimationApi 注释。
更新: Compose 版本 1.1.0 及更高版本 @ExperimentalAnimationApi
不需要。
22.02.2022更新:我做了一些研究,更新了点2。现在我们用when
来控制topBarState
和bottomBarState
.
gitHub 上提供完整代码:
https://github.com/AndreiRoze/BottomBarAnimation/tree/with_animated_topbar
官方文档中可用的动画示例:
https://developer.android.com/jetpack/compose/animation
您可以使用 compositionlocal
,并且当您使用 CompositionLocalProvider
包装您的 mainActivity 时,将 supportActionBar 传递给您的子组件 在您希望隐藏 topBar 的屏幕上,调用 .hide () 方法
在顶部。见下文:
data class ShowAppBar(val show: ActionBar?)
internal val LocalAppBar = compositionLocalOf<ShowAppBar>{ error("No ActionBar provided") }
在mainActivity中,通过
传递ActionBar
val showy = ShowAppBar(show = supportActionBar )
.....
CompositionLocalProvider(
LocalAppBar provides showy
) {
YourTheme {
yourApp()
}
}
在屏幕上调用 >> LocalAppBar.current.show?.hide()
我找到的最简单的解决方案(没有动画)
fun MainScreen(modifier: Modifier = Modifier) {
val navController = rememberNavController()
var showBottomBar by rememberSaveable { mutableStateOf(true) }
val navBackStackEntry by navController.currentBackStackEntryAsState()
showBottomBar = when (navBackStackEntry?.destination?.route) {
"RouteOfScreenA" -> false // on this screen bottom bar should be hidden
"RouteOfScreenB" -> false // here too
else -> true // in all other cases show bottom bar
}
Scaffold(
modifier = modifier,
bottomBar = { if (showBottomBar) MyBottomNavigation(navController = navController) }
) { innerPadding ->
MyNavHost(
navController = navController,
modifier = Modifier.padding(innerPadding)
)
}
}
这对我有用。您从 currentRoute 函数获取当前路线,并检查您的 bottombar 可组合项以隐藏或显示 BottomNavigationView。
@Composable
fun currentRoute(navController: NavHostController): String? {
val navBackStackEntry by navController.currentBackStackEntryAsState()
return navBackStackEntry?.destination?.route
}
@Composable
fun MainScreenView() {
val navController = rememberNavController()
Scaffold(bottomBar = {
if (currentRoute(navController) != BottomNavItem.Setup.screen_route)
BottomNavigation(navController = navController)
}
) {
NavigationGraph(navController = navController)
}
}
我正在创建一个带有底部导航和抽屉的简单应用。
我将所有屏幕包裹在一个带有顶栏和底栏的脚手架中。 我想在特定屏幕上隐藏顶部栏和底部栏。有谁知道如何做到这一点
这里是设置导航的代码。
val navController = rememberNavController()
val scaffoldState = rememberScaffoldState(rememberDrawerState(DrawerValue.Closed))
Scaffold(
bottomBar = {
AppBottomBar(navController)
},
topBar = {
AppTopBar(scaffoldState)
},
drawerContent = {
DrawerContent(navController, scaffoldState)
},
scaffoldState = scaffoldState
) {
// ovoid bottom bar overlay content
Column(modifier = Modifier.padding(bottom = 58.dp)) {
AppNavigation(navController)
}
}
AppNavigation
包含 NavHost
用于导航到屏幕
目前,我可以通过检查当前路由来显示或隐藏 bottomBar、topBar 来实现。但我认为必须有更好的解决方案。我将所有屏幕包裹在 Scaffold 中的方式可能不正确。
val navController = rememberNavController()
val scaffoldState = rememberScaffoldState(rememberDrawerState(DrawerValue.Closed))
Scaffold(
bottomBar = {
if (currentRoute(navController) != "Example Screen") {
AppBottomBar(navController)
}
},
topBar = {
AppTopBar(scaffoldState)
},
drawerContent = {
DrawerContent(navController, scaffoldState)
},
floatingActionButton = {
FloatingButton(navController)
},
scaffoldState = scaffoldState
) {
// ovoid bottom bar overlay content
Column(modifier = Modifier.padding(bottom = 58.dp)) {
AppNavigation(navController)
}
}
@Composable
public fun currentRoute(navController: NavHostController): String? {
val navBackStackEntry by navController.currentBackStackEntryAsState()
return navBackStackEntry?.arguments?.getString(KEY_ROUTE)
}
我建议您将 AnimatedVisibility
用于 BottomNavigation
小部件和 TopAppBar
小部件,我认为这是最清晰的组合方式。
- 您应该使用
remeberSaveable
来存储 BottomBar 和 TopAppBar 的状态:
// State of bottomBar, set state to false, if current page route is "car_details"
val bottomBarState = rememberSaveable { (mutableStateOf(true)) }
// State of topBar, set state to false, if current page route is "car_details"
val topBarState = rememberSaveable { (mutableStateOf(true)) }
- 在可组合函数中,我们使用
when
来控制BottomBar和TopAppBar的状态,下面我们将bottomBarState
和topBarState
设置为true
,如果我们想显示BottomBar 和 TopAppBar,否则我们将bottomBarState
和topBarState
设置为false
:
val navController = rememberNavController()
// Subscribe to navBackStackEntry, required to get current route
val navBackStackEntry by navController.currentBackStackEntryAsState()
// Control TopBar and BottomBar
when (navBackStackEntry?.destination?.route) {
"cars" -> {
// Show BottomBar and TopBar
bottomBarState.value = true
topBarState.value = true
}
"bikes" -> {
// Show BottomBar and TopBar
bottomBarState.value = true
topBarState.value = true
}
"settings" -> {
// Show BottomBar and TopBar
bottomBarState.value = true
topBarState.value = true
}
"car_details" -> {
// Hide BottomBar and TopBar
bottomBarState.value = false
topBarState.value = false
}
}
com.google.accompanist.insets.ui.Scaffold(
bottomBar = {
BottomBar(
navController = navController,
bottomBarState = bottomBarState
)
},
topBar = {
TopBar(
navController = navController,
topBarState = topBarState
)
},
content = {
NavHost(
navController = navController,
startDestination = NavigationItem.Cars.route,
) {
composable(NavigationItem.Cars.route) {
CarsScreen(
navController = navController,
)
}
composable(NavigationItem.Bikes.route) {
BikesScreen(
navController = navController
)
}
composable(NavigationItem.Settings.route) {
SettingsScreen(
navController = navController,
)
}
composable(NavigationItem.CarDetails.route) {
CarDetailsScreen(
navController = navController,
)
}
}
}
)
重要提示: 来自 Accompanist 的脚手架,在 build.gradle 中初始化。我们使用来自 Accompanist 的 Scaffold,因为我们需要完全控制填充,例如,在来自 Compose 的默认 Scaffold 中,如果我们有 TopAppBar,我们不能禁用顶部内容的填充。在我们的例子中,这是必需的,因为我们有 TopAppBar 的动画,内容应该在 TopAppBar 下,我们手动控制每个页面的填充。来自伴奏者的文档:https://google.github.io/accompanist/insets/.
- 将
BottomNavigation
放入AnimatedVisibility
,从bottomBarState
设置visible
值并设置enter
和exit
动画,在我的例子中我slideInVertically
用于enter
动画,slideOutVertically
用于exit
动画:
AnimatedVisibility(
visible = bottomBarState.value,
enter = slideInVertically(initialOffsetY = { it }),
exit = slideOutVertically(targetOffsetY = { it }),
content = {
BottomNavigation {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
items.forEach { item ->
BottomNavigationItem(
icon = {
Icon(
painter = painterResource(id = item.icon),
contentDescription = item.title
)
},
label = { Text(text = item.title) },
selected = currentRoute == item.route,
onClick = {
navController.navigate(item.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
)
}
}
}
)
- 将
TopAppBar
放入AnimatedVisibility
,设置topBarState
的visible
值并设置enter
和exit
动画,在我的例子中我slideInVertically
用于enter
动画,slideOutVertically
用于exit
动画:
AnimatedVisibility(
visible = topBarState.value,
enter = slideInVertically(initialOffsetY = { -it }),
exit = slideOutVertically(targetOffsetY = { -it }),
content = {
TopAppBar(
title = { Text(text = title) },
)
}
)
MainActivity 的完整代码:
package codes.andreirozov.bottombaranimation
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.res.painterResource
import androidx.navigation.NavController
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import codes.andreirozov.bottombaranimation.screens.BikesScreen
import codes.andreirozov.bottombaranimation.screens.CarDetailsScreen
import codes.andreirozov.bottombaranimation.screens.CarsScreen
import codes.andreirozov.bottombaranimation.screens.SettingsScreen
import codes.andreirozov.bottombaranimation.ui.theme.BottomBarAnimationTheme
@ExperimentalAnimationApi
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
BottomBarAnimationApp()
}
}
}
@ExperimentalAnimationApi
@Composable
fun BottomBarAnimationApp() {
// State of bottomBar, set state to false, if current page route is "car_details"
val bottomBarState = rememberSaveable { (mutableStateOf(true)) }
// State of topBar, set state to false, if current page route is "car_details"
val topBarState = rememberSaveable { (mutableStateOf(true)) }
BottomBarAnimationTheme {
val navController = rememberNavController()
// Subscribe to navBackStackEntry, required to get current route
val navBackStackEntry by navController.currentBackStackEntryAsState()
// Control TopBar and BottomBar
when (navBackStackEntry?.destination?.route) {
"cars" -> {
// Show BottomBar and TopBar
bottomBarState.value = true
topBarState.value = true
}
"bikes" -> {
// Show BottomBar and TopBar
bottomBarState.value = true
topBarState.value = true
}
"settings" -> {
// Show BottomBar and TopBar
bottomBarState.value = true
topBarState.value = true
}
"car_details" -> {
// Hide BottomBar and TopBar
bottomBarState.value = false
topBarState.value = false
}
}
// IMPORTANT, Scaffold from Accompanist, initialized in build.gradle.
// We use Scaffold from Accompanist, because we need full control of paddings, for example
// in default Scaffold from Compose we can't disable padding for content from top if we
// have TopAppBar. In our case it's required because we have animation for TopAppBar,
// content should be under TopAppBar and we manually control padding for each pages.
com.google.accompanist.insets.ui.Scaffold(
bottomBar = {
BottomBar(
navController = navController,
bottomBarState = bottomBarState
)
},
topBar = {
TopBar(
navController = navController,
topBarState = topBarState
)
},
content = {
NavHost(
navController = navController,
startDestination = NavigationItem.Cars.route,
) {
composable(NavigationItem.Cars.route) {
// show BottomBar and TopBar
LaunchedEffect(Unit) {
bottomBarState.value = true
topBarState.value = true
}
CarsScreen(
navController = navController,
)
}
composable(NavigationItem.Bikes.route) {
// show BottomBar and TopBar
LaunchedEffect(Unit) {
bottomBarState.value = true
topBarState.value = true
}
BikesScreen(
navController = navController
)
}
composable(NavigationItem.Settings.route) {
// show BottomBar and TopBar
LaunchedEffect(Unit) {
bottomBarState.value = true
topBarState.value = true
}
SettingsScreen(
navController = navController,
)
}
composable(NavigationItem.CarDetails.route) {
// hide BottomBar and TopBar
LaunchedEffect(Unit) {
bottomBarState.value = false
topBarState.value = false
}
CarDetailsScreen(
navController = navController,
)
}
}
}
)
}
}
@ExperimentalAnimationApi
@Composable
fun BottomBar(navController: NavController, bottomBarState: MutableState<Boolean>) {
val items = listOf(
NavigationItem.Cars,
NavigationItem.Bikes,
NavigationItem.Settings
)
AnimatedVisibility(
visible = bottomBarState.value,
enter = slideInVertically(initialOffsetY = { it }),
exit = slideOutVertically(targetOffsetY = { it }),
content = {
BottomNavigation {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
items.forEach { item ->
BottomNavigationItem(
icon = {
Icon(
painter = painterResource(id = item.icon),
contentDescription = item.title
)
},
label = { Text(text = item.title) },
selected = currentRoute == item.route,
onClick = {
navController.navigate(item.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
)
}
}
}
)
}
@ExperimentalAnimationApi
@Composable
fun TopBar(navController: NavController, topBarState: MutableState<Boolean>) {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val title: String = when (navBackStackEntry?.destination?.route ?: "cars") {
"cars" -> "Cars"
"bikes" -> "Bikes"
"settings" -> "Settings"
"car_details" -> "Cars"
else -> "Cars"
}
AnimatedVisibility(
visible = topBarState.value,
enter = slideInVertically(initialOffsetY = { -it }),
exit = slideOutVertically(targetOffsetY = { -it }),
content = {
TopAppBar(
title = { Text(text = title) },
)
}
)
}
结果:
不要忘记为组合函数使用@ExperimentalAnimationApi 注释。
更新: Compose 版本 1.1.0 及更高版本 @ExperimentalAnimationApi
不需要。
22.02.2022更新:我做了一些研究,更新了点2。现在我们用when
来控制topBarState
和bottomBarState
.
gitHub 上提供完整代码: https://github.com/AndreiRoze/BottomBarAnimation/tree/with_animated_topbar
官方文档中可用的动画示例: https://developer.android.com/jetpack/compose/animation
您可以使用 compositionlocal
,并且当您使用 CompositionLocalProvider
包装您的 mainActivity 时,将 supportActionBar 传递给您的子组件 在您希望隐藏 topBar 的屏幕上,调用 .hide () 方法
在顶部。见下文:
data class ShowAppBar(val show: ActionBar?)
internal val LocalAppBar = compositionLocalOf<ShowAppBar>{ error("No ActionBar provided") }
在mainActivity中,通过
传递ActionBarval showy = ShowAppBar(show = supportActionBar )
.....
CompositionLocalProvider(
LocalAppBar provides showy
) {
YourTheme {
yourApp()
}
}
在屏幕上调用 >> LocalAppBar.current.show?.hide()
我找到的最简单的解决方案(没有动画)
fun MainScreen(modifier: Modifier = Modifier) {
val navController = rememberNavController()
var showBottomBar by rememberSaveable { mutableStateOf(true) }
val navBackStackEntry by navController.currentBackStackEntryAsState()
showBottomBar = when (navBackStackEntry?.destination?.route) {
"RouteOfScreenA" -> false // on this screen bottom bar should be hidden
"RouteOfScreenB" -> false // here too
else -> true // in all other cases show bottom bar
}
Scaffold(
modifier = modifier,
bottomBar = { if (showBottomBar) MyBottomNavigation(navController = navController) }
) { innerPadding ->
MyNavHost(
navController = navController,
modifier = Modifier.padding(innerPadding)
)
}
}
这对我有用。您从 currentRoute 函数获取当前路线,并检查您的 bottombar 可组合项以隐藏或显示 BottomNavigationView。
@Composable
fun currentRoute(navController: NavHostController): String? {
val navBackStackEntry by navController.currentBackStackEntryAsState()
return navBackStackEntry?.destination?.route
}
@Composable
fun MainScreenView() {
val navController = rememberNavController()
Scaffold(bottomBar = {
if (currentRoute(navController) != BottomNavItem.Setup.screen_route)
BottomNavigation(navController = navController)
}
) {
NavigationGraph(navController = navController)
}
}