4

所以我有两个选项卡,选项卡 A 和选项卡 B。每个选项卡都有自己的后台堆栈。我使用这个谷歌文档中的代码实现了多回栈导航

    val navController = rememberNavController()
Scaffold(
  bottomBar = {
    BottomNavigation {
      val navBackStackEntry by navController.currentBackStackEntryAsState()
      val currentDestination = navBackStackEntry?.destination
      items.forEach { screen ->
        BottomNavigationItem(
          icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
          label = { Text(stringResource(screen.resourceId)) },
          selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
          onClick = {
            navController.navigate(screen.route) {
              // Pop up to the start destination of the graph to
              // avoid building up a large stack of destinations
              // on the back stack as users select items
              popUpTo(navController.graph.findStartDestination().id) {
                saveState = true
              }
              // Avoid multiple copies of the same destination when
              // reselecting the same item
              launchSingleTop = true
              // Restore state when reselecting a previously selected item
              restoreState = true
            }
          }
        )
      }
    }
  }
) { 
  NavHost(navController, startDestination = A1.route) {
    composable(A1.route) { 
       val viewModelA1 = hiltViewModel() 
       A1(viewModelA1) 
    }
    composable(A2.route) { 
       val viewModelA2 = hiltViewModel() 
       A2(viewModelA2) 
    }
    composable(A3.route) { 
       val viewModelA3 = hiltViewModel() 
       A3(viewModelA3) 
    }
  }
}

选项卡 A 有 3 个屏幕(屏幕 A1 -> 屏幕 A2 -> 屏幕 A3)。我使用该函数来实例化视图模型,并在每个屏幕hiltViewModel()的块内调用它composable()

问题是当我从 A1 导航到 A2 再到 A3 时,然后当我将选项卡更改为选项卡 B 时,屏幕 A2 的视图模型似乎正在被处理(onCleared被调用)。因此,当我返回显示屏幕 A3 的选项卡 A 然后返回屏幕 A2 时,再次实例化 A2 的视图模型(再次init调用块)。我想要实现的是为这个流程保留 A2 的视图模型,直到我退出 A2。

这甚至可能吗?

4

2 回答 2

0

找到了这个问题的根本原因。我正在使用这些依赖项,但它们似乎并没有在一起。

androidx.hilt:hilt-navigation-compose:1.0.0-alpha03 

androidx.navigation:navigation-compose:2.4.0-alpha10" 

我删除了navigation:navigation-compose依赖项,它现在似乎工作正常。

于 2021-10-04T07:08:41.043 回答
0

当您太快地单击下一个导航项时,这似乎是一个错误,而当前视图显示转换尚未完成。我已经举报了,请star一下,引起更多关注。

同时,您可以等待当前屏幕转换完成,然后再导航到下一个屏幕。为此,您可以检查visibleEntries变量并仅在它仅包含单个项目后导航。

此外,我认为当前文档提供的底部导航并不是最好的示例,因为如果您不在起始目的地屏幕上,按下后退按钮将带您回到起始目的地,而我希望视图被关闭。所以我也改变了你的导航方式,如果你对文档行为没问题,你可以fun navigate()用你自己的替换内容。

val navController = rememberNavController()
var waitEndAnimationJob by remember { mutableStateOf<Job?>(null)}
Scaffold(
    bottomBar = {
        BottomNavigation {
            val navBackStackEntry by navController.currentBackStackEntryAsState()
            val currentDestination = navBackStackEntry?.destination
            val scope = rememberCoroutineScope()
            items.forEach { screen ->
                fun navigate() {
                    navController.navigate(screen.route) {
                        val navigationRoutes = items
                            .map(Screen::route)
                        val firstBottomBarDestination = navController.backQueue
                            .firstOrNull { navigationRoutes.contains(it.destination.route) }
                            ?.destination
                        // remove all navigation items from the stack
                        // so only the currently selected screen remains in the stack
                        if (firstBottomBarDestination != null) {
                            popUpTo(firstBottomBarDestination.id) {
                                inclusive = true
                                saveState = true
                            }
                        }
                        // Avoid multiple copies of the same destination when
                        // reselecting the same item
                        launchSingleTop = true
                        // Restore state when reselecting a previously selected item
                        restoreState = true
                    }
                }
                BottomNavigationItem(
                    icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
                    label = { Text(stringResource(screen.resourceId)) },
                    selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
                    onClick = {
                        // if we're already waiting for an other screen to start appearing
                        // we need to cancel that job   
                        waitEndAnimationJob?.cancel()
                        if (navController.visibleEntries.value.count() > 1) {
                            // if navController.visibleEntries has more than one item
                            // we need to wait animation to finish before starting next navigation
                            waitEndAnimationJob = scope.launch {
                                navController.visibleEntries
                                    .collect { visibleEntries ->
                                        if (visibleEntries.count() == 1) {
                                            navigate()
                                            waitEndAnimationJob = null
                                            cancel()
                                        }
                                    }
                            }
                        } else {
                            // otherwise we can navigate instantly
                            navigate()
                        }
                    }
                )
            }
        }
    }
) { innerPadding ->
    // ...
}
于 2021-10-03T10:18:01.763 回答