java.lang.IllegalArgumentException:此NavController未知导航目标xxx

时间:2018-06-27 10:42:37

标签: android android-architecture-navigation

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

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

除此特定导航外,其他所有导航都工作正常。

我使用:

findNavContoller()

Fragment的扩展功能可访问navControler。

任何帮助将不胜感激。

40 个答案:

答案 0 :(得分:19)

就我而言,如果用户非常快地两次单击同一视图,则会发生此崩溃。因此,您需要实现某种逻辑以防止多次单击...这很烦人,但这似乎是必要的。

您可以在此处详细了解如何防止这种情况:Android Preventing Double Click On A Button

编辑3/19/2019 :只需进一步说明一下,此崩溃并非只能通过“非常非常快地单击相同的视图两次”来重现。另外,您可以只用两个手指同时单击两个(或多个)视图,每个视图都有它们自己将执行的导航。当您有项目列表时,这尤其操作很容易。上面有关多次点击防护的信息将解决这种情况。

答案 1 :(得分:13)

在调用导航之前检查currentDestination可能会有所帮助。

例如,如果导航图fragmentAfragmentB上有两个片段目标,并且从fragmentAfragmentB仅有一个动作。如果您已经在navigate(R.id.action_fragmentA_to_fragmentB)上,则调用IllegalArgumentException将导致fragmentB。因此,您应该始终在浏览前检查currentDestination

if (navController.currentDestination?.id == R.id.fragmentA) {
    navController.navigate(R.id.action_fragmentA_to_fragmentB)
}

答案 2 :(得分:8)

试试

  1. 创建这个扩展函数(或普通函数):

更新(无反射且更具可读性)

import androidx.fragment.app.Fragment
import androidx.navigation.NavController
import androidx.navigation.NavDirections
import androidx.navigation.fragment.FragmentNavigator

fun Fragment.safeNavigateFromNavController(directions: NavDirections) {
    val navController = findNavController()
    val destination = navController.currentDestination as FragmentNavigator.Destination
    if (javaClass.name == destination.className) {
        navController.navigate(directions)
    }
}

旧(带反射)

import androidx.fragment.app.Fragment
import androidx.navigation.NavController
import androidx.navigation.NavDirections
import androidx.navigation.fragment.FragmentNavigator

inline fun <reified T : Fragment> NavController.safeNavigate(directions: NavDirections) {
    val destination = this.currentDestination as FragmentNavigator.Destination
    if (T::class.java.name == destination.className) {
        navigate(directions)
    }
}
  1. 并在您的 Fragment 中像这样使用:
val direction = FragmentOneDirections.actionFragmentOneToFragmentTwo()
// new usage
safeNavigateFromNavController(direction)

// old usage
// findNavController().safeNavigate<FragmentOne>(action)

我的问题是

我有一个片段 (FragmentOne),它转到另外两个片段(FragmentTwo 和 FragmentThree)。在一些低端设备中,用户按下重定向到 FragmentTwo 的按钮,但在用户按下重定向到 FragmentThree 的按钮后的几毫秒内。结果是:

<块引用>

致命异常:java.lang.IllegalArgumentException 导航 无法找到动作/目的地 action_fragmentOne_to_fragmentTwo 从当前目的地 Destination(fragmentThree) class=FragmentThree

我的解决方案是:

我检查当前目的地是否属于当前片段。如果为 true,则执行导航动作。

仅此而已!

答案 3 :(得分:6)

您可以在导航控制器的当前目标位置检查请求的操作

fun NavController.navigateSafe(
        @IdRes resId: Int,
        args: Bundle? = null,
        navOptions: NavOptions? = null,
        navExtras: Navigator.Extras? = null
) {
    val action = currentDestination?.getAction(resId)
    if (action != null) navigate(resId, args, navOptions, navExtras)
}

答案 4 :(得分:4)

我已经解决了相同的问题,方法是在导航前放置检查,而不是单击立即控制的样板代码

 if (findNavController().currentDestination?.id == R.id.currentFragment) {
        findNavController().navigate(R.id.action_current_next)}
/* Here R.id.currentFragment is the id of current fragment in navigation graph */

根据此答案

https://stackoverflow.com/a/56168225/7055259

答案 5 :(得分:4)

今天

def navigationVersion =“ 2.2.1”

问题仍然存在。我对Kotlin的看法是:

module

答案 6 :(得分:4)

您可以在导航之前检查请求导航的片段是否仍然是https://forge.typo3.org/issues/91303的当前目的地。

基本上,它会在片段上设置一个标记以供以后查找。

/**
 * 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" />

有关错误和解决方案的更多信息,以及taken from this gist中的navigateSafe(...)扩展方法

答案 7 :(得分:3)

为了避免发生崩溃,我的一位同事编写了一个小型库,该库公开了SafeNavControllerNavController周围的包装,并处理了崩溃的情况,这是由于在服务器上有多个导航命令而导致的同时。

以下是有关整个问题和解决方案的简短article

您可以找到库here

答案 8 :(得分:2)

我通过检查当前目的地中是否存在下一个动作来解决此问题

public static void launchFragment(BaseFragment fragment, int action) {
    if (fragment != null && NavHostFragment.findNavController(fragment).getCurrentDestination().getAction(action) != null) {       
        NavHostFragment.findNavController(fragment).navigate(action);
    }
}

public static void launchFragment(BaseFragment fragment, NavDirections directions) {
    if (fragment != null && NavHostFragment.findNavController(fragment).getCurrentDestination().getAction(directions.getActionId()) != null) {       
        NavHostFragment.findNavController(fragment).navigate(directions);
    }
}

如果用户快速单击2个不同按钮,则可以解决此问题

答案 9 :(得分:2)

就我而言,当我将viewpager片段中的一个片段重新用作viewpager的子片段时发生了问题。 在导航xml中添加了viewpager片段(它是父片段),但未在viewpager父片段中添加动作。

nav.xml
//reused fragment
<fragment
    android:id="@+id/navigation_to"
    android:name="com.package.to_Fragment"
    android:label="To Frag"
    tools:layout="@layout/fragment_to" >
    //issue got fixed when i added this action to the viewpager parent also
    <action android:id="@+id/action_to_to_viewall"
        app:destination="@+id/toViewAll"/>
</fragment>
....
// viewpager parent fragment
<fragment
    android:id="@+id/toViewAll"
    android:name="com.package.ViewAllFragment"
    android:label="to_viewall_fragment"
    tools:layout="@layout/fragment_view_all">

通过将操作添加到父viewpager片段中来解决此问题,如下所示:

nav.xml
//reused fragment
<fragment
    android:id="@+id/navigation_to"
    android:name="com.package.to_Fragment"
    android:label="To Frag"
    tools:layout="@layout/fragment_to" >
    //issue got fixed when i added this action to the viewpager parent also
    <action android:id="@+id/action_to_to_viewall"
        app:destination="@+id/toViewAll"/>
</fragment>
....
// viewpager parent fragment
<fragment
    android:id="@+id/toViewAll"
    android:name="com.package.ViewAllFragment"
    android:label="to_viewall_fragment"
    tools:layout="@layout/fragment_view_all"/>
    <action android:id="@+id/action_to_to_viewall"
        app:destination="@+id/toViewAll"/>
</fragment>

答案 10 :(得分:2)

一种荒谬但非常强大的方法是: 只需称呼它:

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

只需创建此扩展名:

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

答案 11 :(得分:2)

我遇到了同样的错误,因为我在代码中的某个地方同时使用了导航抽屉和getSupportFragmentManager().beginTransaction().replace( )

我通过使用这种条件(测试目标位置)摆脱了错误:

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()一直在寻找家的片段。您只能对所有导航操作使用Navigation.findNavController(v).navigate(R.id.your_action)或导航图控制器功能。

答案 12 :(得分:1)

如其他答案所述,通常在用户出现此异常

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

使用计时器禁用点击不是解决此问题的适当方法。如果计时器到期后用户仍未导航到目的地,则该应用仍会崩溃,并且在许多情况下,如果不执行导航操作,则需要快速单击。

在情况1中,xml中的android:splitMotionEvents="false"或源文件中的setMotionEventSplittingEnabled(false)应该会有所帮助。将此属性设置为false将仅允许一个视图进行点击。您可以阅读有关内容here

在第2种情况下,可能会导致导航过程延迟,从而使用户多次单击视图(API调用,动画等)。如果可能,应解决根本问题,以便即时进行导航,不允许用户单击视图两次。如果像在API调用那样不可避免地发生延迟,则禁用视图或使其不可单击将是适当的解决方案。

答案 13 :(得分:1)

似乎您正在清除任务。一个应用程序可能具有一次性设置或一系列登录屏幕。这些有条件的屏幕不应被视为您应用的开始目标。

https://developer.android.com/topic/libraries/architecture/navigation/navigation-conditional

答案 14 :(得分:1)

如果您使用的是recyclerview,则只需在您的点击上添加点击侦听器的冷却时间,然后在您的recyclerview xml文件中使用android:splitMotionEvents="false"

答案 15 :(得分:1)

如果单击速度太快,它将导致null并崩溃。

我们可以使用RxBinding lib对此提供帮助。您可以在点击之前添加节流阀和持续时间。

 RxView.clicks(view).throttleFirst(duration, TimeUnit.MILLISECONDS)
            .subscribe(__ -> {
            });

这些articles关于在Android上进行节流可能会有所帮助。干杯!

答案 16 :(得分:1)

考虑了this twitter thread中Ian Lake的建议后,我想到了以下方法。将NavControllerWrapper定义为:

class NavControllerWrapper constructor(
  private val navController: NavController
) {

  fun navigate(
    @IdRes from: Int,
    @IdRes to: Int
  ) = navigate(
    from = from,
    to = to,
    bundle = null
  )

  fun navigate(
    @IdRes from: Int,
    @IdRes to: Int,
    bundle: Bundle?
  ) = navigate(
    from = from,
    to = to,
    bundle = bundle,
    navOptions = null,
    navigatorExtras = null
  )

  fun navigate(
    @IdRes from: Int,
    @IdRes to: Int,
    bundle: Bundle?,
    navOptions: NavOptions?,
    navigatorExtras: Navigator.Extras?
  ) {
    if (navController.currentDestination?.id == from) {
      navController.navigate(
        to,
        bundle,
        navOptions,
        navigatorExtras
      )
    }
  }

  fun navigate(
    @IdRes from: Int,
    directions: NavDirections
  ) {
    if (navController.currentDestination?.id == from) {
      navController.navigate(directions)
    }
  }

  fun navigateUp() = navController.navigateUp()

  fun popBackStack() = navController.popBackStack()
}

然后输入导航代码:

val navController = navControllerProvider.getNavController()
navController.navigate(from = R.id.main, to = R.id.action_to_detail)

答案 17 :(得分:1)

通常,当我遇到这种情况时,我遇到了查尔斯·马德雷(Charles Madere)描述的问题:在同一用户界面上触发了两个导航事件,一个导航事件更改了currentDestination,另一个由于更改了currentDestination而失败。 如果双击或使用名为findNavController.navigate的单击侦听器单击两个视图,则会发生这种情况。

因此,要解决此问题,您可以使用if-checks,try-catch,或者如果您有兴趣,可以在导航之前找到findSafeNavController()来为您进行检查。它还具有一次检查功能,以确保您不会忘记此问题。

GitHub

Article detailing the issue

答案 18 :(得分:1)

此问题可能有很多原因。 就我而言,我正在使用MVVM模型,并且正在观察布尔值进行导航 当布尔值为true时->导航 否则什么都不做 一切正常,但是这里有一个错误

当从目标片段中按返回按钮时,我遇到了同样的问题。问题是布尔对象,因为我忘记将布尔值更改为false才创建了混乱。我只是在viewModel中创建了一个函数来更改其值为false并在findNavController()之后调用它

答案 19 :(得分:1)

在我的情况下,发生此错误是因为我在启动屏幕后启用了Uri contactData = data.getData(); Cursor c = getActivity().getContentResolver().query(contactData, null, null, null, null); if (c.moveToFirst()) { String name = c.getString(c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); textcontact.setText(name + "\n"); customerName = name; String contactId = c.getString(c.getColumnIndex(ContactsContract.Contacts._ID)); String hasNumber = c.getString(c.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)); String num = ""; if (Integer.valueOf(hasNumber) == 1) { Cursor numbers = getActivity().getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + contactId, null, null); while (numbers.moveToNext()) { num = numbers.getString(numbers.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); textcontact.append(num + "\n"); phoneNumbers.add(num); } } } Single Top选项的导航操作。

答案 20 :(得分:1)

就我而言,我使用自定义的后退按钮进行导航。我致电onBackPressed()代替了以下代码

findNavController(R.id.navigation_host_fragment).navigateUp()

这导致IllegalArgumentException发生。在将其改为使用navigateUp()方法后,我再也没有崩溃。

答案 21 :(得分:1)

当我按两次返回按钮时,这发生在我身上。首先,我拦截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
        }
    }

第二次发生在我身上,并且状态与第一个相同时,我发现我可能使用了adsurd函数。让我们分析一下这些情况。

  1. 首先,FragmentA导航到FragmentB,然后FragmentB导航到FragmentA,然后按返回按钮...发生崩溃。

  2. 其次,FragmentA导航到FragmentB,然后FragmentB导航到FragmentC,FragmentC导航到FragmentA,然后按返回按钮...崩溃发生。

因此,我认为按返回按钮时,FragmentA将返回FragmentB或FragmentC,这会导致登录混乱。最终,我发现名为popBackStack的函数可用于后退而不是导航。

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

到目前为止,问题确实得到解决。

答案 22 :(得分:1)

为防止崩溃,我做了以下事情:

我有一个BaseFragment,在其中添加了fun,以确保destination知道currentDestination

fun navigate(destination: NavDirections) = with(findNavController()) {
    currentDestination?.getAction(destination.actionId)
        ?.let { navigate(destination) }
}

值得一提的是,我正在使用SafeArgs插件。

答案 23 :(得分:0)

TL; DR navigate的呼叫与try-catch打包在一起(以简单的方式),或者确保在短时间内只有navigate个呼叫时间。这个问题可能不会消失。使用大代码段。

你好基于以上几个有用的回答,我想分享我的可以扩展的解决方案。

以下是导致我的应用程序崩溃的代码:

@Override
public void onListItemClicked(ListItem item) {
    Bundle bundle = new Bundle();
    bundle.putParcelable(SomeFragment.LIST_KEY, item);
    Navigation.findNavController(recyclerView).navigate(R.id.action_listFragment_to_listItemInfoFragment, bundle);
}

轻松重现此错误的一种方法是用多指点击项目列表,其中每个项目的单击都会在导航到新屏幕的过程中解析(基本上与人们所指出的相同-在很短的时间内单击两次或多次)一段的时间)。我注意到了:

  1. 第一次navigate调用总是可以正常工作;
  2. navigate方法的第二次和所有其他调用在IllegalArgumentException中解决。

从我的角度来看,这种情况可能经常出现。由于重复代码是一种不好的做法,因此影响一个观点总是一件好事,我想到了下一个解决方案:

public class NavigationHandler {

public static void navigate(View view, @IdRes int destination) {
    navigate(view, destination, /* args */null);
}

/**
 * Performs a navigation to given destination using {@link androidx.navigation.NavController}
 * found via {@param view}. Catches {@link IllegalArgumentException} that may occur due to
 * multiple invocations of {@link androidx.navigation.NavController#navigate} in short period of time.
 * The navigation must work as intended.
 *
 * @param view        the view to search from
 * @param destination destination id
 * @param args        arguments to pass to the destination
 */
public static void navigate(View view, @IdRes int destination, @Nullable Bundle args) {
    try {
        Navigation.findNavController(view).navigate(destination, args);
    } catch (IllegalArgumentException e) {
        Log.e(NavigationHandler.class.getSimpleName(), "Multiple navigation attempts handled.");
    }
}

}

因此,上面的代码仅在一行中更改:

Navigation.findNavController(recyclerView).navigate(R.id.action_listFragment_to_listItemInfoFragment, bundle);

对此:

NavigationHandler.navigate(recyclerView, R.id.action_listFragment_to_listItemInfoFragment, bundle);

它甚至变得更短一些。该代码已在发生崩溃的确切位置进行了测试。不再体验,将对其他导航使用相同的解决方案,以进一步避免相同的错误。

欢迎任何想法!

导致崩溃的确切原因

请记住,在使用方法Navigation.findNavController时,我们使用相同的导航图,导航控制器和后堆栈。

我们总是在这里获得相同的控制器和图形。调用navigate(R.id.my_next_destination)时,图表和后堆栈会几乎立即更改,而UI尚未更新。只是不够快,但是没关系。更改后堆栈后,导航系统会收到第二个navigate(R.id.my_next_destination)调用。由于后堆栈已更改,因此我们现在相对于堆栈中的顶部片段进行操作。最上面的片段是您使用R.id.my_next_destination导航到的片段,但不包含ID为R.id.my_next_destination的下一个其他目标。因此,由于该片段一无所知的ID,您得到IllegalArgumentException

此确切错误可以在NavController.java方法findDestination中找到。

答案 24 :(得分:0)

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

我们可以使用 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)
}

答案 25 :(得分:0)

对于相同的快速单击导航崩溃问题,还有另一种解决方案:

fun NavController.doIfCurrentDestination(@IdRes destination: Int, action: NavController.()-> Unit){
    if(this.currentDestination?.id == destination){action()}
}

然后像这样使用:

findNavController().doIfCurrentDestination(R.id.my_destination){ navigate(...) }

此解决方案的好处是,您可以使用已使用的签名轻松包装对naviagte()的任何现有调用,而无需进行一百万次重载

答案 26 :(得分:0)

我正在调用2.3.1导航,并且在应用程序配置更改时发生相同的错误。通过调试找到问题的原因后,GaphId中的NavHostFragment作为当前通过调用navController.setGraph()设置的ID无效。 GraphId的{​​{1}}仅可从NavHostFragment标签获得。目前,如果您的代码中动态设置了多个<androidx.fragment.app.FragmentContainerView/>,则会出现此问题。恢复接口后,无法在缓存的GraphId中找到目标。您可以通过在切换图形时通过反射在GraphId中手动指定mGraphId的值来解决此问题。

NavHostFragment

答案 27 :(得分:0)

一些类重命名后,我捕获了此异常。例如: 我在导航图中有一个名为FragmentA@+is/fragment_a的类,分别是FragmentB@+id/fragment_b。然后,我删除了FragmentA,并将FragmentB重命名为FragmentA。因此,在FragmentA的那个节点仍然停留在导航图中之后,android:name的节点的FragmentB被重命名了path.to.FragmentA。我有两个节点,它们具有相同的android:name和不同的android:id,并且在删除的类的节点上定义了我需要的操作。

答案 28 :(得分:0)

更新为@AlexNuts answer以支持导航到嵌套图。当操作将嵌套图用作目标时,就像这样:

<action
    android:id="@+id/action_foo"
    android:destination="@id/nested_graph"/>

此操作的目标ID无法与当前目标进行比较,因为当前目标不能为图形。嵌套图的开始目标必须得到解决。

fun NavController.navigateSafe(directions: NavDirections) {
    // Get action by ID. If action doesn't exist on current node, return.
    val action = (currentDestination ?: graph).getAction(directions.actionId) ?: return
    var destId = action.destinationId
    val dest = graph.findNode(destId)
    if (dest is NavGraph) {
        // Action destination is a nested graph, which isn't a real destination.
        // The real destination is the start destination of that graph so resolve it.
        destId = dest.startDestination
    }
    if (currentDestination?.id != destId) {
        navigate(directions)
    }
}

但是,这将阻止两次导航到相同的目的地,有时这是必需的。为此,您可以在action.navOptions?.shouldLaunchSingleTop()上添加支票,并将app:launchSingleTop="true"添加到不需要重复目的地的操作中。

答案 29 :(得分:0)

如果您有可能也会发生 一个带有片段B的ViewPager的片段A 然后您尝试从B导航到C

由于在ViewPager中片段不是A的目的地,因此您的图形不会知道您在B上。

一种解决方案是使用B中的ADirections导航到C

答案 30 :(得分:0)

为防止崩溃,我建议使用safeargs插件,否则,您需要在每次目标ID存在或不存在时进行检查。另一件事是在用于bottomnavigationview的菜单文件中,每个项目ID必须与添加在导航图xml文件中的片段ID相同。

如果使用kotlin,则需要在build.gradle中添加一些依赖项。

android{
compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_1_8.toString()
    }
}

dependencies {
implementation 'androidx.navigation:navigation-fragment-ktx:2.2.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.2.1'
}

对于SafeArgs

dependencies {
classpath  'android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0'
}

要导航到目标,首先,您必须通过导航图xml文件中的操作将它们连接起来。

<fragment
        android:id="@+id/employeesListFragment"
        android:name="com.example.navigationjetpacksample.fragments.EmployeesListFragment"
        android:label="EmployeesListFragment"
        tools:layout="@layout/frag_employees_list">
        <action
            android:id="@+id/action_employeesListFragment_to_addEmployeeFragment"
            app:destination="@id/addEmployeeFragment" />
    </fragment>

构建/清理项目后,

 fab_add.setOnClickListener {   it.findNavController().navigate(EmployeesListFragmentDirections.actionEmployeesListFragmentToAddEmployeeFragment())
 }

有关更多参考:Jetpack Navigation Example

答案 31 :(得分:0)

更新了@Alex Nuts解决方案

如果没有针对特定片段的操作,并且想要导航到片段

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)
}

答案 32 :(得分:0)

这发生在我身上,我的问题是我点击了tab item fragment上的FAB。我试图从选项卡项目片段之一导航到another fragment

但是根据Ian Lake中的this answer,我们必须使用tablayoutviewpager不支持导航组件。因此,从包含片段的tablayout到选项卡项目片段之间没有导航路径。

例如:

containing fragment -> tab layout fragment -> tab item fragment -> another fragment

解决方案是创建一个从包含片段的标签布局到预期片段的路径 例如:路径:container fragment -> another fragment

缺点:

  • 导航图不再能准确表示用户流量。

答案 33 :(得分:0)

在我的情况下,在50%的情况下,尝试从另一个线程进行导航时遇到了该错误。在主线程上运行代码有帮助

requireActivity().runOnUiThread {
    findNavController().navigate(...)
}

答案 34 :(得分:0)

我为Fragment创建了此扩展功能:

fun Fragment.safeNavigate(
    @IdRes actionId: Int,
    @Nullable args: Bundle? = null,
    @Nullable navOptions: NavOptions? = null,
    @Nullable navigatorExtras: Navigator.Extras? = null
) {
    NavHostFragment.findNavController(this).apply {
        if (currentDestination?.label == this@safeNavigate::class.java.simpleName) {
            navigate(actionId, args, navOptions, navigatorExtras)
        }
    }
}

答案 35 :(得分:0)

在我的情况下,这是由于我在动作目标中不小心添加了+而发生的,而崩溃仅是在我多次访问同一片段时发生的。

 <action
        android:id="@+id/action_to_profileFragment"
        app:destination="@+id/profileFragment" />

解决方案是从操作目标中删除+,仅使用@id/profileFragment而不是@+id/profileFragment

 <action
        android:id="@+id/action_to_profileFragment"
        app:destination="@id/profileFragment" />

答案 36 :(得分:0)

我写了这个扩展名

fun Fragment.navigateAction(action: NavDirections) {
    val navController = this.findNavController()
    if (navController.currentDestination?.getAction(action.actionId) == null) {
        return
    } else {
        navController.navigate(action)
    }
}

答案 37 :(得分:0)

就我而言,我有多个导航图文件,并且试图从1个导航图位置移动到另一个导航图的目标位置。

为此,我们必须像这样在第一个导航图中包含第二个导航图

<include app:graph="@navigation/included_graph" />

并将其添加到您的操作中:

<action
        android:id="@+id/action_fragment_to_second_graph"
        app:destination="@id/second_graph" />

其中second_graph是:

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/second_graph"
    app:startDestination="@id/includedStart">

在第二张图中。

更多信息here

答案 38 :(得分:0)

混合使用Backstack的fragmentManager控件和Backstack的Navigation Architecture控件似乎也可能导致此问题。

例如,原始的CameraX基本示例使用fragmentManager后向导航,如下所示,并且看起来好像与导航没有正确交互:

Vue.component('job', {
        props: ['job'],
        template: ` 
            <p v-if="job.hasOwnProperty('phases') && job.phases.length > 0">A</p>
            <p v-else>B</p>
        `
    })

如果在从主片段(在本例中为相机片段)移动之前,使用此版本记录了“当前目标”,然后在返回主片段时再次对其进行记录,则可以从日志中的ID中看到该ID不相同。猜测是,导航移至片段时对其进行了更新,而fragmntManager随后移回该片段时并未对其进行更新。从日志中:

  

之前:D / CameraXBasic:currentDest?:androidx.navigation.fragment.FragmentNavigator$Destination@b713195

     

之后:D / CameraXBasic:currentDest?:androidx.navigation.fragment.FragmentNavigator$Destination@9807d8f

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

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

这可以正常工作,并且回到主片段时,日志显示相同的ID。

  

之前:D / CameraXBasic:currentDest?:androidx.navigation.fragment.FragmentNavigator$Destination@b713195

     

之后:D / CameraXBasic:currentDest?:androidx.navigation.fragment.FragmentNavigator$Destination@b713195

我怀疑这个故事的寓意,至少在现在是,要非常谨慎地将Navigation与fragmentManager导航混合使用。

答案 39 :(得分:-1)

由于您可能已将目标屏幕分配给错误的图形,所以可能发生了此错误