Tengo problemas con el nuevo componente de Arquitectura de navegación de Android cuando intento navegar de un Fragmento a otro , obtengo este extraño error:
java.lang.IllegalArgumentException: navigation destination XXX
is unknown to this NavController
Cualquier otra navegación funciona bien, excepto esta en particular.
Uso la findNavController()función de Fragmento para obtener acceso a NavController.
Cualquier ayuda será apreciada.
java
android
kotlin
android-navigation
android-architecture-navigation
Jerry Okafor
fuente
fuente

Respuestas:
En mi caso, si el usuario hace clic en la misma vista dos veces muy rápidamente, se producirá este bloqueo. Por lo tanto, debe implementar algún tipo de lógica para evitar múltiples clics rápidos ... Lo cual es muy molesto, pero parece ser necesario.
Puede leer más sobre cómo prevenir esto aquí: Android Prevención de doble clic en un botón
Edición 19/03/2019 : Solo para aclarar un poco más, este bloqueo no es exclusivamente reproducible simplemente "haciendo clic en la misma vista dos veces muy rápidamente". Alternativamente, puede usar dos dedos y hacer clic en dos (o más) vistas al mismo tiempo, donde cada vista tiene su propia navegación que realizarían. Esto es especialmente fácil de hacer cuando tienes una lista de artículos. La información anterior sobre prevención de clics múltiples manejará este caso.
Edición 16/04/2020 : en caso de que no estés terriblemente interesado en leer esa publicación de Stack Overflow anterior, incluyo mi propia solución (Kotlin) que he estado usando durante mucho tiempo.
OnSingleClickListener.kt
ViewExt.kt
HomeFragment.kt
fuente
Verificar
currentDestinationantes de llamar a navegar puede ser útil.Por ejemplo, si tiene dos destinos de fragmentos en el gráfico de navegación
fragmentAyfragmentB, y solo hay una acción desdefragmentAhastafragmentB. las llamadasnavigate(R.id.action_fragmentA_to_fragmentB)resultaránIllegalArgumentExceptioncuando ya estabas activadofragmentB. Por lo tanto, siempre debe verificarcurrentDestinationantes de navegar.fuente
Puede verificar la acción solicitada en el destino actual del controlador de navegación.
ACTUALIZAR el uso agregado de acciones globales para una navegación segura.
fuente
currentDestinationlista de acciones de. Supongamos que tiene una acción global definida y use esa acción para navegar. Esto fallará porque la acción no está definida en la lista <action> de currentDestination. Agregar un cheque comocurrentDestination?.getAction(resId) != null || currentDestination?.id != resIddebería resolverlo, pero también podría no cubrir todos los casos.?: graph.getAction(resId)->currentDestination?.getAction(resId)devolverá una acción para acciones globales o no globales (lo he probado). Además, sería mejor si se hizo uso de Args Safe -> en lugar de pasarnavDirections: NavDirectionsderesIdyargspor separado.También podría suceder si tiene un Fragmento A con un ViewPager de Fragmentos B e intenta navegar de B a C
Como en ViewPager los fragmentos no son un destino de A, su gráfico no sabría que está en B.
Una solución puede ser usar ADirections en B para navegar a C
fuente
(parentFragment as? XActionListener)?.Xaction()y tenga en cuenta que podría mantener esta función como una variable local si eso es útilLo que hice para evitar el bloqueo es lo siguiente:
Tengo un BaseFragment, allí agregué esto
funpara asegurarme de quedestinationes conocido porcurrentDestination:Vale la pena señalar que estoy usando el complemento SafeArgs .
fuente
En mi caso, estaba usando un botón de retroceso personalizado para navegar hacia arriba. Llamé
onBackPressed()en lugar del siguiente códigoEsto hizo
IllegalArgumentExceptionque ocurriera. Después de cambiarlo para usar elnavigateUp()método en su lugar, no volví a tener un bloqueo.fuente
TL; DR Envuelva sus
navigatellamadas contry-catch(forma simple), o asegúrese de que solo haya una llamadanavigateen un corto período de tiempo. Es probable que este problema no desaparezca. Copie un fragmento de código más grande en su aplicación y pruébelo.Hola. Basado en un par de respuestas útiles anteriores, me gustaría compartir mi solución que se puede ampliar.
Aquí está el código que causó este bloqueo en mi aplicación:
Una forma de reproducir fácilmente el error es tocar con varios dedos en la lista de elementos donde el clic en cada elemento se resuelve en la navegación a la nueva pantalla (básicamente lo mismo que la gente notó: dos o más clics en un período de tiempo muy corto ) Me di cuenta que:
navigateinvocación siempre funciona bien;navigatemétodo se resuelven enIllegalArgumentException.Desde mi punto de vista, esta situación puede aparecer con mucha frecuencia. Como repetir código es una mala práctica y siempre es bueno tener un punto de influencia, pensé en la siguiente solución:
}
Y así, el código anterior cambia solo en una línea desde esto:
a esto:
Incluso se hizo un poco más corto. El código se probó en el lugar exacto donde ocurrió el accidente. Ya no lo experimenté y usaré la misma solución para otras navegaciones para evitar el mismo error.
Cualquier idea es bienvenida!
¿Qué causa exactamente el accidente?
Recuerde que aquí trabajamos con el mismo gráfico de navegación, controlador de navegación y back-stack cuando usamos el método
Navigation.findNavController.Siempre obtenemos el mismo controlador y gráfico aquí. Cuando
navigate(R.id.my_next_destination)se llama gráfico y el back-stack cambia casi instantáneamente mientras la interfaz de usuario aún no se actualiza. Simplemente no lo suficientemente rápido, pero eso está bien. Después de que el back-stack ha cambiado, el sistema de navegación recibe la segundanavigate(R.id.my_next_destination)llamada. Dado que el back-stack ha cambiado, ahora operamos en relación con el fragmento superior de la pila. El fragmento superior es el fragmento al que navega utilizandoR.id.my_next_destination, pero no contiene más destinos con IDR.id.my_next_destination. Por lo tanto, se obtieneIllegalArgumentExceptiondebido a la identificación de la que el fragmento no sabe nada.Este error exacto se puede encontrar en el
NavController.javamétodofindDestination.fuente
En mi caso, el problema ocurrió cuando había reutilizado uno de mis Fragmentos dentro de un
viewpagerfragmento como hijo delviewpager. ElviewpagerFragmento (que era el fragmento primario) se agregó en el xml de navegación, pero la acción no se agregó en elviewpagerfragmento primario.Se solucionó el problema agregando la acción al fragmento del visor principal también como se muestra a continuación:
fuente
Hoy
El problema aún existe. Mi enfoque sobre Kotlin es:
fuente
Puede verificar antes de navegar si el Fragmento que solicita la navegación sigue siendo el destino actual, tomado de esta esencia .
Básicamente establece una etiqueta en el fragmento para una búsqueda posterior.
R.id.tag_navigation_destination_ides solo una identificación que tendrá que agregar a su ids.xml, para asegurarse de que sea única.<item name="tag_navigation_destination_id" type="id" />Más información sobre el error y la solución, y
navigateSafe(...)métodos de extensión en "Arreglar el temido" ... es desconocido para este NavController "fuente
NAV_DESTINATION_IDalgo como esto stackoverflow.com/a/15021758/1572848R.id.R.id.tag_navigation_destination_ides solo una identificación que tendrá que agregar a su ids.xml, para asegurarse de que sea única.<item name="tag_navigation_destination_id" type="id" />En mi caso, tenía varios archivos de gráficos de navegación e intentaba moverme de una ubicación de gráfico de navegación a un destino en otro gráfico de navegación.
Para esto tenemos que incluir el segundo gráfico de navegación en el primero como este
y agrega esto a tu acción:
donde
second_graphesta:en el segundo gráfico.
Más información aquí.
fuente
En mi caso, el error ocurrió porque tuve una acción de navegación con
Single Topy lasClear Taskopciones habilitadas después de una pantalla de inicio.fuente
Recibí este mismo error porque usé un cajón de navegación y
getSupportFragmentManager().beginTransaction().replace( )al mismo tiempo en algún lugar de mi código.Me deshice del error usando esta condición (probando si el destino):
En mi caso, el error anterior se activó cuando estaba haciendo clic en las opciones del cajón de navegación. Básicamente, el código anterior ocultó el error, porque en mi código en algún lugar usé la navegación usando
getSupportFragmentManager().beginTransaction().replace( )la condición:nunca fue alcanzado porque
(Navigation.findNavController(v).getCurrentDestination().getId()siempre estaba apuntando al fragmento de la casa. Solo debe usarNavigation.findNavController(v).navigate(R.id.your_action)o navegar por las funciones del controlador de gráficos para todas sus acciones de navegación.fuente
Parece que estás despejando la tarea. Una aplicación puede tener una configuración única o una serie de pantallas de inicio de sesión. Estas pantallas condicionales no deben considerarse el destino inicial de su aplicación.
https://developer.android.com/topic/libraries/architecture/navigation/navigation-conditional
fuente
Capté esta excepción después de algunos cambios de nombre de clases. Por ejemplo: tuve clases llamadas
FragmentAcon@+is/fragment_aen el gráfico de navegación yFragmentBcon@+id/fragment_b. Luego borréFragmentAy cambié el nombreFragmentBaFragmentA. Entonces, después de que el nodo deFragmentAtodavía se quedó en el gráfico de navegación, y el nodoandroid:namedeFragmentB'' cambió su nombrepath.to.FragmentA. Tenía dos nodos con el mismoandroid:namey diferenteandroid:id, y la acción que necesitaba se definió en el nodo de la clase eliminada.fuente
Se me ocurre cuando presiono el botón Atrás dos veces. Al principio, intercepto
KeyListenery anuloKeyEvent.KEYCODE_BACK. Agregué el siguiente código en la función nombradaOnResumepara el Fragmento, y luego esta pregunta / problema está resuelto.Cuando me sucede por segunda vez, y su estado es el mismo que el primero, encuentro que tal vez use la
adsurdfunción. Analicemos estas situaciones.En primer lugar, FragmentA navega a FragmentB, luego FragmentB navega a FragmentA, luego presiona el botón Atrás ... aparece el bloqueo.
En segundo lugar, FragmentA navega a FragmentB, luego FragmentB navega a FragmentC, FragmentC navega a FragmentA, luego presiona el botón Atrás ... aparece el bloqueo.
Así que creo que al presionar el botón Atrás, FragmentA volverá a FragmentB o FragmentC, luego causa el desorden de inicio de sesión. Finalmente, encuentro que la función nombrada
popBackStackse puede usar para retroceder en lugar de navegar.Hasta ahora, el problema está realmente resuelto.
fuente
Parece que mezclar el control fragmentManager del backstack y el control de la arquitectura de navegación del backstack también puede causar este problema.
Por ejemplo, la muestra básica original de CameraX utilizaba la navegación backstack fragmentManager como se muestra a continuación y parece que no interactúa correctamente con Navigation:
Si registra el 'destino actual' con esta versión antes de pasar del fragmento principal (el fragmento de la cámara en este caso) y luego lo registra nuevamente cuando regrese al fragmento principal, puede ver desde la identificación en los registros que la identificación no es lo mismo. Supuestamente, la Navegación lo actualizó cuando se movió al fragmento y el fragmntManager no lo actualizó nuevamente al regresar. De los registros:
La versión actualizada de la muestra básica de CameraX usa la navegación para regresar así:
Esto funciona correctamente y los registros muestran la misma identificación cuando vuelven al fragmento principal.
Sospecho que la moraleja de la historia, al menos en este momento, es tener mucho cuidado al mezclar Navigation con fragmentManager navigation.
fuente
Una forma ridícula pero muy poderosa es: simplemente llame a esto:
Simplemente cree esta extensión:
fuente
Podría haber muchas razones para este problema. En mi caso, estaba usando el modelo MVVM y estaba observando un booleano para la navegación cuando el booleano es verdadero -> navegar de lo contrario no hace nada y esto funcionaba bien, pero hubo un error aquí
al presionar el botón Atrás desde el fragmento de destino, me encontraba con el mismo problema. Y el problema era el objeto booleano, ya que olvidé cambiar el valor booleano a falso, esto creó el desorden. Acabo de crear una función en viewModel para cambiar su valor a falso y lo llamó justo después de findNavController ()
fuente
Por lo general, cuando esto me sucede, tuve el problema descrito por Charles Madere: dos eventos de navegación activados en la misma interfaz de usuario, uno que cambia el CurrentDestination y el otro que falla porque el currentDestination ha cambiado. Esto puede suceder si toca dos veces o hace clic en dos vistas con un escucha de clics que llama a findNavController.navigate.
Por lo tanto, para resolver esto, puede usar if-cheques, try-catch o si está interesado, hay un findSafeNavController () que verifica esto por usted antes de navegar. También tiene un control de pelusa para asegurarse de que no se olvide de este problema.
GitHub
Artículo que detalla el problema
fuente
Si hace clic demasiado rápido, causará nulo y fallará.
Podemos usar RxBinding lib para ayudar en esto. Puede agregar aceleración y duración en el clic antes de que ocurra.
Estos artículos sobre la limitación en Android pueden ser útiles. ¡Salud!
fuente
Si está utilizando una vista de reciclaje simplemente agregue un enfriamiento de escucha de clics en su clic y también en el uso de su archivo xml de reciclaje
android:splitMotionEvents="false"fuente
Después de pensar en el consejo de Ian Lake en este hilo de Twitter, se me ocurrió el siguiente enfoque. Habiendo
NavControllerWrapperdefinido como tal:Luego en el código de navegación:
fuente
He resuelto el mismo problema poniendo check antes de navegar en lugar de código repetitivo para hacer clic al instante
de acuerdo con esta respuesta
https://stackoverflow.com/a/56168225/7055259
fuente
Esto me sucedió, mi problema era que estaba haciendo clic en un FAB
tab item fragment. Estaba tratando de navegar desde uno de los fragmentos de elemento de pestaña aanother fragment.Pero de acuerdo con Ian Lago en esta respuesta tenemos que usar
tablayoutyviewpager, sin ayuda componente de navegación . Debido a esto, no hay una ruta de navegación desde tablayout que contenga un fragmento a un fragmento de elemento de pestaña.ex:
La solución fue crear una ruta desde el diseño de pestaña que contiene el fragmento al fragmento deseado, por ejemplo: ruta:
container fragment -> another fragmentDesventaja:
fuente
En mi caso, recibí ese error cuando intenté navegar desde otro hilo, en el 50% de los casos. Ejecutar el código en el hilo principal ayuda
fuente
En mi caso, esto ocurrió cuando accidentalmente agregué un
+destino en acción, y el bloqueo solo ocurrió cuando fui al mismo fragmento varias veces.La solución es eliminar
+del destino de la acción, usar solo en@id/profileFragmentlugar de@+id/profileFragmentfuente
Solución actualizada @Alex Nuts
Si no hay acción para un fragmento en particular y desea navegar para fragmentar
fuente
Escribí estas extensiones
fuente
Creé esta función de extensión para Fragment:
fuente