IllegalArgumentException: नेविगेशन गंतव्य xxx इस NavController के लिए अज्ञात है


139

जब मैं एक फ़्रैगमेंट से दूसरे में नेविगेट करने का प्रयास करता हूं, तो मुझे नए एंड्रॉइड नेविगेशन आर्किटेक्चर घटक के साथ समस्या हो रही है , मुझे यह अजीब त्रुटि मिलती है:

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

इस विशेष को छोड़कर हर दूसरा नेविगेशन ठीक काम करता है।

मैं उपयोग findNavController()करने के लिए Fragment के फ़ंक्शन का उपयोग करता हूं NavController

किसी भी तरह की सहायता को आभार समझेंगे।


कृपया बेहतर समझ के लिए कुछ कोड प्रदान करें।
एलेक्स

12
यह मेरे साथ भी हो रहा है।
यूरी पेरेज़ बेल्ट्रै

अब तक लाइब्रेरी के नए रिलीज के साथ इस बग की घटना की दर कम हो गई है, लेकिन मुझे लगता है कि लाइब्रेरी अभी अच्छी तरह से प्रलेखित नहीं है।
जेरी ओकॉर्फ

जवाबों:


76

मेरे मामले में, यदि उपयोगकर्ता एक ही दृश्य को बहुत जल्दी दो बार क्लिक करता है, तो यह दुर्घटना घटित होगी। इसलिए आपको कई त्वरित क्लिकों को रोकने के लिए किसी प्रकार के तर्क को लागू करने की आवश्यकता है ... जो बहुत कष्टप्रद है, लेकिन यह आवश्यक प्रतीत होता है।

आप इसे रोकने के बारे में और अधिक यहाँ पढ़ सकते हैं: Android Preventing Double Click On A Button

3/19/2019 को संपादित करें : बस थोड़ा और स्पष्ट करने के लिए, यह दुर्घटना केवल "बहुत ही जल्दी दो बार एक ही दृश्य पर क्लिक" करके विशेष रूप से प्रतिलिपि प्रस्तुत करने योग्य नहीं है। वैकल्पिक रूप से, आप केवल दो उंगलियों का उपयोग कर सकते हैं और एक ही समय में दो (या अधिक) दृश्यों पर क्लिक कर सकते हैं, जहां प्रत्येक दृश्य का अपना नेविगेशन है जिसे वे प्रदर्शन करेंगे। यह वह जगह है विशेष रूप से आप मदों की एक सूची है, जब ऐसा करने के लिए आसान। एकाधिक क्लिक रोकथाम पर उपरोक्त जानकारी इस मामले को संभाल लेगी।

4/16/2020 को संपादित करें : ठीक उसी स्थिति में जब आप उस स्टैक ओवरफ्लो पोस्ट के माध्यम से पढ़ने में बहुत रुचि नहीं रखते हैं, मैं अपने स्वयं के (कोटलिन) समाधान को शामिल कर रहा हूं जिसका उपयोग मैं लंबे समय से कर रहा हूं।

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
इसके लिए धन्यवाद। मुझे कुछ दुर्घटनाओं और कुछ सिर खरोंच से
बचा लिया

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 19

मेरी भी यही समस्या थी। मेरे पास एक EditText और डेटाबेस में EditText की सामग्री को संग्रहीत करने के लिए एक 'save' बटन था। यह हमेशा 'सेव' बटन दबाने पर क्रैश हो जाता है। मुझे संदेह है कि इसका कारण इस तथ्य से है कि 'सेव' बटन को दबाने में सक्षम होने के लिए, मुझे बैक बटन पर टैप करके ऑन-स्क्रीन कीबोर्ड से छुटकारा पाना होगा।
फॉक्स

यह एक त्रुटि स्थिति की जाँच करता है, लेकिन यह समस्या को हल नहीं करता है। दिलचस्प बात यह है कि यदि अवांछित कारणों से नेविगेशन बैकस्टैक खाली हो जाता है तो यह स्थिति सही है।
16:76 पर माइक76

1
आईओएस में भले ही कभी-कभी मल्टीपल व्यूकॉलर आपको बटन दबाने पर कई बार पुश हो जाता है। अनुमान है कि 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?.getAction(resId) != null || currentDestination?.id != resIdइसे हल करना चाहिए, लेकिन यह भी हर मामले को कवर नहीं कर सकता है।
wchristiansen

@wchristiansen, नोट के लिए धन्यवाद। मैंने वैश्विक कार्यों के उपयोग के साथ कोड अपडेट किया है
एलेक्स नट

@AlexNuts शानदार जवाब। मुझे लगता है कि आप हटा सकते हैं ?: graph.getAction(resId)-> currentDestination?.getAction(resId)वैश्विक या गैर-वैश्विक दोनों कार्यों के लिए एक कार्रवाई लौटाएंगे (मैंने इसे परीक्षण किया है)। इसके अलावा, बेहतर होगा यदि आप सेफ आर्ग्स का उपयोग करें -> बजाय में और अलग navDirections: NavDirectionsसे पास करें । resIdargs
वेस

@AlexNuts ध्यान दें कि यह समाधान वर्तमान गंतव्य के समान गंतव्य पर नेविगेट करने का समर्थन नहीं करता है। बंडल Z के साथ गंतव्य Y से गंतव्य X तक पहुंचने के लिए Iow नेविगेट करना संभव नहीं है।
वेस

18

यह भी हो सकता है अगर आपके पास एक टुकड़ा है जो एक दृश्यदर्शी के साथ टुकड़े बी है और आप बी से सी में नेविगेट करने का प्रयास करते हैं

चूंकि ViewPager में टुकड़े A का गंतव्य नहीं है, इसलिए आपका ग्राफ आपको पता नहीं होगा कि आप B पर हैं।

एक समाधान सी में नेविगेट करने के लिए बी में अप्रत्यक्ष उपयोग कर सकता है


इस मामले में, दुर्घटना हर बार नहीं होती है, लेकिन केवल दुर्लभ होती है। इसे कैसे हल करें?
श्रीकर रेड्डी

आप नेवीग्राफ के अंदर एक वैश्विक कार्रवाई जोड़ सकते हैं और इसे नेविगेट करने के लिए उपयोग कर सकते हैं
अब्राहम मैथ्यू

1
जैसा कि बी को अपने सटीक माता-पिता के बारे में पता होने की आवश्यकता नहीं है, यह एक इंटरफेस के माध्यम से अप्रत्यक्ष उपयोग करना बेहतर होगा (parentFragment as? XActionListener)?.Xaction()और ध्यान दें कि आप इस फ़ंक्शन को एक स्थानीय चर के रूप में पकड़ सकते हैं यदि यह सहायक है
hmac

क्या आप
इसे समझने

कोई भी नमूना कोड लगा सकता है, मैं उसी मुद्दे पर फंस गया हूं। एक टुकड़ा है और फिर एक tabfragment है
उस्मान ज़फर

13

दुर्घटना को रोकने के लिए मैंने जो किया वह निम्न है:

मेरे पास एक बेसग्रैगमेंट है, इसमें मैंने यह 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 और इसके बीच क्या अंतर है, अभी भी सिस्टम बैक बटन के साथ अटका हुआ है और इसे ओवरराइड कर रहा है और इसके साथ प्रतिस्थापित करना पागल लगता है
डैनियल विल्सन

2
मैं मानता हूं कि यह पागल लगता है। एंड्रॉइड नेविगेशन आर्किटेक्चर घटक में मैंने जिन चीजों का सामना किया है उनमें से कई को थोड़ा पागल लगता है, यह बहुत सख्ती से आईएमओ स्थापित है। हमारी परियोजना के लिए अपना खुद का कार्यान्वयन करने के बारे में सोचकर, क्योंकि यह बहुत अधिक सिरदर्द पैदा कर रहा है
नील

मेरे लिए काम नहीं करता है ... फिर भी वही त्रुटि हो रही है।
ओटज़िएई

5

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)ग्राफ कहा जाता है और बैक-स्टैक लगभग तुरंत बदल जाता है जबकि यूआई अभी तक अपडेट नहीं किया गया है। बस जल्दी नहीं है, लेकिन यह ठीक है। बैक-स्टैक को बदलने के बाद नेविगेशन सिस्टम को दूसरी navigate(R.id.my_next_destination)कॉल मिलती है । चूंकि बैक-स्टैक बदल गया है इसलिए अब हम स्टैक में शीर्ष टुकड़े के सापेक्ष काम करते हैं। शीर्ष टुकड़ा वह टुकड़ा है जिसका उपयोग करके आप नेविगेट करते हैं R.id.my_next_destination, लेकिन इसमें ID के साथ आगे कोई गंतव्य नहीं है R.id.my_next_destination। इस प्रकार आप प्राप्त करते हैंIllegalArgumentException आईडी के कारण कि टुकड़ा के बारे में कुछ भी नहीं पता है।

यह सटीक त्रुटि NavController.javaविधि में पाई जा सकती है findDestination


4

मेरे मामले में, यह समस्या तब हुई जब मैंने viewpagerएक बच्चे के रूप में एक टुकड़े के अंदर अपने एक टुकड़े का फिर से उपयोग किया था viewpagerviewpagerटुकड़ा (जो माता-पिता टुकड़ा था) नेविगेशन एक्सएमएल में जोड़ा गया, लेकिन कार्रवाई में शामिल नहीं किया गया 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">

मूल दृश्य व्यूअर खंड में क्रिया को जोड़कर समस्या को ठीक किया गया जैसा कि नीचे दिखाया गया है:

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 नेविगेशनVersion = "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 यह केवल एक आईडी है जिसे आपको अपने आईडीएक्सएक्सएमएल में जोड़ना होगा, यह सुनिश्चित करने के लिए कि यह अद्वितीय है। <item name="tag_navigation_destination_id" type="id" />

बग और समाधान, और अधिक जानकारी navigateSafe(...)में विस्तार तरीकों "फिक्सिंग खूंखार" ... इस NavController के लिए अज्ञात है "


मैंने इस समस्या के कुछ अलग-अलग समाधानों का अध्ययन किया है, और आपका निश्चित रूप से सबसे अच्छा है। मुझे इसके लिए इतना कम प्यार देखकर दुखी होता है
ल्यूक

1
यह NAV_DESTINATION_IDइस तरह के stackoverflow.com/a/15021758/1572848
विलियम रीड

हां, मैंने जवाब अपडेट कर दिया है
फ्रैंक

टैग कहां से आ रहा है और इसकी आवश्यकता क्यों है? मुझे समस्या है कि नेविगेशन घटक पर वास्तविक आईडी कहां से मेल नहीं खाती है R.id
रिजेबोसच

R.id.tag_navigation_destination_idयह केवल एक आईडी है जिसे आपको अपने आईडीएक्सएक्सएमएल में जोड़ना होगा, यह सुनिश्चित करने के लिए कि यह अद्वितीय है। <item name="tag_navigation_destination_id" type="id" />
फ्रैंक

3

मेरे मामले में, मेरे पास कई नेवी ग्राफ की फाइलें थीं और मैं 1 नेवी ग्राफ की लोकेशन से दूसरे नेवी ग्राफ में गंतव्य तक जाने की कोशिश कर रहा था।

इसके लिए हमें इस तरह से 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

मेरे मामले में बग पर संदेह हुआ क्योंकि मेरे पास एक नेविगेशन कार्रवाई थी Single Topऔर Clear Taskएक स्प्लैश स्क्रीन के बाद सक्षम विकल्प।


1
लेकिन क्लीयरटैस्क को हटा दिया गया है, आपको इसके बजाय पॉपअप () का उपयोग करना चाहिए।
जेरी ओकाफ़ोर

@ Po10cio उन झंडे में से किसी को भी नहीं चलाया गया था, मैंने इसे हटा दिया था और इसे ठीक कर दिया गया था।
यूरी पेरेज़ बेल्ट्रै

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

ऐसा लगता है जैसे आप कार्य को साफ़ कर रहे हैं। ऐप में एक-बार सेटअप या लॉगिन स्क्रीन की श्रृंखला हो सकती है। इन सशर्त स्क्रीन को आपके एप्लिकेशन का प्रारंभिक गंतव्य नहीं माना जाना चाहिए।

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


1

मैंने कक्षाओं के कुछ नामों के बाद इस अपवाद को पकड़ा। उदाहरण के लिए: मैं बुलाया वर्गों था FragmentAसाथ @+is/fragment_aनेविगेशन ग्राफ़ तथा FragmentBसाथ @+id/fragment_b। फिर मैंने हटा दिया FragmentAऔर नाम बदल FragmentBदिया FragmentA। तो के उस नोड के बाद FragmentAअभी भी नेविगेशन ग्राफ में रुके थे, और android:nameके FragmentBके नोड नाम दिया गया था path.to.FragmentA। मेरे पास एक ही android:nameऔर अलग-अलग दो नोड्स थे android:id, और मुझे जो कार्रवाई की आवश्यकता थी उसे हटाए गए वर्ग के नोड पर परिभाषित किया गया था।


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
                        )

अब तक, समस्या वास्तव में हल है।


1

ऐसा लगता है कि बैकस्टैक के फ्रेग्मेंटमैन कंट्रोल और बैकस्ट के नेविगेशन आर्किटेक्चर कंट्रोल को मिलाने से यह समस्या भी पैदा हो सकती है।

उदाहरण के लिए मूल CameraX बेसिक सैंपल में नीचे दिए गए टुकड़े टुकड़े करने वाले नेविगेशन का इस्तेमाल किया गया था और ऐसा प्रतीत होता है जैसे कि यह ठीक से इंटरैक्ट नहीं किया गया है:

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

यदि आप मुख्य खंड (इस मामले में कैमरा टुकड़ा) से आगे बढ़ने से पहले इस संस्करण के साथ 'वर्तमान गंतव्य' लॉग करते हैं और फिर मुख्य खंड में लौटने पर इसे फिर से लॉग इन करते हैं, तो आप लॉग में आईडी से देख सकते हैं कि आईडी एक ही नहीं है। एक अनुमान के अनुसार, नेविगेशन ने इसे तब अद्यतन किया जब खंड में जा रहा था और सुगंधमग्न ने फिर से इसे तब अद्यतन नहीं किया जब वापस जा रहा हो। लॉग से:

पहले : D / CameraXBasic: currentDest ?: androidx.navigation.fragment.FragmentNavigator$Destination@b713195

के बाद : D / CameraXBasic: currentDest ?: androidx.navigation.fragment.FragmentNavigator$Destination@9807d8f

कैमराएक्स बेसिक सैंपल का अपडेटेड वर्जन नेविगेशन का उपयोग इस तरह से करता है:

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

यह सही ढंग से काम करता है और मुख्य खंड पर वापस जाते समय लॉग एक ही आईडी दिखाते हैं।

पहले : D / CameraXBasic: currentDest ?: androidx.navigation.fragment.FragmentNavigator$Destination@b713195

के बाद : D / CameraXBasic: currentDest ?: androidx.navigation.fragment.FragmentNavigator$Destination@b713195

मुझे कहानी के नैतिक पर संदेह है, कम से कम इस समय, बहुत ही सावधानी से नेविगेशन विखंडन करने के लिए है।


यह प्रशंसनीय लगता है, मैं आगे की जांच करूंगा। क्या कोई भी इस दावे को सत्यापित या प्रमाणित करने में सक्षम है?
जेरी ओकाफ़ोर

@JerryOkafor - मैंने एक ऐप में इसका परीक्षण किया है जो मैं कैमराएक्स सैंपल के आधार पर काम कर रहा था और इसे सत्यापित किया था, लेकिन यह देखना अच्छा होगा कि क्या किसी और ने भी इसे देखा है। मैं वास्तव में एक ही ऐप में एक जगह 'बैक नेविगेशन' से चूक गया था इसलिए इसे हाल ही में फिर से तय किया।
मिक

1

एक हास्यास्पद तरीका लेकिन बहुत शक्तिशाली है: बस इसे कॉल करें:

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

बस इस विस्तार बनाएँ:

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

1

इस समस्या के कई कारण हो सकते हैं। मेरे मामले में मैं MVVM मॉडल का उपयोग कर रहा था और मैं नेविगेशन के लिए एक बूलियन देख रहा था जब बूलियन सच है -> नेविगेट करें और कुछ भी न करें और यह ठीक काम कर रहा था लेकिन यहां एक गलती थी

गंतव्य खंड से वापस बटन दबाने पर मैं एक ही समस्या का सामना कर रहा था। और बूलियन वस्तु एक समस्या थी क्योंकि मैं बूलियन मान को गलत में बदलना भूल गया था, इसने गड़बड़ी पैदा की। मैंने सिर्फ एक फ़ंक्शन बनाया है दृश्य में यह मान को गलत और उसके मान को बदलने के लिए बनाया गया है यह सिर्फ findNavController के बाद कहा जाता है ()


1

आमतौर पर जब मेरे साथ ऐसा होता है, तो मेरे पास चार्ल्स मैडरे द्वारा वर्णित मुद्दा था: एक ही यूआई पर दो नेविगेशन इवेंट शुरू हो गए, एक में करंटडिनेशन बदल रहा है और दूसरा फेल हो रहा है क्योंकि करंटडेस्टिनेशन बदल गया है। ऐसा तब हो सकता है जब आप डबल-टैप करते हैं, या एक क्लिक श्रोता के साथ दो विचारों पर क्लिक करके findNavController.navigate कहते हैं।

तो इसे हल करने के लिए आप या तो उपयोग कर सकते हैं-चेक, ट्राइ-कैच या यदि आप रुचि रखते हैं तो एक खोजसैफनावरकंट्रोलर () है जो नेविगेट करने से पहले आपके लिए यह जांच करता है। यह सुनिश्चित करने के लिए एक लिंट-चेक भी है कि आप इस मुद्दे को न भूलें।

GitHub

लेख इस मुद्दे का विवरण


1

यदि आप बहुत जल्दी क्लिक करते हैं, तो यह अशक्त और दुर्घटना का कारण होगा।

हम इस पर मदद करने के लिए RxBinding लिब का उपयोग कर सकते हैं। ऐसा होने से पहले आप क्लिक पर थ्रॉटल और अवधि जोड़ सकते हैं।

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

एंड्रॉइड पर थ्रॉटलिंग पर ये लेख मदद कर सकते हैं। चीयर्स!


1

यदि आप एक recyclerview का उपयोग कर रहे हैं, तो बस एक क्लिक श्रोता को अपने क्लिक पर cooldown जोड़ें और अपने 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

यह मेरे साथ हुआ, मेरा मुद्दा यह था कि मैं एक एफएबी पर क्लिक कर रहा था tab item fragment। मैं एक टैब आइटम टुकड़े से नेविगेट करने की कोशिश कर रहा था another fragment

लेकिन इस जवाब में इयान झील के अनुसार हमें उपयोग करना है और , कोई नेविगेशन घटक समर्थन नहीं है । इस वजह से, टैबलेउटआउट से लेकर टैब आइटम के टुकड़े तक कोई नेविगेशन पथ नहीं है।tablayoutviewpager

उदाहरण के लिए:

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

समाधान था टैब लेआउट से एक रास्ता बनाना जिसमें खंडित पूर्व टुकड़ा पूर्व: पथ: 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

मैंने यह विस्तार समारोह फ्रैगमेंट के लिए बनाया है:

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)
        }
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.