У меня возникает проблема с новым компонентом архитектуры навигации Android, когда я пытаюсь перейти от одного фрагмента к другому , я получаю эту странную ошибку:
java.lang.IllegalArgumentException: navigation destination XXX
is unknown to this NavController
Любая другая навигация работает нормально, кроме этой.
Я использую findNavController()
функцию Fragment, чтобы получить доступ к NavController
.
Любая помощь будет оценена по достоинству.
java
android
kotlin
android-navigation
android-architecture-navigation
Джерри Окафор
источник
источник
Ответы:
В моем случае, если пользователь дважды очень быстро щелкнет одно и то же представление, произойдет сбой. Итак, вам нужно реализовать какую-то логику, чтобы предотвратить множественные быстрые щелчки ... Что очень раздражает, но кажется необходимым.
Вы можете узнать больше о предотвращении этого здесь: Android Preventing Double Click On A Button
Редактировать 3/19/2019 : Чтобы уточнить немного больше, этот сбой не воспроизводится исключительно, просто «щелкнув один и тот же вид дважды очень и очень быстро». В качестве альтернативы вы можете просто использовать два пальца и одновременно щелкнуть два (или более) представления, при этом каждое представление имеет свою собственную навигацию, которую они будут выполнять. Это особенно легко сделать, когда у вас есть список предметов. Вышеупомянутая информация о предотвращении множественных кликов справится с этим случаем.
Редактировать 4/16/2020 : На всякий случай, если вам не очень интересно читать этот пост о переполнении стека выше, я включаю свое собственное (Kotlin) решение, которое я использую уже давно.
OnSingleClickListener.kt
ViewExt.kt
HomeFragment.kt
источник
Проверка
currentDestination
перед вызовом навигации может быть полезна.Например, если у вас есть два назначения фрагмента на навигационном графе
fragmentA
иfragmentB
, и есть только одно действие отfragmentA
доfragmentB
. звонокnavigate(R.id.action_fragmentA_to_fragmentB)
закончится,IllegalArgumentException
когда вы уже были наfragmentB
. Поэтому вы всегда должны проверятьcurrentDestination
перед навигацией.источник
Вы можете проверить запрошенное действие в текущем пункте назначения контроллера навигации.
В ОБНОВЛЕНИЕ добавлено использование глобальных действий для безопасной навигации.
источник
currentDestination
списка действий. Допустим, у вас определено глобальное действие, и вы используете это действие для навигации. Это не удастся, потому что действие не определено в списке <action> currentDestination. Добавление проверкиcurrentDestination?.getAction(resId) != null || currentDestination?.id != resId
должно решить эту проблему, но также не может охватить все случаи.?: graph.getAction(resId)
->currentDestination?.getAction(resId)
вернет действие как для глобальных, так и для неглобальных действий (я это тестировал). Кроме того, было бы лучше, если бы вы использовали Safe Args -> скорее переходите,navDirections: NavDirections
чемresId
иargs
отдельно.Это также может произойти, если у вас есть фрагмент A с ViewPager из фрагментов B, и вы пытаетесь перейти от B к C
Поскольку в ViewPager фрагменты не являются адресатом A, ваш график не будет знать, что вы находитесь на B.
Решением может быть использование ADirections в B для перехода к C
источник
(parentFragment as? XActionListener)?.Xaction()
и обратите внимание, что вы могли бы сохранить эту функцию как локальную переменную, если это полезноДля предотвращения сбоя я сделал следующее:
У меня есть BaseFragment, я добавил это,
fun
чтобы убедиться, чтоdestination
он известенcurrentDestination
:Стоит отметить, что я использую плагин SafeArgs .
источник
В моем случае я использовал настраиваемую кнопку возврата для перехода вверх. Я вызвал
onBackPressed()
вместо следующего кодаЭто привело
IllegalArgumentException
к возникновению. После того, как я изменил его на использование этогоnavigateUp()
метода, у меня больше не было сбоев.источник
TL; DR Оберните ваши
navigate
звонкиtry-catch
(простой способ) или убедитесь, что будет только один звонок заnavigate
короткий период времени. Эта проблема, скорее всего, не исчезнет. Скопируйте более крупный фрагмент кода в свое приложение и попробуйте.Привет. Основываясь на нескольких приведенных выше полезных ответах, я хотел бы поделиться своим решением, которое можно расширить.
Вот код, который вызвал этот сбой в моем приложении:
Способ легко воспроизвести ошибку - нажать несколькими пальцами на список элементов, где щелчок по каждому элементу разрешается при переходе к новому экрану (в основном так же, как отмечали люди - два или более щелчка за очень короткий период времени ). Я заметил, что:
navigate
вызов всегда работает нормально;navigate
метода разрешаются вIllegalArgumentException
.С моей точки зрения, такая ситуация может возникать очень часто. Поскольку повторение кода - плохая практика, и всегда хорошо иметь одну точку влияния, я подумал о следующем решении:
}
Таким образом, приведенный выше код изменяется только в одной строке от этого:
к этому:
Даже стало немного короче. Код был протестирован в том месте, где произошел сбой. Больше не испытывал этого, и будет использовать то же решение для других навигаций, чтобы избежать той же ошибки в дальнейшем.
Любые мысли приветствуются!
Что именно вызывает сбой
Помните, что здесь мы работаем с одним и тем же графом навигации, контроллером навигации и бэк-стеком, когда используем метод
Navigation.findNavController
.Здесь всегда один и тот же контроллер и график. Когда
navigate(R.id.my_next_destination)
вызывается, граф и обратный стек изменяется почти мгновенно, пока пользовательский интерфейс еще не обновлен. Просто недостаточно быстро, но это нормально. После смены бэк-стека навигационная система получает второйnavigate(R.id.my_next_destination)
вызов. Поскольку задний стек изменился, теперь мы работаем относительно верхнего фрагмента в стеке. Верхний фрагмент - это фрагмент, к которому вы переходите с помощьюR.id.my_next_destination
, но он не содержит следующих пунктов назначения с идентификаторомR.id.my_next_destination
. Таким образом, вы получаетеIllegalArgumentException
из-за идентификатора, о котором фрагмент ничего не знает.Эту точную ошибку можно найти в
NavController.java
методеfindDestination
.источник
В моем случае проблема возникла, когда я повторно использовал один из моих фрагментов внутри
viewpager
фрагмента в качестве дочернего дляviewpager
.viewpager
Фрагмент (который был родительский фрагмент) был добавлен в навигации XML, но действие не было добавлено вviewpager
родительском фрагменте.Устранена проблема путем добавления действия к родительскому фрагменту окна просмотра, как показано ниже:
источник
Cегодня
Проблема все еще существует. Мой подход к Котлину:
источник
Вы можете проверить перед навигацией, является ли фрагмент, запрашивающий навигацию, по-прежнему текущим пунктом назначения, взятым из этой сути .
По сути, он устанавливает тег на фрагменте для последующего поиска.
R.id.tag_navigation_destination_id
это просто идентификатор, который вам нужно будет добавить в свой ids.xml, чтобы убедиться, что он уникален.<item name="tag_navigation_destination_id" type="id" />
Дополнительная информация об ошибке и решении, а также
navigateSafe(...)
методах расширения в «Устранении ужасного»… неизвестна этому NavController »источник
NAV_DESTINATION_ID
чего-то вроде этого stackoverflow.com/a/15021758/1572848R.id
.R.id.tag_navigation_destination_id
это просто идентификатор, который вам нужно будет добавить в свой ids.xml, чтобы убедиться, что он уникален.<item name="tag_navigation_destination_id" type="id" />
В моем случае у меня было несколько файлов навигационного графа, и я пытался перейти из одного местоположения навигационного графа в место назначения в другом навигационном графе.
Для этого мы должны включить 2-й граф навигации в 1-й, как это
и добавьте это к своему действию:
где
second_graph
находится:на втором графике.
Больше информации здесь
источник
Я решил ту же проблему, поставив галочку перед навигацией вместо стандартного кода для мгновенного нажатия кнопки управления
согласно этому ответу
https://stackoverflow.com/a/56168225/7055259
источник
В моем случае ошибка ocurred , потому что я имел навигационное действие с
Single Top
иClear Task
опция включается после заставки.источник
У меня такая же ошибка, потому что я использовал панель навигации и
getSupportFragmentManager().beginTransaction().replace( )
в то же время где-то в своем коде.Я избавился от ошибки, используя это условие (проверка, есть ли пункт назначения):
В моем случае предыдущая ошибка была вызвана, когда я нажимал на параметры панели навигации. В основном приведенный выше код скрывает ошибку, потому что где-то в моем коде я использовал навигацию с помощью
getSupportFragmentManager().beginTransaction().replace( )
условия -никогда не был достигнут, потому что
(Navigation.findNavController(v).getCurrentDestination().getId()
всегда указывал на домашний фрагмент. Вы должны использовать толькоNavigation.findNavController(v).navigate(R.id.your_action)
функции контроллера или навигационного графа для всех ваших действий по навигации.источник
Похоже, вы решаете задачу. Приложение может иметь одноразовую настройку или серию экранов входа в систему. Эти условные экраны не следует рассматривать как начальную точку назначения вашего приложения.
https://developer.android.com/topic/libraries/architecture/navigation/navigation-conditional
источник
Я поймал это исключение после нескольких переименований классов. Например: у меня были классы, вызываемые
FragmentA
с помощью@+is/fragment_a
в графе навигации иFragmentB
с помощью@+id/fragment_b
. Потом удалилFragmentA
и переименовалFragmentB
вFragmentA
. Таким образом, после этого узелFragmentA
оставался в навигационном графе, а узелandroid:name
ofFragmentB
был переименованpath.to.FragmentA
. У меня было два узла с одинаковымиandroid:name
и разнымиandroid:id
, и нужное мне действие было определено на узле удаленного класса.источник
Мне это приходит в голову, когда я нажимаю кнопку назад два раза. Сначала я перехватываю
KeyListener
и отменяюKeyEvent.KEYCODE_BACK
. Я добавил приведенный ниже код в функцию, названнуюOnResume
для фрагмента, и тогда этот вопрос / проблема решена.Когда это случается со мной во второй раз, и его статус такой же, как и у первого, я обнаружил, что, возможно, использую эту
adsurd
функцию. Давайте проанализируем эти ситуации.Сначала FragmentA переходит к FragmentB, затем FragmentB переходит к FragmentA, затем нажимает кнопку возврата ... появляется сбой.
Во-вторых, FragmentA переходит к FragmentB, затем FragmentB переходит к FragmentC, FragmentC переходит к FragmentA, затем нажимает кнопку возврата ... появляется сбой.
Поэтому я думаю, что при нажатии кнопки возврата FragmentA вернется к FragmentB или FragmentC, тогда это вызовет беспорядок при входе. Наконец, я обнаружил, что названная функция
popBackStack
может использоваться для возврата, а не для навигации.Пока проблема действительно решена.
источник
Кажется, что смешивание элемента управления fragmentManager backstack и элемента управления архитектурой навигации backstack также может вызвать эту проблему.
Например, в исходном базовом примере CameraX использовалась навигация backstack fragmentManager, как показано ниже, и похоже, что он неправильно взаимодействовал с навигацией:
Если вы регистрируете «текущее место назначения» с помощью этой версии перед переходом от основного фрагмента (в данном случае фрагмент камеры), а затем снова регистрируете его, когда вы возвращаетесь к основному фрагменту, вы можете увидеть из идентификатора в журналах, что идентификатор не такой же. Предположительно, навигация обновляла его при переходе к фрагменту, а fragmntManager не обновлял его снова при возвращении. Из журналов:
В обновленной версии базового примера CameraX для возврата используется навигация следующим образом:
Это работает правильно, и журналы показывают тот же идентификатор при возврате к основному фрагменту.
Я подозреваю, что мораль этой истории, по крайней мере на данный момент, состоит в том, чтобы быть очень осторожным, смешивая навигацию с навигацией fragmentManager.
источник
Смешной, но очень мощный способ: просто назовите это:
Просто создайте это расширение:
источник
У этой проблемы может быть много причин. В моем случае я использовал модель MVVM, и я наблюдал логическое значение для навигации, когда логическое значение истинно -> переходить, иначе ничего не делать, и это работало нормально, но здесь была одна ошибка
при нажатии кнопки возврата из целевого фрагмента я столкнулся с той же проблемой. И проблема была в логическом объекте, так как я забыл изменить логическое значение на false, это создало беспорядок. Я просто создал функцию в viewModel, чтобы изменить ее значение на false и вызвал его сразу после findNavController ()
источник
Обычно, когда это случается со мной, у меня возникает проблема, описанная Чарльзом Мадером: два события навигации запускаются в одном и том же пользовательском интерфейсе, одно изменяет currentDestination, а другое терпит неудачу, потому что currentDestination изменяется. Это может произойти, если вы дважды коснетесь или щелкните два представления с помощью прослушивателя щелчков, вызывающего findNavController.navigate.
Итак, чтобы решить эту проблему, вы можете использовать if-check, try-catch или, если вам интересно, есть findSafeNavController (), который выполняет это за вас перед навигацией. Также имеется функция проверки на ворсинки, чтобы вы не забыли об этой проблеме.
GitHub
Статья с подробным описанием проблемы
источник
Если вы нажмете слишком быстро, это приведет к обнулению и сбою.
Мы можем использовать RxBinding lib, чтобы помочь в этом. Вы можете добавить дроссель и продолжительность щелчка, прежде чем он произойдет.
Эти статьи о регулировании скорости на Android могут помочь. Ура!
источник
Если вы используете recyclerview, просто добавьте перезарядку прослушивателя кликов при нажатии, а также в своем XML-файле recyclerview используйте
android:splitMotionEvents="false"
источник
Подумав над советом Иэна Лейка в этой ветке твиттера, я пришел к следующему подходу. Определив
NavControllerWrapper
как таковые:Затем в коде навигации:
источник
Я решаю эту проблему, проверяя, существует ли следующее действие в текущем пункте назначения.
Это решает проблему, если пользователь быстро нажимает две разные кнопки.
источник
Это случилось со мной, моя проблема заключалась в том, что я нажимал FAB
tab item fragment
. Я пытался перейти от одного из фрагментов вкладки кanother fragment
.Но , по словам Иэна озера в этом ответе мы должны использовать
tablayout
иviewpager
, никакой поддержки навигации компонент . Из-за этого не существует пути навигации от макета вкладки, содержащего фрагмент, к фрагменту элемента вкладки.например:
Решением было создать путь из макета вкладки, содержащей фрагмент, к намеченному фрагменту, например: путь:
container fragment -> another fragment
Недостаток:
источник
В моем случае я получал эту ошибку при попытке перейти из другого потока в 50% случаев. Запустить код в основном потоке помогает
источник
В моем случае это произошло, когда я случайно добавил
+
пункт назначения в действии, и сбой произошел только тогда, когда я несколько раз переходил к одному и тому же фрагменту.Решение - удалить
+
из пункта назначения действия, использовать только@id/profileFragment
вместо@+id/profileFragment
источник
Обновленное решение @Alex Nuts
Если для определенного фрагмента нет действий и вы хотите перейти к фрагменту
источник
Я написал это расширение
источник