当我试图从一个片段导航到另一个片段时,我遇到了新的Android导航架构组件的问题,我得到了这个奇怪的错误:

java.lang.IllegalArgumentException: navigation destination XXX
is unknown to this NavController

其他导航都很好,除了这个。

我使用Fragment的findNavController()函数来访问NavController。

任何帮助都将不胜感激。


当前回答

在我的情况下,我通过验证所有导航操作都正确地管理到各自的图形中,并更新了设备后退按钮的代码来解决这个问题,如下所示:

 onBackPressedDispatcher.addCallback(this /* lifecycle owner */, object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            // Back is pressed... Finishing the activity
            if (navHostFragment.childFragmentManager.backStackEntryCount == 0) {
                // First fragment is open, backstack is empty
               finish()
            } else {
                navHostFragment.navController.popBackStack()
            }
        }
    })

其他回答

一个荒谬但非常强大的方法是: 简单地称之为:

view?.findNavController()?.navigateSafe(action)

只需创建这个扩展:

fun NavController.navigateSafe(
    navDirections: NavDirections? = null
) {
    try {
        navDirections?.let {
            this.navigate(navDirections)
        }
    }
    catch (e:Exception)
    {
        e.printStackTrace()
    }
}

更新的@Alex坚果解决方案

如果没有特定片段的操作,并希望导航到片段

fun NavController.navigateSafe(
@IdRes actionId: Int, @IdRes fragmentId: Int, args: Bundle? = null,
navOptions: NavOptions? = null, navExtras: Navigator.Extras? = null) 
{
  if (actionId != 0) {
      val action = currentDestination?.getAction(actionId) ?: graph.getAction(actionId)
      if (action != null && currentDestination?.id != action.destinationId) {
          navigate(actionId, args, navOptions, navExtras)
    }
    } else if (fragmentId != 0 && fragmentId != currentDestination?.id)
        navigate(fragmentId, args, navOptions, navExtras)
}

似乎混合fragmentManager控件的backstack和Navigation Architecture控件的backstack也会导致这个问题。

例如,最初的CameraX基本示例使用fragmentManager后台导航,如下所示,它似乎没有正确地与导航交互:

// Handle back button press
        view.findViewById<ImageButton>(R.id.back_button).setOnClickListener {
            fragmentManager?.popBackStack()
        }

如果你在从主片段(在本例中是相机片段)移动之前记录这个版本的“当前目的地”,然后当你返回主片段时再次记录它,你可以从日志中的id中看到id是不相同的。据猜测,导航在移动到片段时更新了它,而fragmntManager在移动回来时没有再次更新它。日志显示:

修改前:D/CameraXBasic: currentDest?: androidx.navigation.fragment.FragmentNavigator Destination@b713195美元 After: D/CameraXBasic: currentDest?: androidx.navigation.fragment.FragmentNavigator Destination@9807d8f美元

更新版的CameraX基本示例使用导航返回如下:

 // Handle back button press
        view.findViewById<ImageButton>(R.id.back_button).setOnClickListener {
            Navigation.findNavController(requireActivity(), R.id.fragment_container).navigateUp()
        }

这可以正常工作,当返回到主片段时,日志显示相同的id。

修改前:D/CameraXBasic: currentDest?: androidx.navigation.fragment.FragmentNavigator Destination@b713195美元 After: D/CameraXBasic: currentDest?: androidx.navigation.fragment.FragmentNavigator Destination@b713195美元

我怀疑这个故事的寓意,至少在这个时候,是要非常小心地混合导航和fragmentManager导航。

将我的答案优雅地扔到处理两种情况(双击,同时点击两个按钮)的环中,但尽量不掩盖真正的错误。

我们可以使用navigateSafe()函数来检查我们试图导航到的目的地从当前目的地是否是无效的,但从前一个目的地是否是有效的。如果是这种情况,代码假设用户双击或同时点击两个按钮。

然而,这个解决方案并不完美,因为它可能会掩盖一些小众情况下的实际问题,即我们试图导航到恰好是父端的目的地。但据推测,这种情况不太可能发生。

代码:

fun NavController.navigateSafe(directions: NavDirections) {
    val navigateWillError = currentDestination?.getAction(directions.actionId) == null

    if (navigateWillError) {
        if (previousBackStackEntry?.destination?.getAction(directions.actionId) != null) {
            // This is probably some user tapping two different buttons or one button twice quickly
            // Ignore...
            return
        }

        // This seems like a programming error. Proceed and let navigate throw.
    }

    navigate(directions)
}

如果你有的话,这种情况也会发生 一个片段a和片段B的ViewPager 你试着从B导航到C

因为在ViewPager中片段不是a的目的地,所以你的图形不会知道你在B上。

一种解决方案是在B中使用A方向导航到C