Eu tenho um problema enorme com o modo como o backstack do fragmento android parece funcionar e ficaria muito grato por qualquer ajuda oferecida.
Imagine que você tem 3 fragmentos
[1] [2] [3]
Quero que o usuário possa navegar, [1] > [2] > [3]
mas no caminho de volta (pressionando o botão Voltar) [3] > [1]
.
Como eu imaginava, isso seria realizado se não fosse chamada addToBackStack(..)
ao criar a transação que traz fragmento [2]
para o detentor de fragmento definido em XML.
A realidade disso parece que, se eu não quiser [2]
aparecer novamente quando o usuário pressionar o botão Voltar [3]
, não devo chamar addToBackStack
a transação que mostra o fragmento [3]
. Isso parece completamente contra-intuitivo (talvez proveniente do mundo iOS).
De qualquer forma, se eu fizer dessa maneira, quando eu for [1] > [2]
e pressionar, chego de volta [1]
como esperado.
Se eu for [1] > [2] > [3]
e depois pressionar, pulo de volta para [1]
(como esperado). Agora o comportamento estranho acontece quando tento pular de [2]
novo para [1]
. Antes de tudo, [3]
é exibido brevemente antes de aparecer [2]
. Se eu pressionar novamente neste momento, [3]
será exibido e se eu pressionar novamente, o aplicativo será encerrado.
Alguém pode me ajudar a entender o que está acontecendo aqui?
E aqui está o arquivo xml de layout para minha atividade principal:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<fragment
android:id="@+id/headerFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
class="com.fragment_test.FragmentControls" >
<!-- Preview: layout=@layout/details -->
</fragment>
<FrameLayout
android:id="@+id/detailFragment"
android:layout_width="match_parent"
android:layout_height="fill_parent"
/>
Atualizar Este é o código que estou usando para criar pela hierarquia de navegação
Fragment frag;
FragmentTransaction transaction;
//Create The first fragment [1], add it to the view, BUT Dont add the transaction to the backstack
frag = new Fragment1();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.commit();
//Create the second [2] fragment, add it to the view and add the transaction that replaces the first fragment to the backstack
frag = new Fragment2();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.addToBackStack(null);
transaction.commit();
//Create third fragment, Dont add this transaction to the backstack, because we dont want to go back to [2]
frag = new Fragment3();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.commit();
//END OF SETUP CODE-------------------------
//NOW:
//Press back once and then issue the following code:
frag = new Fragment2();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.addToBackStack(null);
transaction.commit();
//Now press back again and you end up at fragment [3] not [1]
Muito Obrigado
fonte
Respostas:
Explicação: sobre o que está acontecendo aqui?
Se tivermos em mente que
.replace()
é igual ao.remove().add()
que sabemos pela documentação:então o que está acontecendo é assim (estou adicionando números à frag para deixar mais claro):
(aqui todas as coisas enganosas começam a acontecer)
Lembre-se de que
.addToBackStack()
está salvando apenas a transação e não o fragmento como ele mesmo! Então agora temosfrag3
o layout:Solução possível
Considere implementar
FragmentManager.BackStackChangedListener
para observar as mudanças na pilha traseira e aplicar sua lógica noonBackStackChanged()
método:FragmentTransaction.addToBackStack(String name);
fonte
FragmentManager.BackStackChangedListener
para observar as alterações na pilha de trás. Monitore todas as suas transações com oonBackStackChanged()
método e aja conforme necessário: por ex. rastrear uma contagem de transações no BackStack; verifique transação específica por nome (FragmentTransaction addToBackStack (String name)
) etc.Certo!!! depois de muito puxar o cabelo, finalmente descobri como fazer isso funcionar corretamente.
Parece que o fragmento [3] não é removido da visualização quando a tecla é pressionada, então você deve fazê-lo manualmente!
Primeiro de tudo, não use replace (), mas use remove e adicione separadamente. Parece que replace () não funciona corretamente.
A próxima parte disso é substituir o método onKeyDown e remover o fragmento atual toda vez que o botão Voltar é pressionado.
Espero que isto ajude!
fonte
Antes de tudo, agradeço à @Arvis por uma explicação que abre os olhos.
Prefiro uma solução diferente à resposta aceita aqui para este problema. Eu não gosto de mexer com o comportamento anulador de volta mais do que o absolutamente necessário e quando tentei adicionar e remover fragmentos por conta própria sem que a pilha traseira padrão aparecesse quando o botão Voltar fosse pressionado, eu me encontrei no inferno :) Se você. adicione f2 sobre f1 quando você o remover f1 não chamará nenhum dos métodos de retorno de chamada, como onResume, onStart etc. e isso pode ser muito lamentável.
De qualquer forma, é assim que eu faço:
Atualmente em exibição, é apenas o fragmento f1.
f1 -> f2
Nada fora do comum aqui. Do que no fragmento f2, esse código leva você ao fragmento f3.
f2 -> f3
Não sei ao ler os documentos se isso deve funcionar, é dito que esse método de transação pop-up é assíncrono e talvez uma maneira melhor seria chamar popBackStackImmediate (). Mas, até onde eu sei, nos meus dispositivos está funcionando perfeitamente.
A referida alternativa seria:
Aqui, na verdade, haverá um breve retorno a f1 antes de passar para f3, portanto, uma pequena falha.
Na verdade, isso é tudo o que você precisa fazer, sem necessidade de substituir o comportamento da pilha traseira ...
fonte
Eu sei que é uma quetion antiga, mas eu tenho o mesmo problema e corrijo assim:
Primeiro, adicione Fragment1 ao BackStack com um nome (por exemplo, "Frag1"):
E então, sempre que você quiser voltar ao Fragmento1 (mesmo depois de adicionar 10 fragmentos acima), basta chamar popBackStackImmediate com o nome:
Espero que ajude alguém :)
fonte
Após a resposta do @Arvis, decidi me aprofundar ainda mais e escrevi um artigo sobre isso aqui: http://www.andreabaccega.com/blog/2015/08/16/how-to-avoid-fragments-overlapping- devido-a-pesadelo-pesadelo-no-android /
Para os desenvolvedores preguiçosos ao redor. Minha solução consiste em adicionar sempre as transações ao backstack e executar um extra
FragmentManager.popBackStackImmediate()
quando necessário (automaticamente).O código tem muito poucas linhas de código e, no meu exemplo, eu queria pular de C para A sem voltar para "B" se o usuário não se aprofundar no backstack (ex: C navega para D).
Portanto, o código anexado funcionaria da seguinte forma: A -> B -> C (traseira) -> A e A -> B -> C -> D (traseira) -> C (traseira) -> B (traseira) -> A
Onde
foram emitidos de "B" a "C" como na pergunta.
Ok, ok, aqui está o código :)
fonte
Se você está lutando com addToBackStack () e popBackStack (), basta usar
Em sua atividade No OnBackPressed () descubra a remessa por tag e faça suas coisas
Para obter mais informações https://github.com/DattaHujare/NavigationDrawer , nunca uso addToBackStack () para manipular fragmentos.
fonte
Eu acho que, quando li sua história, [3] também está nos bastidores. Isso explica por que você o vê piscando.
A solução seria nunca colocar [3] na pilha.
fonte
s old method issue but now it
bem. GraçasEu tive um problema semelhante em que tinha três fragmentos consecutivos no mesmo
Activity
[M1.F0] -> [M1.F1] -> [M1.F2] seguido por uma chamada para um novoActivity
[M2]. Se o usuário pressionou um botão em [M2], eu queria retornar a [M1, F1] em vez de [M1, F2], que é o que o comportamento da contrapressão já fazia.Para fazer isso, removo [M1, F2], chamo show em [M1, F1], confirmo a transação e, em seguida, adiciono [M1, F2] chamando-o com hide. Isso removeu a pressão extra adicional que, de outra forma, teria sido deixada para trás.
Oi Depois de fazer este código: Não consigo ver o valor do Fragment2 ao pressionar a tecla Voltar. Meu código:
fonte
executePendingTransactions()
,commitNow()
não funcionou (Trabalhou no androidx (jetpack).
fonte