当前位置: 首页 > 知识库问答 >
问题:

在Scaffold Jetpack Compose内的特定屏幕上隐藏顶部和底部导航器

陆啸
2023-03-14

我正在创建一个带有底部导航和抽屉的简单应用程序

我将所有屏幕包装在带有顶部栏和底部栏的脚手架中。我想在特定屏幕上隐藏顶部栏和底部栏。有人知道如何实现吗

以下是设置导航的代码。

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)
    }
}

应用导航包含NavHost用于导航到屏幕

共有3个答案

籍弘伟
2023-03-14

我找到的最简单的解决方案(没有动画)

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)
        )
    }
}
羊舌和安
2023-03-14

现在,我可以通过检查当前路由以显示或隐藏底部栏,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)
}
太叔鹏云
2023-03-14

我建议您使用AnimatedVienceforBottomNaviption小部件和TopAppBar小部件,我认为这是最清晰的撰写方式。

    < li >您应该使用< code>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)) }
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,
                )
            }
        }
    }
)

重要提示:来自Companist的脚手架,在build.gradle中初始化。我们使用来自Companist的脚手架,因为我们需要完全控制填充,例如,在Compose的默认脚手架中,如果我们有TopAppBar,我们不能禁用顶部内容的填充。在我们的例子中,这是必需的,因为我们为TopAppBar提供了动画,内容应该在TopAppBar下,我们手动控制每个页面的填充。来自伴奏者的文档:https://google.github.io/accompanist/insets/.

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
                            }
                        }
                    )
                }
            }
        }
    )
AnimatedVisibility(
        visible = topBarState.value,
        enter = slideInVertically(initialOffsetY = { -it }),
        exit = slideOutVertically(targetOffsetY = { -it }),
        content = {
            TopAppBar(
                title = { Text(text = title) },
            )
        }
    )

主要活动的完整代码:

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及以上的<code>@ExperimentalaImationAPI</code>。

22.02.2022更新:我做了一些研究,更新点2.现在我们使用time进行控制topBarStateBotomBarState

gitHub 上提供的完整代码:https://github.com/AndreiRoze/BottomBarAnimation/tree/with_animated_topbar

官方文档中提供的动画示例:https://developer.android.com/jetpack/compose/animation

 类似资料:
  • 我试图创建一个单一活动的Android应用程序。我有带有BottomNavigationView的MainActivity (only activity),三个顶级片段和一些子片段。我的要求是,每当屏幕显示顶级片段,底部导航应该是可见的,这样切换是可能的。但是当我查看任何子片段时,底部导航应该是隐藏的。是否有任何现成的方式使用导航组件或需要手动更改可见性?

  • 我的应用程序有问题。我有一个底部导航视图,包含3个不同的片段,尽管目前只有主要的内容。当我试图从左侧的两个片段中的一个移动到主视图时,问题就出现了,即当底部导航视图被隐藏时。我附上了主代码。 PD:我有25.3.1版本的所有库(如果有用的话)。 感谢您的关注。 activity\u main。xml 主要活动。Java语言 我也给你添加了两张图片。 Ofertas片段 主片段

  • 在我的android应用程序中,当用户聚焦SearchView时,我希望底部的菜单栏消失(这也会弹出软键盘)。当SearchView失去焦点时,我想再次显示底部的导航栏。 我尝试过使用,视图确实隐藏或显示,但由于某种原因,它总是保留其高度。下面是我BottomNavigationView的代码: 处理隐藏/显示导航栏的代码: BottomNavigationView由线性布局控制,如下所示:

  • 我必须在向上滚动时隐藏底部导航视图,在向下滚动时显示。如何实现这个?我的布局是这样的 我附上了视图截图。请检查一下。

  • BottomBar有4个目的地:ScreenA,ScreenB,ScreenC和ScreenD。 在ScreenA上按下按钮(不是底部栏项)时,我想使用参数转到ScreenB。 导航图 底部栏 到目前为止,单击ScreenA上的按钮会将我导航到ScreenB,但参数值始终为0,因为我永远不会传递在lambda中传递的参数。我使用的是compose_version='1.2.0-alpha04'和"

  • 我正试图创建一个响应导航栏,它固定在页面顶部,链接高度为全高(100%),这样它们的背景颜色就可以改变。到目前为止,我只成功地修复了导航栏,并且这些链接在设备之间只是有点兼容。我将使用这种效果,即媒体查询强制菜单进入包含在图像中的导航。http://getbootstrap.com/examples/navbar-fixed-top/.我不喜欢bootstrap,我希望在将来自己能够做到这一点。有