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

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

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

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

任何帮助都将不胜感激。


当前回答

您可以在导航之前检查请求导航的Fragment是否仍然是当前目的地,从这个要点出发。

它基本上是在片段上设置一个标记,以便稍后进行查找。

/**
 * Returns true if the navigation controller is still pointing at 'this' fragment, or false if it already navigated away.
 */
fun Fragment.mayNavigate(): Boolean {

    val navController = findNavController()
    val destinationIdInNavController = navController.currentDestination?.id
    val destinationIdOfThisFragment = view?.getTag(R.id.tag_navigation_destination_id) ?: destinationIdInNavController

    // check that the navigation graph is still in 'this' fragment, if not then the app already navigated:
    if (destinationIdInNavController == destinationIdOfThisFragment) {
        view?.setTag(R.id.tag_navigation_destination_id, destinationIdOfThisFragment)
        return true
    } else {
        Log.d("FragmentExtensions", "May not navigate: current destination is not the current fragment.")
        return false
    }
}

R.id。Tag_navigation_destination_id只是一个id,必须添加到ids.xml中,以确保它是唯一的。<item name="tag_navigation_destination_id" type="id" />

更多关于bug和解决方案的信息,以及navigateSafe(…)扩展方法在“修复可怕的”…是未知的NavController”

其他回答

我得到了同样的错误,因为我使用了导航抽屉和getSupportFragmentManager(). begintransaction()。替换()在同一时间在我的代码某处。

我通过使用这个条件(测试是否目的地)摆脱了错误:

if (Navigation.findNavController(v).getCurrentDestination().getId() == R.id.your_destination_fragment_id)
Navigation.findNavController(v).navigate(R.id.your_action);

在我的例子中,之前的错误是在我单击导航抽屉选项时触发的。基本上上面的代码确实隐藏了错误,因为在我的代码中,我使用导航使用getSupportFragmentManager(). begintransaction()。replace()条件-

 if (Navigation.findNavController(v).getCurrentDestination().getId() ==
  R.id.your_destination_fragment_id) 

从未到达,因为(Navigation.findNavController(v). getcurrentdestination (). getid()总是指向home片段。你必须只使用navigation . findnavcontroller (v).navigate(R.id.your_action)或nav图形控制器函数来处理你的所有导航操作。

当我按了两次后退键时,我想到了这个问题。首先,我拦截KeyListener并覆盖KeyEvent.KEYCODE_BACK。我在名为OnResume的函数中添加了下面的代码,然后解决了这个问题/问题。

  override fun onResume() {
        super.onResume()
        view?.isFocusableInTouchMode = true
        view?.requestFocus()
        view?.setOnKeyListener { v, keyCode, event ->
            if (event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_BACK) {
                activity!!.finish()
                true
            }
            false
        }
    }

当我第二次遇到这种情况时,它的状态与第一次相同,我发现我可能会使用add函数。让我们来分析一下这些情况。

首先,FragmentA导航到FragmentB,然后FragmentB导航到FragmentA,然后按下返回按钮…崩溃出现了。 其次,FragmentA导航到FragmentB,然后FragmentB导航到FragmentC, FragmentC导航到FragmentA,然后按下返回按钮…崩溃出现了。

所以我认为当按下返回按钮时,FragmentA会回到FragmentB或FragmentC,这会导致登录混乱。最后,我发现名为popBackStack的函数可以用于返回,而不是导航。

  NavHostFragment.findNavController(this@TeacherCloudResourcesFragment).
                        .popBackStack(
                            R.id.teacher_prepare_lesson_main_fragment,false
                        )

到目前为止,问题已经真正解决了。

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

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

只需创建这个扩展:

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

为了避免这种崩溃,我的一个同事写了一个小库,它公开了一个SafeNavController,一个围绕NavController的包装器,并处理由于多个导航命令同时发生崩溃的情况。

这里有一篇关于整个问题和解决方案的短文。

你可以在这里找到图书馆。

正如在其他回答中提到的,此异常通常发生在用户

同时单击处理导航的多个视图 在处理导航的视图上多次单击。

使用计时器来禁用单击并不是处理此问题的合适方法。如果用户在计时器过期后还没有导航到目的地,应用程序无论如何都会崩溃,在许多情况下,导航不是执行的动作,快速点击是必要的。

在情况1中,android:splitMotionEvents=“false”在xml或setMotionEventSplittingEnabled(false)在源文件应该有帮助。将此属性设置为false将只允许一个视图进行单击。你可以在这里阅读

在情况2中,会有一些东西延迟导航过程,允许用户多次单击视图(API调用,动画等)。如果可能的话,应该解决根本问题,以便即时进行导航,不允许用户两次单击视图。如果延迟是不可避免的,就像在API调用的情况下,禁用视图或使其不可点击将是适当的解决方案。