首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在Scaffold内部的特定屏幕上隐藏顶部和底部导航器

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

Stack Overflow用户
提问于 2021-03-28 02:54:01
回答 5查看 11.6K关注 0票数 21

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

我把所有的屏幕都包在脚手架里,上面和底部都有。我想隐藏在一个特定的屏幕上的顶部和底部栏。有人知道如何实现这一目标吗?

下面是设置导航的代码。

代码语言:javascript
运行
复制
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

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2021-03-28 04:09:20

现在,我可以通过检查当前显示或隐藏bottomBar,topBar的路由来实现这一点。但我认为一定有更好的解决办法。我把所有屏幕都包在Scaffold里面的方式可能不对。

代码语言:javascript
运行
复制
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)
}
票数 21
EN

Stack Overflow用户

发布于 2022-02-06 19:56:56

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

  1. 您应该使用remeberSaveable存储BottomBar和TopAppBar的状态:

代码语言:javascript
运行
复制
// 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的控制状态,下面我们将bottomBarStatetopBarState设置为true,如果我们想显示BottomBar和TopAppBar,则将bottomBarStatetopBarState设置为true

代码语言:javascript
运行
复制
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,
                )
            }
        }
    }
)

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

  1. BottomNavigation放入AnimatedVisibility中,从bottomBarState中设置visible值,并设置enterexit动画,在我的示例中,enter动画使用slideInVerticallyexit动画使用slideOutVertically动画:H 236G 237

代码语言:javascript
运行
复制
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
                            }
                        }
                    )
                }
            }
        }
    )

  1. TopAppBar放入AnimatedVisibility中,从topBarState中设置visible值,并设置enterexit动画,在我的示例中,enter动画使用slideInVerticallyexit动画使用slideOutVertically动画:H 251G 252

代码语言:javascript
运行
复制
AnimatedVisibility(
        visible = topBarState.value,
        enter = slideInVertically(initialOffsetY = { -it }),
        exit = slideOutVertically(targetOffsetY = { -it }),
        content = {
            TopAppBar(
                title = { Text(text = title) },
            )
        }
    )

MainActivity的完整代码:

代码语言:javascript
运行
复制
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注释来编写函数。

更新:编写1.1.0及以上版本的不需要@ExperimentalAnimationApi

22.02.2022更新:I做了一些研究,并更新了第2点。现在我们使用when来控制topBarStatebottomBarState

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

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

票数 32
EN

Stack Overflow用户

发布于 2022-04-20 13:36:55

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

代码语言:javascript
运行
复制
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)
        )
    }
}
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66837991

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档