Mam problem z nowym komponentem Android Navigation Architecture, gdy próbuję przejść z jednego fragmentu do drugiego , pojawia się ten dziwny błąd:
java.lang.IllegalArgumentException: navigation destination XXX
is unknown to this NavController
Każda inna nawigacja działa dobrze, z wyjątkiem tej konkretnej.
Używam findNavController()
funkcji Fragment, aby uzyskać dostęp do NavController
.
Każda pomoc zostanie doceniona.
java
android
kotlin
android-navigation
android-architecture-navigation
Jerry Okafor
źródło
źródło
Odpowiedzi:
W moim przypadku, jeśli użytkownik bardzo szybko kliknie ten sam widok dwa razy, nastąpi awaria. Musisz więc zaimplementować jakąś logikę, aby zapobiec wielokrotnym szybkim kliknięciom ... Co jest bardzo denerwujące, ale wydaje się konieczne.
Możesz przeczytać więcej na temat zapobiegania temu tutaj: Android Zapobieganie dwukrotnemu kliknięciu przycisku
Edycja 19.03.2019 : Aby wyjaśnić nieco dokładniej, tej awarii nie można odtworzyć wyłącznie poprzez „bardzo szybkie kliknięcie tego samego widoku dwa razy, bardzo szybko”. Alternatywnie możesz po prostu użyć dwóch palców i kliknąć dwa (lub więcej) widoki w tym samym czasie, gdzie każdy widok ma własną nawigację, którą by wykonywał. Jest to szczególnie łatwe, gdy masz listę przedmiotów. Powyższe informacje o zapobieganiu wielokrotnym kliknięciom pomogą w tym przypadku.
Edycja 4/16/2020 : Na wypadek, gdybyś nie był bardzo zainteresowany przeczytaniem powyższego postu Stack Overflow, dołączam moje własne rozwiązanie (Kotlin), którego używam od dłuższego czasu.
OnSingleClickListener.kt
ViewExt.kt
HomeFragment.kt
źródło
Sprawdź,
currentDestination
zanim zadzwonisz, nawigacja może być pomocna.Na przykład, jeśli masz dwa miejsca docelowe fragmentów na wykresie nawigacyjnym
fragmentA
ifragmentB
i jest tylko jedna akcja odfragmentA
dofragmentB
. wywołanienavigate(R.id.action_fragmentA_to_fragmentB)
spowoduje,IllegalArgumentException
że już byłeś nafragmentB
. Dlatego zawsze powinieneś sprawdzićcurrentDestination
przed rozpoczęciem nawigacji.źródło
Możesz sprawdzić żądaną akcję w aktualnym miejscu docelowym kontrolera nawigacyjnego.
UPDATE dodał użycie globalnych działań dla bezpiecznej nawigacji.
źródło
currentDestination
listą akcji. Załóżmy, że masz zdefiniowaną akcję globalną i użyj jej do nawigacji. To się nie powiedzie, ponieważ akcja nie jest zdefiniowana na liście <action> currentDestination. Dodanie czeku likecurrentDestination?.getAction(resId) != null || currentDestination?.id != resId
powinno rozwiązać ten problem, ale może też nie obejmować wszystkich przypadków.?: graph.getAction(resId)
->currentDestination?.getAction(resId)
zwróci akcję zarówno dla akcji globalnych, jak i nieglobalnych (przetestowałem to). Ponadto, będzie lepiej jeśli zostały dokonane użyć bezpiecznego args -> raczej zdać sięnavDirections: NavDirections
niżresId
iargs
oddzielnie.Może się to również zdarzyć, jeśli masz Fragment A z ViewPager fragmentów B i próbujesz przejść z B do C
Ponieważ w ViewPager fragmenty nie są miejscem docelowym A, twój wykres nie wiedziałby, że jesteś na B.
Rozwiązaniem może być użycie ADirections w B, aby przejść do C
źródło
(parentFragment as? XActionListener)?.Xaction()
i zanotować można trzymać tę funkcję jako zmiennej lokalnej, czy to jest pomocneOto co zrobiłem, aby zapobiec awarii:
Mam BaseFragment, tam dodałem to,
fun
aby upewnić się, żedestination
jest znany przezcurrentDestination
:Warto zauważyć, że używam wtyczki SafeArgs .
źródło
W moim przypadku używałem niestandardowego przycisku Wstecz do nawigacji w górę. Zadzwoniłem
onBackPressed()
zamiast poniższego koduTo spowodowało
IllegalArgumentException
wystąpienie. Po tym, jak zmieniłem go, abynavigateUp()
zamiast tego używać metody, nie miałem ponownie awarii.źródło
TL; DR Owijaj swoje
navigate
połączeniatry-catch
(w prosty sposób) lub upewnij się, żenavigate
w krótkim czasie będzie tylko jedno połączenie . Ten problem prawdopodobnie nie zniknie. Skopiuj większy fragment kodu do swojej aplikacji i wypróbuj.Dzień dobry. W oparciu o kilka przydatnych odpowiedzi powyżej, chciałbym podzielić się moim rozwiązaniem, które można rozszerzyć.
Oto kod, który spowodował awarię w mojej aplikacji:
Sposobem na łatwe odtworzenie błędu jest dotknięcie wieloma palcami listy elementów, gdzie kliknięcie każdego elementu powoduje przejście do nowego ekranu (w zasadzie to samo, co ludzie zauważyli - dwa lub więcej kliknięć w bardzo krótkim czasie ). Zauważyłem to:
navigate
wywołanie zawsze działa dobrze;navigate
metody są rozwiązywane wIllegalArgumentException
.Z mojego punktu widzenia taka sytuacja może pojawiać się bardzo często. Ponieważ powtarzanie kodu jest złą praktyką i zawsze dobrze jest mieć jeden punkt wpływu, pomyślałem o następnym rozwiązaniu:
}
I tak powyższy kod zmienia się tylko w jednej linii z tego:
do tego:
Stało się nawet trochę krótsze. Kod został przetestowany w dokładnym miejscu, w którym nastąpiła awaria. Już tego nie doświadczyłem i użyję tego samego rozwiązania w innych nawigacjach, aby dalej uniknąć tego samego błędu.
Wszelkie myśli są mile widziane!
Co dokładnie powoduje awarię
Pamiętaj, że tutaj pracujemy z tym samym wykresem nawigacyjnym, kontrolerem nawigacji i stosem wstecznym, gdy używamy metody
Navigation.findNavController
.Tutaj zawsze otrzymujemy ten sam kontroler i wykres. Kiedy
navigate(R.id.my_next_destination)
nazywa się wykres i zmiany stosu wstecznego prawie natychmiast, gdy interfejs użytkownika nie jest jeszcze aktualizowany. Po prostu nie wystarczająco szybko, ale to jest w porządku. Po zmianie stosu system nawigacji odbiera drugienavigate(R.id.my_next_destination)
wywołanie. Ponieważ stos się zmienił, działamy teraz względem górnego fragmentu stosu. Górny fragment to fragment, do którego nawigujeszR.id.my_next_destination
, ale nie zawiera żadnych dalszych celów z identyfikatoremR.id.my_next_destination
. W ten sposób otrzymujeszIllegalArgumentException
ze względu na identyfikator, o którym fragment nic nie wie.Dokładny błąd można znaleźć w
NavController.java
metodziefindDestination
.źródło
W moim przypadku problem wystąpił, gdy ponownie użyłem jednego z moich fragmentów wewnątrz
viewpager
fragmentu jako dzieckoviewpager
.viewpager
Fragment (który był fragment rodzic) dodano xml nawigacji, ale działanie to zostało dodane wviewpager
fragmentu macierzystego.Naprawiono problem, dodając akcję do nadrzędnego fragmentu podglądu, jak pokazano poniżej:
źródło
Dzisiaj
Problem nadal istnieje. Moje podejście do Kotlina to:
źródło
Możesz sprawdzić przed rozpoczęciem nawigacji, czy fragment żądający nawigacji jest nadal aktualnym celem, wzięty z tego sedna .
Zasadniczo ustawia znacznik na fragmencie do późniejszego wyszukiwania.
R.id.tag_navigation_destination_id
to tylko identyfikator, który musisz dodać do swojego ids.xml, aby upewnić się, że jest unikalny.<item name="tag_navigation_destination_id" type="id" />
Więcej informacji o błędzie i rozwiązaniu oraz
navigateSafe(...)
metodach rozszerzania w „Naprawianie przerażających”… jest nieznane temu NavController ”źródło
NAV_DESTINATION_ID
czegoś takiego jak ten stackoverflow.com/a/15021758/1572848R.id
.R.id.tag_navigation_destination_id
to tylko identyfikator, który musisz dodać do swojego ids.xml, aby upewnić się, że jest unikalny.<item name="tag_navigation_destination_id" type="id" />
W moim przypadku miałem wiele plików z wykresami nawigacyjnymi i próbowałem przejść z 1 lokalizacji wykresu nawigacyjnego do miejsca docelowego na innym wykresie nawigacyjnym.
W tym celu musimy dołączyć drugi wykres nawigacyjny do pierwszego w ten sposób
i dodaj to do swojej akcji:
gdzie
second_graph
jest:na drugim wykresie.
Więcej informacji tutaj
źródło
Rozwiązałem ten sam problem, umieszczając check przed nawigacją zamiast standardowego kodu do natychmiastowego kliknięcia
zgodnie z tą odpowiedzią
https://stackoverflow.com/a/56168225/7055259
źródło
W moim przypadku bug ocurred bo miałem działanie nawigacji z
Single Top
iClear Task
opcje włączone po ekranie powitalnym.źródło
Otrzymałem ten sam błąd, ponieważ użyłem szuflady nawigacji i
getSupportFragmentManager().beginTransaction().replace( )
jednocześnie gdzieś w moim kodzie.Pozbyłem się błędu, korzystając z tego warunku (testowanie, czy miejsce docelowe):
W moim przypadku poprzedni błąd został wywołany, gdy klikałem opcje szuflady nawigacji. Zasadniczo powyższy kod ukrył błąd, ponieważ w moim kodzie gdzieś użyłem nawigacji z użyciem
getSupportFragmentManager().beginTransaction().replace( )
warunku -nigdy nie został osiągnięty, ponieważ
(Navigation.findNavController(v).getCurrentDestination().getId()
zawsze zmierzał do fragmentu domu. DoNavigation.findNavController(v).navigate(R.id.your_action)
wszystkich działań nawigacyjnych należy używać tylko funkcji kontrolera lub nawigować po nich.źródło
Wygląda na to, że usuwasz zadanie. Aplikacja może mieć jednorazową konfigurację lub serię ekranów logowania. Te ekrany warunkowe nie powinny być traktowane jako początkowe miejsce docelowe Twojej aplikacji.
https://developer.android.com/topic/libraries/architecture/navigation/navigation-conditional
źródło
Złapałem ten wyjątek po kilku zmianach nazw klas. Na przykład: miałem klasy wywołane
FragmentA
z@+is/fragment_a
w wykresie nawigacyjnym iFragmentB
z@+id/fragment_b
. Następnie usunąłemFragmentA
i zmieniłem nazwęFragmentB
naFragmentA
. Więc po tym węźleFragmentA
nadal przebywał na wykresie nawigacji orazandroid:name
zFragmentB
„s węzła została zmienionapath.to.FragmentA
. Miałem dwa węzły z takimi samymiandroid:name
i różnymiandroid:id
, a akcja, której potrzebowałem, została zdefiniowana na węźle usuniętej klasy.źródło
Przychodzi mi do głowy, gdy dwukrotnie wciskam przycisk Wstecz. Na początku przechwytuję
KeyListener
i nadpisujęKeyEvent.KEYCODE_BACK
. Dodałem poniższy kod w funkcji o nazwieOnResume
Fragment, a następnie ta kwestia / problem jest rozwiązany.Kiedy zdarza mi się to po raz drugi, a jego stan jest taki sam jak pierwszy, stwierdzam, że być może używam tej
adsurd
funkcji. Przeanalizujmy te sytuacje.Najpierw FragmentA przechodzi do FragmentuB, następnie FragmentB nawiguje do FragmentuA, a następnie naciska przycisk Wstecz ... pojawia się awaria.
Po drugie, FragmentA nawiguje do FragmentB, następnie FragmentB nawiguje do FragmentC, FragmentC nawiguje do FragmentuA, a następnie naciska przycisk Wstecz ... pojawia się awaria.
Myślę więc, że po naciśnięciu przycisku Wstecz FragmentA powróci do FragmentB lub FragmentC, a następnie spowoduje bałagan logowania. Wreszcie stwierdzam, że nazwana funkcja
popBackStack
może być używana raczej do nawigacji niż do nawigacji.Jak dotąd problem jest naprawdę rozwiązany.
źródło
Wygląda na to, że mieszanie kontroli fragmentManagera z backstackiem i kontroli architektury nawigacji dla backstacka może również powodować ten problem.
Na przykład oryginalna podstawowa próbka CameraX wykorzystywała nawigację wsteczną fragmentManager, jak poniżej i wygląda na to, że nie współdziała poprawnie z nawigacją:
Jeśli zarejestrujesz `` bieżące miejsce docelowe '' w tej wersji przed przejściem z głównego fragmentu (w tym przypadku fragmentu kamery), a następnie zarejestrujesz go ponownie po powrocie do głównego fragmentu, możesz zobaczyć z id w logach, że id nie jest taki sam. Przypuszczalnie nawigacja zaktualizowała go podczas przechodzenia do fragmentu, a fragmntManager nie zaktualizował go ponownie podczas powrotu. Z dzienników:
Zaktualizowana wersja podstawowego przykładu CameraX korzysta z Nawigacji w następujący sposób:
Działa to poprawnie, a dzienniki pokazują ten sam identyfikator po powrocie do głównego fragmentu.
Podejrzewam, że morał tej historii, przynajmniej w tej chwili, polega na bardzo ostrożnym łączeniu nawigacji z nawigacją fragmentManager.
źródło
Śmiesznym sposobem, ale bardzo potężnym jest: Po prostu nazwij to:
Po prostu utwórz to rozszerzenie:
źródło
Przyczyn tego problemu może być wiele. W moim przypadku korzystałem z modelu MVVM i obserwowałem wartość logiczną do nawigacji, gdy wartość logiczna jest prawdziwa -> nawiguj w przeciwnym razie nic nie rób i to działało dobrze, ale tutaj był jeden błąd
po naciśnięciu przycisku Wstecz z fragmentu docelowego napotkałem ten sam problem. i problemem był obiekt boolowski, ponieważ zapomniałem zmienić wartość boolean na false, co spowodowało bałagan. właśnie utworzyłem funkcję w viewModel, aby zmienić jej wartość na false i nazwał to zaraz po funkcji findNavController ()
źródło
Zwykle, gdy mi się to przytrafia, miałem problem opisany przez Charlesa Madere: dwa zdarzenia nawigacji wywołane w tym samym interfejsie użytkownika, jedno zmieniające currentDestination, a drugie kończy się niepowodzeniem, ponieważ currentDestination jest zmieniony. Może się to zdarzyć, jeśli dwukrotnie dotkniesz lub klikniesz dwa widoki z odbiornikiem kliknięć wywołującym findNavController.navigate.
Aby rozwiązać ten problem, możesz użyć if-check, try-catch lub, jeśli jesteś zainteresowany, istnieje funkcja findSafeNavController (), która sprawdza to za Ciebie przed nawigacją. Posiada również funkcję sprawdzania kłaczków, aby upewnić się, że nie zapomnisz o tym problemie.
GitHub
Artykuł szczegółowo opisujący problem
źródło
Jeśli klikniesz zbyt szybko, spowoduje to zerowanie i awarię.
Możemy użyć biblioteki RxBinding, aby pomóc w tym. Możesz dodać przyspieszenie i czas trwania kliknięcia, zanim to nastąpi.
Te artykuły o ograniczaniu przepustowości w systemie Android mogą pomóc. Twoje zdrowie!
źródło
Jeśli używasz widoku recyklingu, po prostu dodaj czas odnowienia nasłuchiwania kliknięć na kliknięciu, a także w pliku XML recyclinglerview
android:splitMotionEvents="false"
źródło
Po przemyśleniu rady Iana Lake'a w tym twitterze wymyśliłem następujące podejście. Po
NavControllerWrapper
zdefiniowaniu jako takie:Następnie w kodzie nawigacyjnym:
źródło
Rozwiązuję ten problem, sprawdzając, czy następna akcja istnieje w bieżącym miejscu docelowym
To rozwiązuje problem, jeśli użytkownik szybko kliknie przycisk 2 różne
źródło
Zdarzyło mi się to, moim problemem było klikanie przycisku FAB
tab item fragment
. Próbowałem przejść z jednego z fragmentów pozycji karty doanother fragment
.Ale według Iana Lake'a w tej odpowiedzi musimy użyć
tablayout
iviewpager
, żadnego wsparcia dla komponentów nawigacyjnych . Z tego powodu nie ma ścieżki nawigacji od tabeli zawierającej fragment do fragmentu pozycji karty.dawny:
Rozwiązaniem było utworzenie ścieżki z układu zakładki zawierającej fragment do zamierzonego fragmentu np .: ścieżka:
container fragment -> another fragment
Niekorzyść:
źródło
W moim przypadku otrzymałem ten błąd, gdy próbowałem przejść z innego wątku, w 50% przypadków. Uruchom kod w głównym wątku pomaga
źródło
W moim przypadku miało to miejsce, gdy przypadkowo dodałem miejsce
+
docelowe w akcji, a awaria wystąpiła tylko wtedy, gdy wielokrotnie przeszedłem do tego samego fragmentu.Rozwiązaniem jest usunięcie
+
z miejsca docelowego akcji, użyj tylko@id/profileFragment
zamiast@+id/profileFragment
źródło
Zaktualizowane rozwiązanie @Alex Nuts
Jeśli nie ma akcji dla konkretnego fragmentu i chcesz przejść do fragmentu
źródło
Napisałem te rozszerzenia
źródło