IllegalArgumentException:此NavController未知导航目标xxx


137

当我尝试从一个Fragment导航到另一个Fragment时,新的Android Navigation Architecture组件出现问题,出现这个奇怪的错误:

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

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

我使用findNavController()Fragment的功能来访问NavController

任何帮助将不胜感激。


请提供一些代码以更好地理解。
Alex

12
我也正在发生这种情况。
埃里·佩雷斯Beltré

到目前为止,此漏洞的发生率已随着库的较新版本而降低,但我认为该库的文献资料尚不充分。
杰里·奥卡福

Answers:


75

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

您可以在此处阅读更多有关防止这种情况的信息:Android防止双击按钮

编辑3/19/2019:只是为了进一步说明一下,仅通过“非常非常快地单击同一视图两次”,无法完全重现此崩溃。或者,您可以只用两个手指同时单击两个(或多个)视图,每个视图都有它们自己将执行的导航。当您有项目列表时,这特别容易做到。上面有关多次点击防护的信息将处理这种情况。

编辑4/16/2020:以防万一,您对阅读上面的Stack Overflow帖子并不太感兴趣,我包括了我很久以来一直使用的自己的(Kotlin)解决方案。

OnSingleClickListener.kt

class OnSingleClickListener : View.OnClickListener {

    private val onClickListener: View.OnClickListener

    constructor(listener: View.OnClickListener) {
        onClickListener = listener
    }

    constructor(listener: (View) -> Unit) {
        onClickListener = View.OnClickListener { listener.invoke(it) }
    }

    override fun onClick(v: View) {
        val currentTimeMillis = System.currentTimeMillis()

        if (currentTimeMillis >= previousClickTimeMillis + DELAY_MILLIS) {
            previousClickTimeMillis = currentTimeMillis
            onClickListener.onClick(v)
        }
    }

    companion object {
        // Tweak this value as you see fit. In my personal testing this
        // seems to be good, but you may want to try on some different
        // devices and make sure you can't produce any crashes.
        private const val DELAY_MILLIS = 200L

        private var previousClickTimeMillis = 0L
    }

}

ViewExt.kt

fun View.setOnSingleClickListener(l: View.OnClickListener) {
    setOnClickListener(OnSingleClickListener(l))
}

fun View.setOnSingleClickListener(l: (View) -> Unit) {
    setOnClickListener(OnSingleClickListener(l))
}

HomeFragment.kt

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    settingsButton.setOnSingleClickListener {
        // navigation call here
    }
}

23
有关使用2个手指并同时单击2个视图的编辑!这是我的关键,并帮助我轻松复制了该问题。大量更新信息。
理查德·勒·马修里尔

在调试阶段,我碰巧在应用程序卡住的情况下单击以等待继续执行。好像是连续两次单击IDE的另一种情况
Marco

1
谢谢你 为我节省了一些崩溃和一些头部抓挠的问题:)
user2672052

58

currentDestination致电前检查可能会有所帮助。

例如,如果您在导航图fragmentA和上有两个片段目标fragmentB,并且从fragmentA到仅执行一个操作fragmentB。通话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)
}

3
我有一个搜索应用程序,可通过带有参数的操作进行导航。因此,它可以从currentDestination导航到自身。我最终做了同样的事情,除了navController.currentDestination == navController.graph.node。不过感觉有点脏,我觉得我不必这样做。
肖恩·梅布什

84
图书馆不应该强迫我们进行检查,这确实是荒谬的。
DaniloDeQueiroz

我遇到过同样的问题。我有一个EditText和一个“保存”按钮,用于将EditText的内容存储在数据库中。它总是在按下“保存”按钮时崩溃。我怀疑原因与以下事实有关:为了能够按下“保存”按钮,我需要通过点击“后退”按钮来摆脱屏幕键盘。
狐狸

这将检查错误情况,但不能解决问题。有趣的是,如果由于不需要的原因导航后退栈为空,则此条件成立。
Mike76 '19

1
即使在iOS中,尽管有时您多次按下按钮时会同时按下多个ViewController。猜猜Android和iOS都有此问题。
coolcool1994

47

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

UPDATE 添加了用于安全导航的全局操作的用法。

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

1
该解决方案不适用于在currentDestination动作列表之外定义的任何动作。假设您定义了一个全局操作,然后使用该操作进行导航。这将失败,因为未在currentDestination的<action>列表中定义该动作。添加类似的支票currentDestination?.getAction(resId) != null || currentDestination?.id != resId应该可以解决它,但可能无法涵盖所有​​情况。
wchristiansen

@wchristiansen,谢谢您的笔记。我已经更新了代码全球行动的用法
亚历坚果

@AlexNuts很好的答案。我认为您可以删除?: graph.getAction(resId)-> currentDestination?.getAction(resId)将为全局或非全局操作返回一个操作(我已经对其进行了测试)。另外,如果您使用Safe Args->而navDirections: NavDirections不是分别传入resId和传递,会更好args
Wess

@AlexNuts请注意,此解决方案不支持导航到与当前目的地相同的目的地。无法从捆绑软件Y的目标X导航到捆绑软件Z的目标X。
Wess

18

如果您的片段A的ViewPager的片段为B,并且尝试从B导航到C,也可能发生这种情况

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

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


在这种情况下,崩溃不会每次都发生,而只会很少发生。怎么解决呢?
斯里卡·雷迪

您可以在navGraph内添加一个全局操作,并使用它进行导航
Abraham Mathew

1
由于B不需要知道其确切的父对象,因此最好通过类似这样的接口使用ADirections (parentFragment as? XActionListener)?.Xaction()并注意,如果有帮助,可以将此函数作为局部变量保存
hmac

您能否分享一个示例代码来说明这一点,因为我有同样的问题
Ikhiloya Imokhai

任何人都可以使用示例代码,我陷入了同样的问题。先有片段然后有片段
Usman Zafer

13

为防止崩溃,我做了以下工作:

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

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

值得注意的是,我正在使用SafeArgs插件。


12

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

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

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


我不知道onBackPressed和this之间的区别是什么,仍然停留在系统后退按钮上并覆盖它,并用这个替换似乎很疯狂
Daniel Wilson

2
我同意这确实很疯狂。我在android导航体系结构组件中遇到的许多事情都感到有些疯狂,因为它设置得过于严格。考虑为项目做我自己的实现,因为这会造成很多麻烦
Neil

对我不起作用...仍然出现相同的错误。
Otziii

5

TL; DRnavigatetry-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);

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

任何想法都欢迎!

究竟是什么导致撞车

请记住,这里使用method时,我们使用相同的导航图,导航控制器和后堆栈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。这样,您就可以得到IllegalArgumentException该片段一无所知的ID。

这个确切的错误可以在NavController.javamethod中找到findDestination


4

就我而言,当我将片段中的一个viewpager片段重新用作的子代时,便出现了问题viewpager。的viewpager片段(这是母体片段)在导航XML中加入,但是在不添加动作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>

4

今天

def navigationVersion =“ 2.2.1”

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

// To avoid "java.lang.IllegalArgumentException: navigation destination is unknown to this NavController", se more https://stackoverflow.com/q/51060762/6352712
fun NavController.navigateSafe(
    @IdRes destinationId: Int,
    navDirection: NavDirections,
    callBeforeNavigate: () -> Unit
) {
    if (currentDestination?.id == destinationId) {
        callBeforeNavigate()
        navigate(navDirection)
    }
}

fun NavController.navigateSafe(@IdRes destinationId: Int, navDirection: NavDirections) {
    if (currentDestination?.id == destinationId) {
        navigate(navDirection)
    }
}

4

您可以在导航之前检查请求导航的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(...)的extention方法“修复可怕‘......是未知的,这NavController’


我研究了一些解决此问题的方法,您的方法绝对是最好的方法。让我难过,看到这么小爱它
卢克

1
创建一个唯一的标识符来代替NAV_DESTINATION_ID诸如stackoverflow.com/a/15021758/1572848之
William Reed

是的,我更新了答案
Frank

标签从何而来,为什么需要它?我遇到导航组件上的实际ID与匹配的问题R.id
riezebosch

R.id.tag_navigation_destination_id只是一个ID,您必须将其添加到ids.xml中以确保其唯一性。<item name="tag_navigation_destination_id" type="id" />
Frank

3

就我而言,我有多个导航图文件,并且试图从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">

在第二张图中。

更多信息在这里



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)对所有导航操作使用或导航图形控制器功能。



1

在对类进行一些重命名后,我捕获了此异常。例如:我有类调用FragmentA@+is/fragment_a导航图和FragmentB使用@+id/fragment_b。然后,我删除并重FragmentA命名FragmentBFragmentA。该节点后,所以FragmentA仍然留在导航图中,和android:nameFragmentB的节点改名path.to.FragmentA。我有两个相同android:name且不同的节点android:id,并且我需要的操作在已删除类的节点上定义。


1

当我两次按返回按钮时,这种情况发生在我身上。首先,我拦截KeyListener并覆盖KeyEvent.KEYCODE_BACK。我将以下代码添加到以OnResumeFragment 命名的函数中,然后解决了这个问题。

  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
                        )

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


1

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

例如,原始的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

之后: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

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

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


这听起来很合理,我将进一步调查。有谁能够核实或证实这一说法?
杰里·奥卡福

@JerryOkafor-我已经在基于CameraX Sample的应用程序中对其进行了测试,并对其进行了验证,但是很高兴看到其他人是否也看到了它。我实际上错过了同一应用程序中某个位置的“后退导航”,因此最近也刚刚对其进行了修复。
米克

1

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

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

只需创建此扩展名:

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

1

此问题可能有很多原因。在我的情况下,我使用的是MVVM模型,并且当布尔值为true时,我正在观察一个布尔值进行导航->导航否则不执行任何操作,而且工作正常,但是这里有一个错误

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


1

通常当我遇到这种情况时,我遇到了Charles Madere所描述的问题:在同一ui上触发了两个导航事件,一个更改了currentDestination,另一个由于更改了currentDestination而失败。如果您双击,或者使用调用findNavController.navigate的单击侦听器单击两个视图,则会发生这种情况。

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

的GitHub

详细说明问题的文章


1

在考虑了Twitter线程中 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)

1

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

 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


0

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

但是根据Ian Lake回答,我们必须使用tablayout并且viewpager没有导航组件支持。因此,没有从包含片段的选项卡布局到选项卡项目片段的导航路径。

例如:

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

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

坏处:

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

0

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

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

我希望看到更多对此的投票,这听起来似乎合理,但我无法验证。
杰里·奥卡福

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

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

0

我写了这个扩展

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

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

0

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

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

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

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


0

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


1
看看下面矿山的答案
疯狂的
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.