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
currentDestination
antes de llamar a navegar puede ser útil.Por ejemplo, si tiene dos destinos de fragmentos en el gráfico de navegación
fragmentA
yfragmentB
, y solo hay una acción desdefragmentA
hastafragmentB
. las llamadasnavigate(R.id.action_fragmentA_to_fragmentB)
resultaránIllegalArgumentException
cuando ya estabas activadofragmentB
. Por lo tanto, siempre debe verificarcurrentDestination
antes 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
currentDestination
lista 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 != resId
deberí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: NavDirections
deresId
yargs
por 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
fun
para asegurarme de quedestination
es 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
IllegalArgumentException
que ocurriera. Después de cambiarlo para usar elnavigateUp()
método en su lugar, no volví a tener un bloqueo.fuente
TL; DR Envuelva sus
navigate
llamadas contry-catch
(forma simple), o asegúrese de que solo haya una llamadanavigate
en 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:
navigate
invocación siempre funciona bien;navigate
mé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 obtieneIllegalArgumentException
debido a la identificación de la que el fragmento no sabe nada.Este error exacto se puede encontrar en el
NavController.java
métodofindDestination
.fuente
En mi caso, el problema ocurrió cuando había reutilizado uno de mis Fragmentos dentro de un
viewpager
fragmento como hijo delviewpager
. Elviewpager
Fragmento (que era el fragmento primario) se agregó en el xml de navegación, pero la acción no se agregó en elviewpager
fragmento 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_id
es 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_ID
algo como esto stackoverflow.com/a/15021758/1572848R.id
.R.id.tag_navigation_destination_id
es 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_graph
esta: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 Top
y lasClear Task
opciones 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
FragmentA
con@+is/fragment_a
en el gráfico de navegación yFragmentB
con@+id/fragment_b
. Luego borréFragmentA
y cambié el nombreFragmentB
aFragmentA
. Entonces, después de que el nodo deFragmentA
todavía se quedó en el gráfico de navegación, y el nodoandroid:name
deFragmentB
'' cambió su nombrepath.to.FragmentA
. Tenía dos nodos con el mismoandroid:name
y 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
KeyListener
y anuloKeyEvent.KEYCODE_BACK
. Agregué el siguiente código en la función nombradaOnResume
para 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
adsurd
funció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
popBackStack
se 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
NavControllerWrapper
definido 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
tablayout
yviewpager
, 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 fragment
Desventaja:
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/profileFragment
lugar de@+id/profileFragment
fuente
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