Jetpack Compose Navigation - 底部导航多个返回堆栈 - 查看模型范围问题

Posted

技术标签:

【中文标题】Jetpack Compose Navigation - 底部导航多个返回堆栈 - 查看模型范围问题【英文标题】:Jetpack Compose Navigation - Bottom Nav Multiple Back Stack - View Model Scoping Issue 【发布时间】:2021-11-24 03:01:11 【问题描述】:

所以我有两个选项卡,选项卡 A 和选项卡 B。每个选项卡都有自己的后退堆栈。我使用google docs中的代码实现了多回栈导航

    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。

这可能吗?

【问题讨论】:

【参考方案1】:

当您点击下一个导航项的速度过快,而当前视图显示转换尚未完成时,这似乎是一个错误。我已经reported了,请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 ->
    // ...

【讨论】:

【参考方案2】:

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

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

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

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

【讨论】:

以上是关于Jetpack Compose Navigation - 底部导航多个返回堆栈 - 查看模型范围问题的主要内容,如果未能解决你的问题,请参考以下文章

Android Jetpack Compose学习—— Jetpack compose基础布局

Android Jetpack Compose学习—— Jetpack compose基础布局

Android Jetpack Compose学习—— Jetpack compose入门

Android Jetpack Compose学习—— Jetpack compose入门

jetpack compose 接收返回参数

什么是Jetpack Compose?带你走进Jetpack Compose~