Jetpack Compose - 使用和不使用 BottomBar 进行导航
Jetpack Compose - Navigate both with and without BottomBar
我有一个 Jetpack Compose 项目,其中包括使用 Scaffold
和 BottomBar
进行导航。顺便说一下,在特定屏幕中,我希望能够在 Scaffold
的 innerPadding
范围之外导航,并且只需要全屏显示可组合项。
使用 official docs 方法很好,除非您只需要导航单击 BottomBar
的项目。
我在这个 中找到了一个变通解决方案,您在其中使用了这个:
@Composable
fun OneScreen(navController: NavHostController) {
MainScaffold(
bottomBar = { BottomBar(navController = navController) },
content = {
// content
})
}
对于需要显示 BottomBar
的屏幕,并且:
@Composable
fun AnotherScreen() {
MainScaffold () {
//content
}
}
对于不需要显示的屏幕。
我称之为“变通解决方案”,因为通过这种方式,您需要在每个屏幕上重新生成 BottomBar
,由于重组(与标准 BottomBar 导航不同),每次您单击一个项目时它都会闪烁.
我觉得应该有一个更优雅的解决方案,但我缺乏确定它的经验,直到现在我才找到它。
编辑:我在相关方面错了。闪烁不是因为重组,而是因为 Navigation
组件提供的 Transition Animation
(Fade out/Fade in)。因此,可以消除 'flashing' 问题设置无过渡动画。当然,这仍然是一个变通解决方案,因为它限制了用户在应用程序动画方面的灵活性。
我发现了一种解决方法,我认为它比之前 post 中链接的解决方法更好。
我决定使用以下方法:
- 对不包含
BottomBar
的所有 Screens
使用 NavGraph
导航
- 为
BottomBar
个屏幕项目使用条件可组合生成
通过这种方式,我认为更容易管理 Transition Animations
并具有可读代码。
我确定这不是最好的解决方案(我不会将其标记为解决方案)但我认为值得分享该方法。
主要活动
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyBottomNavTestTheme {
val navController = rememberNavController()
SetupNavGraph(navController)
}
}
}
}
导航图
const val TEST_ROUTE = "test_route"
@Composable
fun SetupNavGraph(navController: NavHostController) {
NavHost(
navController = navController,
startDestination = Screen.MainScreen.route,
route = TEST_ROUTE
) {
composable(Screen.MainScreen.route) { MainScreen(navController) }
composable(Screen.AnotherScreen.route) { AnotherScreen(navController) }
}
}
主屏幕
@Composable
fun MainScreen(navController: NavHostController){
var selectedItem by remember { mutableStateOf(0)}
Scaffold(
bottomBar = { MyBottomNavBar() {selectedItem = it} }
) {
selectedItem ->
when (selectedItem){
0 -> TabOne(navController)
1 -> TabTwo(navController)
2 -> TabThree(navController)
}
}
}
@Composable
fun MyBottomNavBar(
onSelectedItem: (Int) -> Unit
) {
BottomNavigation() {
BottomNavigationItem(
selected = true,
onClick = { onSelectedItem(0) },
icon = { Icon(imageVector = Icons.Filled.Person, contentDescription = "Person Icon") },
enabled = true,
)
BottomNavigationItem(
selected = true,
onClick = { onSelectedItem(1) },
icon = { Icon(imageVector = Icons.Filled.Phone, contentDescription = "Phone Icon") },
enabled = true,
)
BottomNavigationItem(
selected = true,
onClick = { onSelectedItem(2) },
icon = { Icon(imageVector = Icons.Filled.Place, contentDescription = "Place Icon") },
enabled = true,
)
}
}
@Composable
fun TabOne(navController: NavHostController){
Surface(
modifier = Modifier
.fillMaxSize()
){
Text(
text = "TabOne"
)
}
}
@Composable
fun TabTwo(navController: NavHostController){
Surface(
modifier = Modifier
.fillMaxSize()
){
Text(
text = "TabTwo"
)
}
}
@Composable
fun TabThree(navController: NavHostController){
Surface(
modifier = Modifier
.fillMaxSize()
){
Column(){
Text(
text = "TabThree"
)
Button(onClick = {navController.navigate(Screen.AnotherScreen.route)}){
Text("Go to AnotherScreen")
}
}
}
}
另一个屏幕
@Composable
fun AnotherScreen(navController: NavHostController){
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Button(onClick = {navController.navigate(Screen.MainScreen.route)}) {
Text("Go to MainScreen")
}
}
}
我有一个 Jetpack Compose 项目,其中包括使用 Scaffold
和 BottomBar
进行导航。顺便说一下,在特定屏幕中,我希望能够在 Scaffold
的 innerPadding
范围之外导航,并且只需要全屏显示可组合项。
使用 official docs 方法很好,除非您只需要导航单击 BottomBar
的项目。
我在这个
@Composable
fun OneScreen(navController: NavHostController) {
MainScaffold(
bottomBar = { BottomBar(navController = navController) },
content = {
// content
})
}
对于需要显示 BottomBar
的屏幕,并且:
@Composable
fun AnotherScreen() {
MainScaffold () {
//content
}
}
对于不需要显示的屏幕。
我称之为“变通解决方案”,因为通过这种方式,您需要在每个屏幕上重新生成 BottomBar
,由于重组(与标准 BottomBar 导航不同),每次您单击一个项目时它都会闪烁.
我觉得应该有一个更优雅的解决方案,但我缺乏确定它的经验,直到现在我才找到它。
编辑:我在相关方面错了。闪烁不是因为重组,而是因为 Navigation
组件提供的 Transition Animation
(Fade out/Fade in)。因此,可以消除 'flashing' 问题设置无过渡动画。当然,这仍然是一个变通解决方案,因为它限制了用户在应用程序动画方面的灵活性。
我发现了一种解决方法,我认为它比之前 post 中链接的解决方法更好。 我决定使用以下方法:
- 对不包含
BottomBar
的所有 - 为
BottomBar
个屏幕项目使用条件可组合生成
Screens
使用 NavGraph
导航
通过这种方式,我认为更容易管理 Transition Animations
并具有可读代码。
我确定这不是最好的解决方案(我不会将其标记为解决方案)但我认为值得分享该方法。
主要活动
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyBottomNavTestTheme {
val navController = rememberNavController()
SetupNavGraph(navController)
}
}
}
}
导航图
const val TEST_ROUTE = "test_route"
@Composable
fun SetupNavGraph(navController: NavHostController) {
NavHost(
navController = navController,
startDestination = Screen.MainScreen.route,
route = TEST_ROUTE
) {
composable(Screen.MainScreen.route) { MainScreen(navController) }
composable(Screen.AnotherScreen.route) { AnotherScreen(navController) }
}
}
主屏幕
@Composable
fun MainScreen(navController: NavHostController){
var selectedItem by remember { mutableStateOf(0)}
Scaffold(
bottomBar = { MyBottomNavBar() {selectedItem = it} }
) {
selectedItem ->
when (selectedItem){
0 -> TabOne(navController)
1 -> TabTwo(navController)
2 -> TabThree(navController)
}
}
}
@Composable
fun MyBottomNavBar(
onSelectedItem: (Int) -> Unit
) {
BottomNavigation() {
BottomNavigationItem(
selected = true,
onClick = { onSelectedItem(0) },
icon = { Icon(imageVector = Icons.Filled.Person, contentDescription = "Person Icon") },
enabled = true,
)
BottomNavigationItem(
selected = true,
onClick = { onSelectedItem(1) },
icon = { Icon(imageVector = Icons.Filled.Phone, contentDescription = "Phone Icon") },
enabled = true,
)
BottomNavigationItem(
selected = true,
onClick = { onSelectedItem(2) },
icon = { Icon(imageVector = Icons.Filled.Place, contentDescription = "Place Icon") },
enabled = true,
)
}
}
@Composable
fun TabOne(navController: NavHostController){
Surface(
modifier = Modifier
.fillMaxSize()
){
Text(
text = "TabOne"
)
}
}
@Composable
fun TabTwo(navController: NavHostController){
Surface(
modifier = Modifier
.fillMaxSize()
){
Text(
text = "TabTwo"
)
}
}
@Composable
fun TabThree(navController: NavHostController){
Surface(
modifier = Modifier
.fillMaxSize()
){
Column(){
Text(
text = "TabThree"
)
Button(onClick = {navController.navigate(Screen.AnotherScreen.route)}){
Text("Go to AnotherScreen")
}
}
}
}
另一个屏幕
@Composable
fun AnotherScreen(navController: NavHostController){
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Button(onClick = {navController.navigate(Screen.MainScreen.route)}) {
Text("Go to MainScreen")
}
}
}