我刚刚开始使用 Jetpack Compose,想尝试一下 BottomNavigation。包含三个项目的基本实现是没有问题的。现在,当单击列表项时,三个屏幕之一应导航到详细信息屏幕。出现的问题是,在详细信息屏幕上,不再选择底部导航项。
这是我的实现:
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
WhoHasItTheme {
val navController = rememberNavController()
Scaffold(
bottomBar = {
BottomNavigationBar(
items = listOf(
BottomNavItem(
name = "Home",
route = Screen.Home.route,
icon = Icons.Default.Home
),
BottomNavItem(
name = "Search",
route = Screen.GameListScreen.route,
icon = Icons.Default.Search
),
BottomNavItem(
name = "Profile",
route = Screen.Profile.route,
icon = Icons.Default.Person
)
),
navController = navController,
onItemClick = {
navController.navigate(it.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
)
}
) {
Box(modifier = Modifier.padding(it)) {
Navigation(navController = navController)
}
}
}
}
}
}
@Composable
fun Navigation(navController: NavHostController) {
NavHost(
navController = navController,
startDestination = Screen.Home.route
) {
composable(Screen.Home.route) {
HomeScreen()
}
composable(Screen.GameListScreen.route) {
GameListScreen(navController)
}
composable(
route = "${Screen.GameDetailScreen.route}/{gameId}",
arguments = listOf(navArgument("gameId") { type = NavType.IntType })
) {
GameDetailScreen()
}
composable(Screen.Profile.route) {
ProfileScreen()
}
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun BottomNavigationBar(
items: List<BottomNavItem>,
navController: NavController,
modifier: Modifier = Modifier,
onItemClick: (BottomNavItem) -> Unit
) {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
BottomNavigation(
modifier
) {
items.forEach { item ->
BottomNavigationItem(
selected = currentDestination?.hierarchy?.any { it.route == item.route } == true,
onClick = { onItemClick(item) },
selectedContentColor = MaterialTheme.colors.onSurface,
label = { Text(item.name) },
alwaysShowLabel = false,
icon = {
Column(horizontalAlignment = CenterHorizontally) {
if (item.badgeCount > 0) {
BadgeBox(
badgeContent = {
Text(item.badgeCount.toString())
}
) {
Icon(imageVector = item.icon, contentDescription = item.name)
}
} else {
Icon(imageVector = item.icon, contentDescription = item.name)
}
}
}
)
}
}
}
根据我的理解,
currentDestination?.hierarchy
还应该包括根屏幕(GameListScreen)。我在这里理解错了什么以及如何才能使每个底部导航项目的根屏幕下方的屏幕仍然将其选项卡项目设置为“选定”?
似乎
currentDestination?.hierarchy
没有按预期工作,所以我建议解决这个问题
添加此文件 TapSelection.kt,其中 selectedTapMap 作为我们选择的来源,添加到您的主要水龙头,在本例中我有三个主要水龙头
我们还有两种方法来更新地图并获取选定的水龙头
val selectedTapMap = hashMapOf<String, String>().apply {
this[Screen.RidesScreen.route] = Screen.RidesScreen.route
this[Screen.InboxScreen.route] = Screen.InboxScreen.route
this[Screen.ProfileScreen.route] = Screen.ProfileScreen.route
}
fun updateSelectedTapMap(currentScreen: String?, newScreen: String) {
currentScreen?.let {
selectedTapMap[newScreen] = selectedTapMap.getOrDefault(currentScreen.firstSlot(), "")
}
}
fun getSelectedTapOrNull(currentRout: String?) = selectedTapMap.getOrDefault(
currentRout?.firstSlot(),
null
)
此后,每当您导航时,请调用更新乐趣
updateSelectedTapMap(this.currentDestination?.route, Screen.NewSubScreen.route)
用此行替换选定的 val 行
val selected = currentDestination?.hierarchy?.any {
getSelectedTapOrNull(it.route) == screen.route
} == true
添加这个方法,是字符串扩展
fun String.firstSlot() = this.split("/").firstOrNull()
这样,当您导航到新屏幕时,它将从当前屏幕中获取选定的点击,我们在 TapSelection 文件中有一个事实来源,添加新屏幕时您需要添加的就是调用更新函数。
就是这样,希望对你有帮助
在这种情况下,您应该在构建时使用带有 navigation() 的 nested graph 导航图。例如:
navigation(startDestination = Screen.GameListScreen.route) {
composable(Screen.GameListScreen.route) {
GameListScreen(navController)
}
composable(
route = "${Screen.GameDetailScreen.route}/{gameId}",
arguments = listOf(navArgument("gameId") { type = NavType.IntType })
) {
GameDetailScreen()
}
}