IllegalArgumentException:ナビゲーション先xxxがこのNavControllerに認識されていません


138

あるフラグメントから別のフラグメントに移動しようとすると、新しいAndroid Navigation Architectureコンポーネントに問題が発生し、次の奇妙なエラーが発生します。

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

これ以外のすべてのナビゲーションは正常に機能します。

findNavController()はFragmentの関数を使用してにアクセスしますNavController

どんな助けでもありがたいです。


よりよく理解するためにいくつかのコードを提供してください。
Alex

12
これは私にも起こっています。
EuryPérezBeltré18年

これまでのところ、ライブラリの新しいリリースではこのバグの発生率は低下していますが、ライブラリはまだ十分に文書化されていないと思います。
Jerry

回答:


75

私の場合、ユーザーが同じビューを非常に速く2回クリックすると、このクラッシュが発生します。したがって、複数のクイッククリックを防ぐために、ある種のロジックを実装する必要があります...これは非常に面倒ですが、必要なようです。

これの防止について詳しくは、こちらをご覧ください。Androidボタンのダブルクリックを防止するAndroid

編集3/19/2019:もう少し明確にするために、このクラッシュは「同じビューを非常に速く2回クリックする」だけで再現できるわけではありません。または、2本の指を使用して2つ(またはそれ以上)のビューを同時にクリックすることもできます。各ビューには、実行する独自のナビゲーションがあります。これは、アイテムのリストがある場合に特に簡単です。マルチクリック防止に関する上記の情報は、このケースを処理します。

編集2020年4月16日:上記の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つのビューを同時にクリックすることに関する編集!それが私の鍵であり、問​​題を簡単に再現するのに役立ちました。その情報で素晴らしい更新。
Richard Le Mesurier

デバッグフェーズ中に、アプリが実行を続行するために待機している間に偶然クリックしてしまいました。IDEに続けて2回クリックする別のケースのようです
Marco

1
これをありがとう。いくつかのクラッシュといくつかの頭のスクラッチを保存しました:)
user2672052

58

currentDestinationナビゲートを呼び出す前に確認すると役立つ場合があります。

たとえば、ナビゲーショングラフfragmentAとに2つのフラグメント宛先がありfragmentB、からfragmentAまでのアクションが1つしかない場合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を除いて、同じことをしてしまいました。ちょっと汚い感じで、やらなくていい気がします。
Shawn Maybush

84
ライブラリはこのチェックを強制するべきではありません、それは確かにばかげています。
DaniloDeQueiroz

同じ問題がありました。EditTextと、データベースにEditTextのコンテンツを保存するための「保存」ボタンがありました。「保存」ボタンを押すと常にクラッシュしました。その理由は、「保存」ボタンを押すことができるようにするために、「戻る」ボタンをタップして画面上のキーボードを取り除く必要があるという事実に関係しているのではないかと思います。
フォックス

これはエラー状態をチェックしますが、問題を解決しません。興味深いことに、不要な理由でナビゲーションバックスタックが空になった場合、この条件は当てはまります。
Mike76

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、メモありがとうございます。グローバルアクションを使用してコードを更新しました
Alex Nuts

@AlexNuts素晴らしい答え。私はあなたが削除できると思います?: graph.getAction(resId)-> currentDestination?.getAction(resId)グローバルまたは非グローバルの両方のアクションのアクションを返します(私はテストしました)むしろ渡し> -あなたは安全なのArgsを利用した場合にも、良いだろうnavDirections: NavDirections未満resIdargs別に。
ウェス

@AlexNutsこのソリューションは、現在の宛先と同じ宛先へのナビゲートをサポートしていないことに注意してください。バンドルYを使用して宛先XからバンドルZを使用して宛先Xに移動することはできません。
ウェス

18

また、フラグメントBのViewPagerを持つフラグメントAがあり、BからCに移動しようとした場合にも発生する可能性があります。

ViewPagerではフラグメントはAの宛先ではないため、グラフはあなたがBにいることを認識しません。

ソリューションは、BのADirectionsを使用してCに移動することです。


この場合、クラッシュは毎回発生するわけではなく、まれにしか発生しません。それを解決するには?
Srikar Reddy

navGraph内にグローバルアクションを追加し、それを使用してナビゲートできます
Abraham Mathew

1
Bはその正確な親を意識する必要はありませんとして、それは次のようにインターフェースを介して、より良い使用ADirectionsになり(parentFragment as? XActionListener)?.Xaction()、それが役に立つ場合は、ローカル変数としてこの機能を保持できる注意
HMAC

私は同じ問題が持っているとして、あなたはこれを説明するためのサンプルコードを共有してくださいすることができます
Ikhiloya Imokhaiを

誰でもサンプルコードをplzできますが、同じ問題に悩まされています。フラグメントとタブフラグメントを用意する
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ナビゲーションアーキテクチャコンポーネントで遭遇したことの多くは少しおかしく感じられ、IMOが厳格に設定されています。頭痛の種が多すぎるため、プロジェクトに独自の実装を行うことを考えている
Neil

私にはうまくいきません...それでも同じエラーが発生します。
Otziii

5

TL; DRnavigate通話をtry-catch(簡単な方法で)ラップするかnavigate、短期間にの通話が1つだけになるようにします。この問題はおそらく解消されません。より大きなコードスニペットをアプリにコピーして試してください。

こんにちは。上記のいくつかの有用な回答に基づいて、拡張可能な私のソリューションを共有したいと思います。

これが私のアプリケーションでこのクラッシュを引き起こしたコードです:

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

バグを簡単に再現する方法は、アイテムのリストを複数の指でタップして、各アイテムのクリックが新しい画面へのナビゲーションで解決されるようにすることです(基本的に、メモした人と同じ-非常に短時間で2回以上のクリック) )。きがついた:

  1. 最初のnavigate呼び出しは常に正常に機能します。
  2. 2番目以降のすべてのnavigateメソッドの呼び出しは、で解決されIllegalArgumentExceptionます。

私の観点からは、この状況は非常に頻繁に発生する可能性があります。コードの繰り返しは悪い習慣であり、次の解決策について私が考えた1つの影響点を持つことは常に良いことなので、

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.");
    }
}

}

したがって、上記のコードはこれから1行だけ変更されます。

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がまだ更新されていない間。十分な速度ではありませんが、それで問題ありません。バックスタックが変更された後、ナビゲーションシステムは2番目のnavigate(R.id.my_next_destination)呼び出しを受信します。バックスタックが変更されたので、スタックの一番上のフラグメントに対して動作するようになりました。一番上のフラグメントは、を使用して移動するフラグメントですが、R.id.my_next_destination次のIDの宛先は含まれていませんR.id.my_next_destination。したがってIllegalArgumentException、フラグメントが何も知らないため、ID が取得されます。

この正確なエラーはNavController.javamethodにありfindDestinationます。


4

私の場合、問題はフラグメント内のviewpagerフラグメントの1つをの子として再利用したときに発生しましたviewpagerviewpager(親断片であった)断片は、ナビゲーション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

この要点から取得した、ナビゲーションを要求するフラグメントがまだ現在の宛先であるかどうかをナビゲートする前に確認できます。

基本的には、後で検索するためにフラグメントにタグを設定します。

/**
 * 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 一意であることを確認するために、ids.xmlに追加する必要がある単なるidです。 <item name="tag_navigation_destination_id" type="id" />

バグと解決策、および詳細navigateSafe(...)における拡張子法「の修正は恐ろしい『...』このからNavControllerに知られていません


私はこの問題のいくつかの異なる解決策を研究しましたが、あなたの解決策は間違いなく最も優れています。それに対する愛情があまりないのを見て私は悲しくなります
ルーク

1
NAV_DESTINATION_IDこのようなものの代わりに一意の識別子を作成すると役立つ場合があります。stackoverflow.com/ a / 15021758/1572848
ウィリアムリード

はい、私は答えを更新しました
フランク

タグはどこから来て、なぜそれが必要なのですか?ナビゲーションコンポーネントの実際のIDがのIDと一致しない問題がありR.idます。
riezebosch

R.id.tag_navigation_destination_id一意であることを確認するために、ids.xmlに追加する必要がある単なるidです。<item name="tag_navigation_destination_id" type="id" />
フランク

3

私の場合、複数のナビゲーショングラフファイルがあり、1つのナビゲーショングラフの場所から別のナビゲーショングラフの移動先に移動しようとしていました。

これのために、私たちはこのような最初のものに2番目のナビゲーショングラフを含める必要があります

<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番目のグラフ。

詳細はこちら


2

私の場合、スプラッシュスクリーンの後にSingle TopClear Taskオプションを有効にしたナビゲーションアクションがあったため、バグが発生しました。


1
ただし、clearTaskは非推奨です。代わりにpopUpTo()を使用してください。
Jerry

@ Po10cioこれらのフラグはどれも必要ありませんでした。削除しただけで修正されました。
EuryPérezBeltré2018

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して名前FragmentBをに変更しましたFragmentA。だからのノードの後にFragmentA、まだナビゲーショングラフに滞在し、android:nameFragmentBのノード改名されましたpath.to.FragmentA。同じandroid:nameと異なる2つのノードがありandroid:id、削除したクラスのノードで必要なアクションが定義されていました。


1

戻るボタンを2回押すと発生します。最初は、傍受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

バックスタックのfragmentManagerコントロールとバックスタックのナビゲーションアーキテクチャコントロールを混在させると、この問題も発生するようです。

たとえば、元の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か所で「戻るナビゲーション」を実際に見逃していたので、最近また修正しました。
ミック

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の場合にナビゲーション用のブール値を観察していました->ナビゲートし、それ以外は何もせず、これは正常に機能していましたが、ここで1つの間違いがありました

宛先フラグメントから戻るボタンを押すと、同じ問題が発生しました。ブール値をfalseに変更するのを忘れたため、ブールオブジェクトが問題になり、messが作成されました。値をfalseに変更する関数をviewModelに作成しただけで、 findNavController()の直後にそれを呼び出しました


1

通常、これが発生すると、Charles Madereによって説明された問題が発生しました。同じUIで2つのナビゲーションイベントがトリガーされ、1つはcurrentDestinationを変更し、もう1つはcurrentDestinationが変更されたために失敗しました。これは、ダブルタップするか、findNavController.navigateを呼び出すクリックリスナーで2つのビューをクリックすると発生する可能性があります。

したがって、これを解決するには、if-checks、try-catchを使用するか、興味がある場合は、移動する前にこれを確認するfindSafeNavController()を使用します。また、この問題を忘れないようにするための糸くずチェックも付いています。

GitHub

問題を詳述した記事


1

クリックが速すぎると、nullとクラッシュが発生します。

これを支援するためにRxBinding libを使用できます。それが発生する前に、クリックでスロットルと期間を追加できます。

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

Androidでのスロットルに関するこれらの記事が役立つ場合があります。乾杯!


1

recyclerviewを使用している場合は、クリックにクリックリスナークールダウンを追加し、recyclerview xmlファイルにも追加します。 android:splitMotionEvents="false"


1
私の下の答えを見てください
クレイジー

1

このツイッタースレッドでイアンレイクのアドバイスを検討した後、私は次のアプローチを考え出しました。た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。タブアイテムフラグメントの1つからanother fragment

しかし、この回答のIan Lakeによれば、およびを使用する必要があり、ナビゲーションコンポーネントのサポートはありませんtablayoutviewpager。このため、フラグメントを含むtablayoutからタブ項目フラグメントへのナビゲーションパスはありません。

例:

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

解決策は、フラグメントを含むタブレイアウトから目的のフラグメントex:パスへのパスを作成することでした。 container fragment -> another fragment

不利益:

  • ナビゲーショングラフは、ユーザーフローを正確に表しなくなりました。

0

私の場合、別のスレッドからナビゲートしようとしたときにそのエラーが発生しました(50%の場合)。メインスレッドでコードを実行すると役立ちます

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

私はこれにもっと投票したいのですが、もっともらしいようですが、確認できません。
Jerry

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

私はフラグメントのためにこの拡張関数を作成しました:

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)
        }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.