IllegalArgumentException: навігаційне призначення xxx невідоме цьому NavController


139

У мене виникають проблеми з новим компонентом архітектури навігації Android, коли я намагаюся переходити від одного фрагмента до іншого , я отримую цю дивну помилку:

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

Будь-яка інша навігація працює чудово, крім цієї конкретної.

Я використовую findNavController()функцію Fragment, щоб отримати доступ до NavController.

Будь-яка допомога буде вдячна.


Введіть код для кращого розуміння.
Алекс

12
Це трапляється і зі мною.
Еври Перес Бельтре

Наразі частота появи цієї помилки знижувалася з новими випусками бібліотеки, але я думаю, що бібліотека ще недостатньо задокументована.
Джеррі Окафор

Відповіді:


76

У моєму випадку, якщо користувач двічі дуже швидко натисне на той самий вигляд, цей збій відбудеться. Тож вам потрібно застосувати якусь логіку, щоб запобігти численним швидким клацанням ... Що дуже дратує, але це, здається, є необхідним.

Докладніше про запобігання цьому ви можете прочитати тут: Android Запобігання подвійного клацання на кнопці

Редагувати 19.03.2019 : Просто для уточнення ще більше, ця аварійна ситуація не може бути відтворена, лише "натиснувши на один і той же погляд двічі дуже швидко". Крім того, ви можете просто використовувати два пальці і клацнути два (або більше) представлення одночасно, де кожен вид має власну навігацію, яку вони виконували б. Це особливо легко зробити, коли у вас є список предметів. Наведена вище інформація про запобігання декількох клацань буде обробляти цей випадок.

Редагувати 16.04.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
Марко,

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
У мене є пошуковий додаток, який здійснює навігацію з дією з аргументами. Таким чином він міг перейти від поточного Визначення до себе. Я в кінцевому підсумку робив те саме, крім navController.currentDestination == navController.graph.node. Це відчувало щось брудне, але я відчуваю, що мені не потрібно було цього робити.
Шон Мейбуш

84
Бібліотека не повинна змушувати нас робити цю перевірку, це справді смішно.
DaniloDeQueiroz

У мене було те саме питання. У мене був EditText та кнопка "збереження" для зберігання вмісту EditText у базі даних. Він завжди виходив з ладу при натисканні кнопки "зберегти". Я підозрюю, що причина пов’язана з тим, що для того, щоб мати можливість натиснути кнопку «зберегти», мені потрібно позбутися екранної клавіатури, натиснувши кнопку назад.
The Fox

Це перевіряє наявність помилки, але це не вирішує проблему. Цікаво, що ця умова справджується, якщо навігаційна backstack стає порожньою з небажаних причин.
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?.getAction(resId) != null || currentDestination?.id != resIdмає вирішити його, але також може не охоплювати кожну справу.
wchristiansen

@wchristiansen, дякую за замітки. Я оновив код із використанням глобальних дій
Alex Nuts

@AlexNuts чудова відповідь. Я думаю, ви можете видалити ?: graph.getAction(resId)-> currentDestination?.getAction(resId)поверне дію як для глобальних, так і неглобальних дій (я тестував це). Крім того, було б краще, якщо ви скористалися Safe Args -> скоріше пройдіть, navDirections: NavDirectionsніж resIdі argsокремо.
Весс

@AlexNuts Зверніть увагу, що це рішення не підтримує перехід до того ж пункту призначення, що й поточний пункт призначення. Як правило, навігація від пункту призначення X із розшаруванням Y до місця призначення X за допомогою пучка Z неможлива.
Весс

18

Це також може статися, якщо у вас є фрагмент A з ViewPager фрагментів B, і ви намагаєтесь переходити з B до C

Оскільки у ViewPager фрагменти не є пунктом призначення A, ваш графік не знатиме, що ви перебуваєте на B.

Рішенням може бути використання ADirections в B для переходу до C


У цьому випадку аварія не відбувається щоразу, а відбувається лише рідко. Як це вирішити?
Шрікар Редді

Ви можете додати глобальну дію всередині navGraph і використовувати її для навігації
Авраам Матвій

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()методу замість цього, у мене знову не було аварії.


Я не розумію, в чому різниця між onBackPress і цим, все ще застряг за кнопкою повернення системи і переосмисленням її та заміною цього здається божевільним
Даніель Вілсон

2
Я згоден, це здається божевільним. Багато речей, з якими я стикався в компоненті архітектури андроїд навігації, відчуваються трохи божевільними, він налаштований занадто жорстко ІМО. Думаю зробити власну реалізацію для нашого проекту, оскільки це просто створює занадто багато головних болів
Ніл

Не працює для мене ... Все ж отримую ту саму помилку.
Otziii

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, але він не містить наступних пунктів призначення з ідентифікатором R.id.my_next_destination. Таким чином ви отримуєте IllegalArgumentExceptionчерез ідентифікатор, про який фрагмент нічого не знає.

Цю точну помилку можна знайти в NavController.javaметоді 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">

Виправлено проблему, додавши дію до фрагменту батьківського переглядача, як показано нижче:

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"

Проблема все ще існує. Мій підхід до Котліна такий:

// 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, щоб переконатися, що він унікальний. <item name="tag_navigation_destination_id" type="id" />

Більше інформації про помилку та рішення та navigateSafe(...)методи розширення у "Виправлення страху" ... не відоме цьому NavController "


Я вивчив кілька різних рішень цієї проблеми, і ваше, безумовно, найприємніше. Мені стає сумно бачити так мало кохання до цього
Лука

1
може бути корисним створити унікальний ідентифікатор замість NAV_DESTINATION_IDчогось подібного stackoverflow.com/a/15021758/1572848
William Reed

так, я оновив відповідь
Френк

Звідки береться тег і навіщо він потрібен? У мене проблеми, коли фактичні ідентифікатори на навігаційному компоненті не відповідають цим R.id.
riezebosch

R.id.tag_navigation_destination_idце лише ідентифікатор, який вам доведеться додати до свого ids.xml, щоб переконатися, що він унікальний. <item name="tag_navigation_destination_id" type="id" />
Френк

3

У моєму випадку у мене було декілька файлів навігаційного графіка, і я намагався перейти з місця розташування 1-го графіка до місця призначення в іншому наві-графіку.

Для цього ми повинні включити 2-й навігаційний графік у 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

У моєму випадку помилка виникла, тому що у мене відбулася навігаційна дія з параметрами Single Topта Clear Taskвключеними параметрами після сплеску екрана.


1
Але ClearTask застарілий, замість цього слід використовувати popUpTo ().
Джеррі Окафор

@ Po10cio Жоден із цих прапорів не був потрібний, я його просто зняв, і це було виправлено.
Eury Pérez Beltré

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)або керувати функціями контролера графіки для всіх ваших навігаційних дій.


2

Я вирішив ту саму проблему, поставивши перевірку перед навігацією замість кодової панелі для миттєвого натискання клавіші управління

 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



1

Я виловив цей виняток після деяких перейменованих занять. Наприклад: у мене був класи , які називаються FragmentAз @+is/fragment_aнавігацією графіка і FragmentBз @+id/fragment_b. Потім я видалив FragmentAі перейменував FragmentBна FragmentA. Таким чином , після цього вузла до FragmentAсих пір залишилися в навігаційному графіку, і android:nameз FragmentBвузла «S був перейменований 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 використовував навігацію за backstack fragmentManager, як показано нижче, і здається, що він неправильно взаємодіє з навігацією:

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

Якщо ви ввійдете в цю версію "поточного пункту призначення" перед тим, як перейти з основного фрагмента (фрагмент камери в цьому випадку), а потім знову ввійдете в систему, коли ви повернетесь до основного фрагмента, ви можете побачити з ідентифікатора в журналах, що ідентифікатор не те саме. За здогадкою, Навігація оновила його під час переміщення до фрагменту, і 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()
        }

Це працює правильно, і журнали показують той самий ідентифікатор, коли повертаються до основного фрагмента.

До : D / CameraXBasic: currentDest ?: androidx.navigation.fragment.FragmentNavigator$Destination@b713195

Після : D / CameraXBasic: currentDest ?: androidx.navigation.fragment.FragmentNavigator$Destination@b713195

Я підозрюю, що мораль цієї історії, принаймні в цей час, полягає в тому, щоб бути дуже обережним змішуючи Навігацію з навігацією на фрагменті.


Це звучить правдоподібно, я буду досліджувати далі. Хтось зміг перевірити чи обґрунтувати цю претензію?
Джеррі Окафор

@JerryOkafor - я перевірив це в додатку, над яким працював на основі зразка CameraX, і перевірив це, але було б добре перевірити, чи бачив це ще хтось. Я фактично пропустив "зворотну навігацію" в одному місці в тому ж додатку, тому нещодавно знову це виправили.
Мік

1

Смішний спосіб, але дуже потужний: просто назвіть це:

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

Просто створіть цей розширення:

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

1

Причин цієї проблеми може бути багато. У моєму випадку я використовував модель MVVM, і я спостерігав булеву для навігації, коли булева правда -> навігація в іншому не робить нічого, і це справно працює, але тут була одна помилка

при натисканні кнопки "назад" від фрагмента пункту призначення я зіткнувся з тією ж проблемою. І проблемою був бульний об'єкт, оскільки я забув змінити булеве значення на помилкове, це створило безлад. Я просто створив функцію в viewModel, щоб змінити її значення на false і закликав його відразу після findNavController ()


1

Зазвичай, коли це трапляється зі мною, у мене виникла проблема, описана Чарльзом Мадером: На одному і тому ж інтерфейсі запускаються дві навігаційні події, одна змінює поточневизначення, а інша виходить з ладу, оскільки змінено поточневизначення. Це може статися, якщо двічі торкнутись або натиснути два перегляди за допомогою слухача кліків, який викликає findNavController.navigate.

Тому для вирішення цього питання ви можете скористатись if-check, try-catch або якщо вас зацікавило, є findSafeNavController (), який робить це для вас перед навігацією. Він також має перевірку, щоб не забути про цю проблему.

GitHub

Стаття, що детально описує це питання


1

Якщо ви натиснете на нього занадто швидко, це спричинить нуль і збій.

Ми можемо використовувати RxBinding lib, щоб допомогти у цьому. Ви можете додати газ і тривалість натискання, перш ніж це станеться.

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

Ці статті про дроселювання на Android можуть допомогти. Ура!


1

Якщо ви використовуєте переробник перегляду, просто додайте прослуховування клацання слухача на натискання, а також у використанні xml-файлу recilerlerview 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)

0

Це сталося зі мною, моє питання - я натискав на FAB tab item fragment. Я намагався перейти від одного фрагмента елемента вкладки до another fragment.

Але згідно з Ієном Лейком у цій відповіді ми маємо використовувати tablayoutі viewpager, не, підтримку навігаційних компонентів . Через це не існує навігаційного шляху від таблэйту, що містить фрагмент, до фрагмента елемента вкладки.

колишній:

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

Рішенням було створити шлях від макета вкладки, що містить фрагмент, до призначеного фрагмента ex: path: container fragment -> another fragment

Недолік:

  • Nav-графік більше не відображає потік користувачів точно.

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)
        }
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.