Estou tendo problemas com o novo componente da arquitetura de navegação do Android quando tento navegar de um fragmento para outro , recebo este erro estranho:
java.lang.IllegalArgumentException: navigation destination XXX
is unknown to this NavController
Qualquer outra navegação funciona bem, exceto esta em particular.
Eu uso a findNavController()
função de fragmento para obter acesso ao NavController
.
Qualquer ajuda será apreciada.
java
android
kotlin
android-navigation
android-architecture-navigation
Jerry Okafor
fonte
fonte
Respostas:
No meu caso, se o usuário clicar na mesma exibição duas vezes muito rapidamente, essa falha ocorrerá. Portanto, você precisa implementar algum tipo de lógica para evitar vários cliques rápidos ... O que é muito irritante, mas parece ser necessário.
Você pode ler mais sobre como evitar isso aqui: Android Impedindo o clique duplo em um botão
Editar 19/03/2019 : Apenas para esclarecer um pouco mais, esta falha não é reproduzível exclusivamente apenas "clicando na mesma visualização duas vezes muito rapidamente". Como alternativa, você pode usar apenas dois dedos e clicar em duas (ou mais) vistas ao mesmo tempo, onde cada uma tem sua própria navegação que executaria. Isso é especialmente fácil de fazer quando você tem uma lista de itens. As informações acima sobre prevenção de múltiplos cliques tratam desse caso.
Edit 16/4/2020 : Caso você não esteja muito interessado em ler a publicação Stack Overflow acima, estou incluindo minha própria solução (Kotlin) que uso há muito tempo.
OnSingleClickListener.kt
ViewExt.kt
HomeFragment.kt
fonte
Verifique
currentDestination
antes de ligar para navegar pode ser útil.Por exemplo, se você tiver dois destinos de fragmento no gráfico de navegação
fragmentA
efragmentB
, e houver apenas uma ação defragmentA
parafragmentB
. a chamadanavigate(R.id.action_fragmentA_to_fragmentB)
resultará emIllegalArgumentException
quando você já estava ligadofragmentB
. Portanto, você deve sempre verificar ocurrentDestination
antes de navegar.fonte
Você pode verificar a ação solicitada no destino atual do controlador de navegação.
ATUALIZAÇÃO adicionou o uso de ações globais para navegação segura.
fonte
currentDestination
lista de ações da. Digamos que você tenha uma ação global definida e use essa ação para navegar. Isso falhará porque a ação não está definida na lista <action> do currentDestination. Adicionar um cheque comocurrentDestination?.getAction(resId) != null || currentDestination?.id != resId
deve resolvê-lo, mas também pode não cobrir todos os casos.?: graph.getAction(resId)
->currentDestination?.getAction(resId)
retornará uma ação para ações globais ou não globais (eu testei). Além disso, seria melhor se você fez uso de Args seguro -> em vez passar emnavDirections: NavDirections
queresId
eargs
separadamente.Também pode acontecer se você tiver um fragmento A com um ViewPager dos fragmentos B e tentar navegar de B para C
Como no ViewPager os fragmentos não são um destino de A, seu gráfico não saberia que você está em B.
Uma solução pode ser usar ADirections em B para navegar até C
fonte
(parentFragment as? XActionListener)?.Xaction()
e observe que você poderia realizar essa função como uma variável local se isso é útilO que fiz para impedir a falha é o seguinte:
Eu tenho um BaseFragment, lá eu adicionei isso
fun
para garantir que odestination
é conhecido pelocurrentDestination
:Vale a pena notar que estou usando o plugin SafeArgs .
fonte
No meu caso, eu estava usando um botão voltar personalizado para navegar para cima. Liguei
onBackPressed()
no lugar do código a seguirIsso causou a
IllegalArgumentException
ocorrência. Depois que eu mudei para usar onavigateUp()
método em vez disso, não tive mais uma falha.fonte
TL; DR Encerre suas
navigate
chamadas comtry-catch
(maneira simples) ou verifique se haverá apenas uma chamadanavigate
em um curto período de tempo. Esse problema provavelmente não desaparecerá. Copie um snippet de código maior no seu aplicativo e experimente.Olá. Com base em algumas respostas úteis acima, gostaria de compartilhar minha solução que pode ser estendida.
Aqui está o código que causou essa falha no meu aplicativo:
Uma maneira de reproduzir facilmente o bug é tocar com vários dedos na lista de itens em que o clique em cada item é resolvido na navegação para a nova tela (basicamente o mesmo que as pessoas notaram - dois ou mais cliques em um período muito curto de tempo ) Eu percebi isso:
navigate
invocação sempre funciona bem;navigate
método são resolvidasIllegalArgumentException
.Do meu ponto de vista, essa situação pode aparecer com muita frequência. Como a repetição de código é uma prática ruim e sempre é bom ter um ponto de influência, pensei na próxima solução:
}
E, portanto, o código acima muda apenas em uma linha a partir disso:
para isso:
Até ficou um pouco mais curto. O código foi testado no local exato em que a falha ocorreu. Não o experimentou mais e usará a mesma solução para outras navegações para evitar ainda mais o mesmo erro.
Quaisquer pensamentos são bem-vindos!
O que exatamente causa o acidente
Lembre-se que aqui trabalhamos com o mesmo gráfico de navegação, controlador de navegação e volta-stack quando usamos método
Navigation.findNavController
.Sempre temos o mesmo controlador e gráfico aqui. Quando
navigate(R.id.my_next_destination)
é chamado gráfico e a pilha traseira muda quase instantaneamente enquanto a interface do usuário ainda não está atualizada. Apenas não é rápido o suficiente, mas tudo bem. Após a troca da pilha traseira, o sistema de navegação recebe a segundanavigate(R.id.my_next_destination)
chamada. Como a pilha traseira mudou, agora operamos em relação ao fragmento superior da pilha. O fragmento superior é o fragmento para o qual você navega usandoR.id.my_next_destination
, mas não contém outros destinos com IDR.id.my_next_destination
. Assim, você obtémIllegalArgumentException
o ID que o fragmento não conhece.Este erro exata pode ser encontrada no
NavController.java
métodofindDestination
.fonte
No meu caso, o problema ocorreu quando eu reutilizei um dos meus fragmentos dentro de um
viewpager
fragmento como filho doviewpager
. Oviewpager
fragmento (que era o fragmento pai) foi adicionado no xml Navigation, mas a ação não foi adicionada noviewpager
fragmento pai.Corrigido o problema adicionando a ação ao fragmento do viewpager pai também como mostrado abaixo:
fonte
Hoje
O problema ainda existe. Minha abordagem sobre Kotlin é:
fonte
Você pode verificar antes de navegar se o fragmento que solicita a navegação ainda é o destino atual, retirado dessa essência .
Basicamente, define uma tag no fragmento para pesquisa posterior.
R.id.tag_navigation_destination_id
é apenas um ID que você precisará adicionar ao seu ids.xml, para garantir que ele seja único.<item name="tag_navigation_destination_id" type="id" />
Mais informações sobre o bug e a solução e os
navigateSafe(...)
métodos de extensão em "Corrigindo o temido"… é desconhecido para este NavController ”fonte
NAV_DESTINATION_ID
algo como este stackoverflow.com/a/15021758/1572848R.id
.R.id.tag_navigation_destination_id
é apenas um ID que você precisará adicionar ao seu ids.xml, para garantir que ele seja único.<item name="tag_navigation_destination_id" type="id" />
No meu caso, eu tinha vários arquivos de gráfico de navegação e estava tentando passar de 1 local do gráfico de navegação para um destino em outro gráfico de navegação.
Para isso, temos que incluir o segundo gráfico de navegação no primeiro como este
e adicione isso à sua ação:
onde
second_graph
é:no segundo gráfico.
Mais informações aqui
fonte
No meu caso, o bug ocorreu porque eu tive uma ação de navegação com
Single Top
asClear Task
opções e ativadas após uma tela inicial.fonte
Eu recebi esse mesmo erro porque usei uma gaveta de navegação e
getSupportFragmentManager().beginTransaction().replace( )
, ao mesmo tempo, em algum lugar do meu código.Eu me livrei do erro usando esta condição (testando se o destino):
No meu caso, o erro anterior foi acionado quando eu estava clicando nas opções da gaveta de navegação. Basicamente, o código acima ocultou o erro, porque em meu código em algum lugar eu usei a navegação usando
getSupportFragmentManager().beginTransaction().replace( )
A condição -nunca foi alcançado porque
(Navigation.findNavController(v).getCurrentDestination().getId()
estava sempre poitando para o fragmento de casa. Você deve usarNavigation.findNavController(v).navigate(R.id.your_action)
ou navegar apenas nas funções do controlador gráfico para todas as suas ações de navegação.fonte
Parece que você está limpando a tarefa. Um aplicativo pode ter uma configuração única ou uma série de telas de login. Essas telas condicionais não devem ser consideradas o destino inicial do seu aplicativo.
https://developer.android.com/topic/libraries/architecture/navigation/navigation-conditional
fonte
Eu peguei essa exceção depois de algumas renomeações de classes. Por exemplo: eu tive aulas chamadas
FragmentA
com@+is/fragment_a
no gráfico de navegação eFragmentB
com@+id/fragment_b
. Então eu apagueiFragmentA
e renomeiFragmentB
paraFragmentA
. Então, depois que o nó deFragmentA
ainda ficou no gráfico de navegação eandroid:name
deFragmentB
nó 's foi renomeadopath.to.FragmentA
. Eu tinha dois nós com o mesmoandroid:name
e diferentesandroid:id
, e a ação necessária foi definida no nó da classe removida.fonte
Ocorre-me quando pressiono o botão voltar duas vezes. No começo, intercepto
KeyListener
e anuloKeyEvent.KEYCODE_BACK
. Adicionei o código abaixo na função nomeadaOnResume
para o fragmento e, em seguida, esta questão / problema foi resolvido.Quando acontece comigo pela segunda vez, e seu status é o mesmo que o primeiro, acho que talvez eu use a
adsurd
função Vamos analisar essas situações.Primeiramente, o FragmentA navega para o FragmentB, depois o FragmentB navega para o FragmentA e, em seguida, pressione o botão Voltar ... a falha aparece.
Em segundo lugar, o FragmentA navega para o FragmentB, depois o FragmentB navega para o FragmentC, o FragmentC navega para o FragmentA e, em seguida, pressione o botão Voltar ... a falha aparece.
Então, ao pressionar o botão Voltar, o FragmentA retornará ao FragmentB ou FragmentC, causando a bagunça do login. Finalmente, acho que a função nomeada
popBackStack
pode ser usada para voltar em vez de navegar.Até agora, o problema está realmente resolvido.
fonte
Parece que a mistura do controle fragmentManager do backstack e do controle Architecture Architecture do backstack também pode causar esse problema.
Por exemplo, a amostra básica original do CameraX usou a navegação de backstack fragmentManager como abaixo e parece que não interage corretamente com o Navigation:
Se você registrar o 'destino atual' com esta versão antes de sair do fragmento principal (neste caso, o fragmento da câmera) e depois registrá-lo novamente quando retornar ao fragmento principal, poderá ver no id nos logs que o id Não é a mesma coisa. De uma maneira adivinhada, o Navigation o atualizou ao mover para o fragmento e o fragmntManager não o atualizou novamente ao voltar. Dos logs:
A versão atualizada do exemplo básico do CameraX usa o Navigation para retornar assim:
Isso funciona corretamente e os logs mostram o mesmo ID quando voltamos ao fragmento principal.
Suspeito que a moral da história, pelo menos nesse momento, seja muito cuidadosa misturando Navegação com navegação fragmentManager.
fonte
Uma maneira ridícula mas muito poderosa é: Simplesmente chame isto:
Basta criar esta extensão:
fonte
Pode haver muitas razões para esse problema. No meu caso, eu estava usando o modelo MVVM e estava observando um booleano para navegação quando o booleano é verdadeiro -> navegar mais não faz nada e isso estava funcionando bem, mas houve um erro aqui
Ao pressionar o botão voltar do fragmento de destino, eu estava encontrando o mesmo problema. E o problema era o objeto booleano, pois esqueci de alterar o valor booleano para false, isso criou a bagunça. Acabei de criar uma função no viewModel para alterar seu valor para false e chamou logo após o findNavController ()
fonte
Normalmente, quando isso acontece comigo, tive o problema descrito por Charles Madere: Dois eventos de navegação acionados na mesma interface do usuário, um alterando o currentDestination e o outro falhando porque o currentDestination é alterado. Isso pode acontecer se você clicar duas vezes ou clicar em duas visualizações com um ouvinte de clique chamando findNavController.navigate.
Portanto, para resolver isso, você pode usar if-checks, try-catch ou se estiver interessado, há um findSafeNavController () que faz isso para você antes de navegar. Ele também possui uma verificação de fiapos para garantir que você não se esqueça desse problema.
GitHub
Artigo detalhando a questão
fonte
Se você clicar rápido demais, isso causará nulo e falha.
Podemos usar RxBinding lib para ajudar nisso. Você pode adicionar acelerador e duração ao clique antes que isso aconteça.
Estes artigos sobre limitação no Android podem ajudar. Felicidades!
fonte
Se você estiver usando uma visualização de reciclagem, adicione um cooldown do ouvinte de clique no seu clique e também no seu arquivo xml de reciclagem
android:splitMotionEvents="false"
fonte
Depois de pensar nos conselhos de Ian Lake neste tópico do twitter, vim com a seguinte abordagem. Tendo
NavControllerWrapper
definido como tal:Em seguida, no código de navegação:
fonte
Eu resolvi o mesmo problema colocando check antes de navegar em vez do código padrão para clicar instantaneamente no controle
de acordo com esta resposta
https://stackoverflow.com/a/56168225/7055259
fonte
Isso aconteceu comigo, meu problema era que eu estava clicando em um FAB
tab item fragment
. Eu estava tentando navegar de um fragmento de item da guia paraanother fragment
.Mas de acordo com Ian Lake em esta resposta temos que usar
tablayout
eviewpager
, sem suporte componente de navegação . Por esse motivo, não há caminho de navegação do tablayout contendo fragmento para o fragmento do item de tabulação.ex:
A solução foi criar um caminho a partir do layout da guia contendo fragmento para o fragmento pretendido, ex: path:
container fragment -> another fragment
Desvantagem:
fonte
No meu caso, recebi esse erro ao tentar navegar de outro segmento, em 50% dos casos. Executar o código no thread principal ajuda
fonte
No meu caso, isso ocorreu quando adicionei acidentalmente um
+
destino em ação, e a falha só ocorreu quando fui ao mesmo fragmento várias vezes.A solução é remover
+
do destino da ação, use apenas em@id/profileFragment
vez de@+id/profileFragment
fonte
Solução @Alex Nuts atualizada
Se não houver ação para um fragmento específico e desejar navegar para o fragmento
fonte
Eu escrevi estas extensões
fonte
Eu criei esta função de extensão para o Fragment:
fonte