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

导航组件-起始目的地不正确

农鸿达
2023-03-14

前几天,我开始注意到我的应用程序中有一些东西,它非常不一致。有时会发生,有时不会。

我正在使用导航组件来处理应用程序中的导航,我开始注意到,有时,当通过操作栏后退按钮或设备后退按钮弹出后退时,它返回到一个不再是起始目的地(或至少不应该是)的片段。

在我的例子中,应用程序在MainFragment中启动,经过身份验证后移动到DashboardFragment。这是一种非常常见的情况。

应用程序中的导航非常平坦。大多数时候它只有1级深度,因此几乎所有视图都可以从仪表板访问。

该应用程序在登录视图中启动,就像许多人一样,然后进入仪表板,其中会话将保留为“开始目的地”。为了实现这一点,它在nav_图中使用poputto和popuptinclusive来完成。

<fragment
    android:id="@+id/mainFragment"
    android:name="com.example.view.fragments.MainFragment"
    android:label="Welcome">
    <action
        android:id="@+id/action_mainFragment_to_dashboardFragment"
        app:destination="@id/dashboardFragment"
        app:popUpTo="@id/mainFragment"
        app:popUpToInclusive="true"/>
</fragment>

<fragment
    android:id="@+id/dashboardFragment"
    android:name="com.example.view.fragments.dashboard.DashboardFragment"
    android:label="@string/dashboard_header" >
    <action
        android:id="@+id/action_dashboardFragment_to_notificationsFragment"
        app:destination="@id/notificationsFragment" />
</fragment>

当用户成功进行身份验证并且该转到仪表板时,我使用NavController.navigate()将它们发送到那里。

findNavController().navigate(
    MainFragmentDirections.actionMainFragmentToDashboardFragment()
)

// This should have the same result and it does appear to be affected by the same issue
// findNavController().navigate(R.id.action_mainFragment_to_dashboardFragment)

我有一个带有后箭头和导航抽屉的动作栏。在主活动中,我需要定义AppBarConfiguration并覆盖onSupportNavigateUp()

lateinit var appBarConfiguration: AppBarConfiguration
...
override fun onCreate(savedInstanceState: Bundle?) {
    Timber.d("onCreate()")
    super.onCreate(savedInstanceState)

    _binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)

    // There is 2 different drawer menu's respectfully.
    appBarConfiguration = AppBarConfiguration(
        setOf(
            R.id.mainFragment,
            R.id.dashboardFragment
        ), binding.drawerLayout
    )

    setSupportActionBar(binding.toolbar)
    setupActionBarWithNavController(navController, appBarConfiguration)
}
...
override fun onSupportNavigateUp(): Boolean {
    Timber.d("-> onSupportNavigateUp()")
    val breadcrumb = navController
        .backStack
        .map { it.destination }
        .filterNot { it is NavGraph }
        .joinToString(" > ") { it.displayName.split('/')[1] }

    Timber.d("Backstack: $breadcrumb")
    Timber.d("Previous backstack entry: ${navController.previousBackStackEntry?.destination?.displayName}")

    return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
...

当我们后退一步时,日志是这样的,并且工作正常

D/MainActivity: -> onSupportNavigateUp()
D/MainActivity: Backstack: dashboardFragment > testingFragment
D/MainActivity: Previous backstack entry: com.example:id/dashboardFragment
D/DashboardFragment: -> onCreateView()
D/BaseFragment: -> onCreateView()
D/DashboardFragment: -> onViewCreated()

我还注意到,在操作栏中使用汉堡包时,它也会调用onSupportNavigateUp()

D/MainActivity: -> onSupportNavigateUp()
D/MainActivity: pendingAction: false
D/MainActivity: Backstack: dashboardFragment
D/MainActivity: Previous backstack entry: null

当我使用抽屉导航到一个目的地时,我确实在日志中看到了这一点,我不确定返回的位置/原因,或者它是否有任何重要性

I/NavController: Ignoring popBackStack to destination com.example:id/mainFragment as it was not found on the current back stack

现在,当它不能正常工作时,日志就是这样的

D/MainActivity: -> onSupportNavigateUp()
D/MainActivity: Backstack: mainFragment > testingFragment
D/MainActivity: Previous backstack entry: com.example:id/mainFragment
D/MainFragment: -> onCreateView()
D/BaseFragment: -> onCreateView()
D/MainFragment: -> onViewCreated()

这确实让人感觉在执行从主片段到仪表板片段的导航时,没有应用popUpTo和popUpToInclusive属性(有时)。同样值得怀疑的是,尽管仪表板片段没有被设置为新的起始目标,但它也从后台丢失了。假设没有应用属性,我希望看到面包屑的Backstack:mainFragment

任何帮助都将不胜感激!

共有2个答案

陆信瑞
2023-03-14

正如Ian在第一个链接中所说:

后堆栈始终将应用程序的开始目标放在堆栈底部。

您的起始目的地是导航图中的“家”,即用小房子图标标识的那个。在XML中使用start Destation属性进行设置。当导航库创建一个背包时,它总是将该目的地放在底部。它将永远在那里,即使您尝试使用poUpTo属性避开它。

这就是为什么如果有一个片段是你的“家”片段,比如你的仪表板,那么它需要明确地成为你的开始目标。如果用户退出你的应用程序,这是他们最不愿意看到的。如果您将登录或欢迎屏幕作为开始目标,他们将返回到该屏幕。

这就是为什么您需要将“home”片段设置为开始目的地,然后从那里处理登录或欢迎屏幕的任何额外导航。这就是导航设备的工作原理。如果你试着解决它(我做到了!)你们会遇到其他问题,很多不错的功能,比如自动后退娱乐可能无法正常工作

冀弘济
2023-03-14

虽然肯定有更好的程序可以遵循(如前面评论中的导航原则所述),但更改的开销会引入过多的新错误,并且此时范围太大。

为什么在导航时(甚至使用NavDirections),popUpTo和popUpToInclusive通过XML是不可靠的,这仍然是个未知数。然而,到目前为止,在导航时传递NavOptions似乎解决了这个问题。

findNavController().navigate(
    MainFragmentDirections.actionMainFragmentToDashboardFragment(),
    NavOptions.Builder().setPopUpTo(R.id.mainFragment, true).build()
)

到目前为止,这个问题尚未再次出现。

 类似资料:
  • 我已经开始使用新的导航组件,我真的很喜欢它!不过,我确实有一个问题——当我在图形的起始目的地时,我应该如何处理后退按钮? 这是我现在使用的代码: 当我在图表上的任何地方时,它都工作得很好,它会将我发回,但当我在开始时-应用程序崩溃,因为Backback是空的。 这一切对我来说都有意义,我只是不知道如何处理。 虽然我可以检查当前片段的ID是否与我知道是图的根的ID相同,但我正在寻找一个更优雅的解决方

  • 根据github示例,我正在尝试实现一个带有多个后堆栈的多个。然而,该示例对每个选项卡使用不同的导航图,这使事情变得简单。在我的情况下,我需要对所有选项卡使用相同的导航图,但起始目的地不同于导航图中设置的“起始目的地”。 到目前为止,我已经设法修改了文件,以便为所有选项卡实现单个导航图,并且我得到了多个,它们有自己的后台堆栈,但我不知道如何在不同的目的地启动导航图。 我试过使用,但由于它尚未连接,

  • 使用导航体系结构组件,我们还可以从以下代码中进行动态导航: 然后,当我们需要从一个新的根目的地导航到一个新的根目的地(向后导航将关闭应用程序)时,我们无法知道当前的根目的地是什么,所以我们不知道将popUpTo目的地设置为什么。 如果我们将其设置为不在backstack中的目的地,那么我们将获得警告日志(从中的): “忽略popBackStack to destination*:id/splash

  • 导航组件说明 组件 说明 最低版本 navigator 页面链接 1.0.0 functional-page-navigator 用于跳转插件功能页 不支持 navigator 属性 类型 默认值 必填 说明 最低版本 url string 否 当前小程序内的跳转链接 open-type navigate 否 跳转方式 hover-class string navigator-hover 否 指定

  • 我正在一个新的Android应用程序上使用导航组件,但我不知道怎么做 首先,我有我的主活动,我有main_navigation_graph 主要活动 NavHostFragment main_navigation_graph里面有3个碎片 这里一切都很好。问题是当我到达最后一个片段时,因为在这个片段上,我想根据BottomNavigationView输入(暂时)显示一些子片段(在新的NavHost

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