Eu escrevi uma atividade fictícia que alterna entre dois fragmentos. Quando você passa do FragmentA para o FragmentB, o FragmentA é adicionado à pilha de trás. No entanto, quando eu retorno ao FragmentA (pressionando para trás), um FragmentA totalmente novo é criado e o estado em que ele se encontra é perdido. Tenho a sensação de que estou seguindo a mesma coisa que esta pergunta, mas incluí um exemplo de código completo para ajudar a solucionar o problema:
public class FooActivity extends Activity {
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(android.R.id.content, new FragmentA());
transaction.commit();
}
public void nextFragment() {
final FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(android.R.id.content, new FragmentB());
transaction.addToBackStack(null);
transaction.commit();
}
public static class FragmentA extends Fragment {
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View main = inflater.inflate(R.layout.main, container, false);
main.findViewById(R.id.next_fragment_button).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
((FooActivity) getActivity()).nextFragment();
}
});
return main;
}
@Override public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Save some state!
}
}
public static class FragmentB extends Fragment {
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.b, container, false);
}
}
}
Com algumas mensagens de log adicionadas:
07-05 14:28:59.722 D/OMG ( 1260): FooActivity.onCreate
07-05 14:28:59.742 D/OMG ( 1260): FragmentA.onCreateView
07-05 14:28:59.742 D/OMG ( 1260): FooActivity.onResume
<Tap Button on FragmentA>
07-05 14:29:12.842 D/OMG ( 1260): FooActivity.nextFragment
07-05 14:29:12.852 D/OMG ( 1260): FragmentB.onCreateView
<Tap 'Back'>
07-05 14:29:16.792 D/OMG ( 1260): FragmentA.onCreateView
Ele nunca chama FragmentA.onSaveInstanceState e cria um novo FragmentA quando você revida. No entanto, se eu estiver no FragmentA e bloquear a tela, o FragmentA.onSaveInstanceState será chamado. Tão estranho ... estou errado ao esperar que um fragmento adicionado à pilha traseira não precise ser recriado? Aqui está o que os documentos dizem:
Considerando que, se você chamar addToBackStack () ao remover um fragmento, o fragmento será parado e será retomado se o usuário navegar novamente.
ListView
. Parece demais para anexar um ouvinte de rolagem e atualizar uma variável de instância.Respostas:
Se você retornar a um fragmento da pilha traseira, ele não o recriará, mas reutilizará a mesma instância e começará
onCreateView()
no ciclo de vida do fragmento, consulte Ciclo de vida do fragmento .Portanto, se você deseja armazenar o estado, deve usar variáveis de instância e não confiar
onSaveInstanceState()
.fonte
Comparando com os da Apple
UINavigationController
eUIViewController
, o Google não se sai bem na arquitetura de software Android. E o documento do Android sobreFragment
não ajuda muito.Quando você insere FragmentB a partir de FragmentA, a instância FragmentA existente não é destruída. Quando você pressiona Voltar no FragmentB e retorna ao FragmentA, não criamos uma nova instância do FragmentA. As instâncias FragmentA existentes
onCreateView()
serão chamadas.O principal é que não devemos aumentar a exibição novamente nos FragmentA
onCreateView()
, porque estamos usando a instância do FragmentA existente. Precisamos salvar e reutilizar o rootView.O código a seguir funciona bem. Ele não apenas mantém o estado do fragmento, mas também reduz a carga de RAM e CPU (porque apenas aumentamos o layout, se necessário). Não acredito que o código e o documento de exemplo do Google nunca o mencionam, mas sempre aumentam o layout .
Versão 1 (não use a versão 1. Use a versão 2)
------ Atualização em 3 de maio de 2005: -------
Como os comentários mencionados, algumas vezes
_rootView.getParent()
é nulo emonCreateView
, o que causa a falha. A versão 2 remove _rootView em onDestroyView (), como sugerido pela dell116. Testado no Android 4.0.3, 4.4.4, 5.1.0.Versão 2
AVISO!!!
Este é um HACK! Embora eu esteja usando no meu aplicativo, você precisa testar e ler os comentários com cuidado.
fonte
Eu acho que existe uma maneira alternativa de conseguir o que você está procurando. Não digo que seja uma solução completa, mas serviu ao objetivo no meu caso.
O que fiz foi, em vez de substituir o fragmento, apenas adicionei o fragmento de destino. Então, basicamente você usará o
add()
métodoreplace()
.O que mais eu fiz. Escondo meu fragmento atual e também o adiciono ao backstack.
Portanto, ele sobrepõe um novo fragmento ao atual, sem destruir sua exibição. (Verifique se o
onDestroyView()
método não está sendo chamado. Além disso, adicione-o aobackstate
me dá a vantagem de retomar o fragmento.Aqui está o código:
O sistema AFAIK apenas chama
onCreateView()
se a exibição for destruída ou não criada. Mas aqui salvamos a exibição, não removendo-a da memória. Portanto, não criará uma nova exibição.E quando você voltar do Fragmento de Destino, ele aparecerá na última
FragmentTransaction
fragmento superior que removerá, o que fará com que a exibição mais acima (do SourceFragment) apareça na tela.COMENTÁRIO: Como eu disse, não é uma solução completa, pois não remove a visão do fragmento de código-fonte e, portanto, ocupa mais memória do que o habitual. Mas ainda assim, sirva ao propósito. Além disso, estamos usando um mecanismo totalmente diferente de ocultar a vista em vez de substituí-la, que não é tradicional.
Portanto, não é exatamente como você mantém o estado, mas como você mantém a visão.
fonte
Activity A{Fragment A --> Fragment B}
quandoonResume()
inicio o aplicativo novamente depois de pressionar o botão home, ambos os fragmentos são chamados e, portanto, eles iniciam sua pesquisa. Como posso controlar isso?Eu sugeriria uma solução muito simples.
Pegue a variável de referência View e defina view no OnCreateView. Verifique se a visualização já existe nessa variável e, em seguida, retorne a mesma.
fonte
if (_rootView.getParent() != null) { ((ViewGroup)_rootView.getParent()).removeView(_rootView); }
é apropriado limpar a memória?null
afragmentView
variável noonDestroy()
métodoonDestroyView()
. Essa limpeza não está acontecendo para a nossa variável de visualização de backup (aquifragmentView
) e causará vazamento de memória quando o fragmento for empilhado / destruído novamente. Você pode encontrar a mesma referência em [Causas comuns para vazamentos de memória] ( square.github.io/leakcanary/fundamentals/… ) na introdução do LeakCanery.Me deparei com esse problema em um fragmento contendo um mapa, que possui muitos detalhes de configuração para salvar / recarregar. Minha solução foi basicamente manter esse fragmento ativo o tempo todo (semelhante ao que a @kaushal mencionou).
Digamos que você tenha o Fragmento A atual e queira exibir o Fragmento B. Resumindo as conseqüências:
Portanto, se você deseja manter os dois fragmentos "salvos", apenas alterne-os usando hide () / show ().
Prós : método fácil e simples para manter vários fragmentos em execução
Contras : você usa muito mais memória para manter todos eles em execução. Pode ter problemas, por exemplo, exibir muitos bitmaps grandes
fonte
onSaveInstanceState()
é chamado apenas se houver alteração na configuração.Desde a mudança de um fragmento para outro, não há alteração na configuração, portanto, nenhuma chamada para
onSaveInstanceState()
existe. Que estado não está sendo salvo? Você pode especificar?Se você digitar algum texto no EditText, ele será salvo automaticamente. Qualquer item da interface do usuário sem qualquer ID é o item cujo estado de exibição não deve ser salvo.
fonte
onSaveInstanceState()
também é chamado quando o sistema destrói Activity porque não possui recursos.Aqui, já que
onSaveInstanceState
no fragmento não chama quando você adiciona fragmento ao backstack. O ciclo de vida do fragmento no backstack quando restaurado, iniciaonCreateView
e terminaonDestroyView
enquantoonSaveInstanceState
é chamado entreonDestroyView
eonDestroy
. Minha solução é criar variável de instância e initonCreate
. Código de amostra:E confira
onActivityCreated
:fonte
fonte
Meu problema era semelhante, mas me superei sem manter vivo o fragmento. Suponha que você tenha uma atividade que tenha 2 fragmentos - F1 e F2. F1 é iniciado inicialmente e digamos que contém algumas informações do usuário e, após alguma condição, F2 solicita que o usuário preencha um atributo adicional - seu número de telefone. Em seguida, você deseja que esse número de telefone retorne à F1 e conclua a inscrição, mas percebe que todas as informações anteriores do usuário foram perdidas e não possui os dados anteriores. O fragmento é recriado do zero e, mesmo se você salvou essas informações no
onSaveInstanceState
pacote, volta nulo emonActivityCreated
.Solução: salve as informações necessárias como uma variável de instância na atividade de chamada. Em seguida, passe essa variável de instância para o seu fragmento.
Então, seguindo o meu exemplo: antes de exibir F2, salve os dados do usuário na variável de instância usando um retorno de chamada. Então inicio F2, o usuário preenche o número de telefone e pressiona salvar. Uso outro retorno de chamada em atividade, coleto essas informações e substituo meu fragmento F1, desta vez com dados do pacote que posso usar.
Mais informações sobre retornos de chamada podem ser encontradas aqui: https://developer.android.com/training/basics/fragments/communicating.html
fonte
fonte
Substitua um fragmento usando o seguinte código:
OnBackPressed () da atividade é:
fonte
fonte
Solução perfeita que encontra fragmento antigo na pilha e carrega-o, se existir na pilha.
use-o como
fonte