Como posso manter o estado do fragmento quando adicionado à pilha traseira?

160

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.

Eric
fonte
3
@ Jan-Henk E as coisas que precisam ser buscadas? Por exemplo, a posição de rolagem de a ListView. Parece demais para anexar um ouvinte de rolagem e atualizar uma variável de instância.
Jake Wharton
2
@JakeWharton Concordo que deveria ser mais fácil, mas até onde eu sei, não há como contornar isso, porque onCreateView é chamado quando um fragmento é restaurado do backstack. Mas posso estar errado :)
Jan-Henk
1
onCreate não é chamado. Então, aparentemente, está reutilizando a mesma instância, mas chamando novamente o CreateView? Coxo. Acho que posso apenas armazenar em cache o resultado do onCreateView e retornar a exibição existente se o onCreateView for chamado novamente.
Eric
1
Exatamente o que eu estava procurando por horas. Você pode postar como conseguiu isso usando a variável de instância?
Uma
1
Recentemente, iniciei minha própria implementação em github.com/frostymarvelous/Folio e me deparei com um problema. Sou capaz de criar cerca de 5 páginas / fragmentos complexos antes de começar a ter falhas no OOM. Foi isso que me levou aqui. Ocultar e mostrar simplesmente não é suficiente. As exibições têm muita memória.
27616 frostymarvelous

Respostas:

120

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().

Jan-Henk
fonte
32
A versão atual da documentação contradiz esta afirmação. O fluxograma diz o que você afirma, mas o texto na área principal da página diz que onCreateView () é chamado apenas na primeira vez que o Fragmento é exibido: developer.android.com/guide/components/fragments.html Estou lutando contra isso problema agora e não vejo nenhum método chamado ao retornar um fragmento do backstack. (Android 4.2)
Colin M.
10
Tentou registrar seu comportamento. O onCreateView () é sempre chamado quando o fragmento está sendo exibido.
Princepiero
4
@ColinM. Alguma solução para o problema?
nevasca
9
Isso não funciona para mim. Minhas variáveis ​​de instância são nulas ao retornar ao fragmento! Como posso salvar o estado?
Don Rhummy
5
portanto, se não devemos retransmitir a instância de salvamento, como salvar o status e os dados do fragmento?
Mahdi
80

Comparando com os da Apple UINavigationControllere UIViewController, o Google não se sai bem na arquitetura de software Android. E o documento do Android sobre Fragmentnã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)

public class FragmentA extends Fragment {
    View _rootView;
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (_rootView == null) {
            // Inflate the layout for this fragment
            _rootView = inflater.inflate(R.layout.fragment_a, container, false);
            // Find and setup subviews
            _listView = (ListView)_rootView.findViewById(R.id.listView);
            ...
        } else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove _rootView from the existing parent view group
            // (it will be added back).
            ((ViewGroup)_rootView.getParent()).removeView(_rootView);
        }
        return _rootView;
    }
}

------ 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

public class FragmentA extends Fragment {
    View _rootView;
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (_rootView == null) {
            // Inflate the layout for this fragment
            _rootView = inflater.inflate(R.layout.fragment_a, container, false);
            // Find and setup subviews
            _listView = (ListView)_rootView.findViewById(R.id.listView);
            ...
        } else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove _rootView from the existing parent view group
            // in onDestroyView() (it will be added back).
        }
        return _rootView;
    }

    @Override
    public void onDestroyView() {
        if (_rootView.getParent() != null) {
            ((ViewGroup)_rootView.getParent()).removeView(_rootView);
        }
        super.onDestroyView();
    }
}

AVISO!!!

Este é um HACK! Embora eu esteja usando no meu aplicativo, você precisa testar e ler os comentários com cuidado.

Vince Yuan
fonte
38
Manter uma referência à visualização root do fragmento inteiro é uma má ideia para IMO. Se você estiver adicionando continuamente vários fragmentos a um backstack e todos eles mantiverem seu rootview (que possui um espaço de memória bastante grande), existe uma grande possibilidade de você acabar com o OutOfMemoryError, já que todos os fragmentos mantêm referência de rootview e não podem ser observados no GC colete-o. Eu acho que a melhor abordagem é aumentar a visualização o tempo todo (e deixar o sistema Android lidar com a criação / destruição da visualização) e a verificação onActivityCreated / onViewCreated se seus dados forem nulos. Se sim, carregue-o; caso contrário, defina os dados como visualizações.
traninho 30/10
15
Não faça isso! Quando a hierarquia de exibição do fragmento é criada, ela contém uma referência interna à atividade que continha o fragmento no momento. Quando ocorre uma alteração na configuração, a Atividade geralmente é recriada. A reutilização do layout antigo mantém a atividade zumbi na memória, juntamente com os objetos que ele também faz referência. A perda de memória como essa prejudica o desempenho e torna seu aplicativo o principal candidato para rescisão imediata quando não está em primeiro plano.
Krylez
4
@AllDayAmazing Esse é um bom ponto. Para ser sincero, estou muito confuso agora. Alguém pode tentar explicar por que manter uma referência ao rootview de um fragmento não está ok, mas manter uma referência apenas para qualquer filho do rootview (que tenha uma referência ao rootview de qualquer maneira) está ok?
traninho
2
Fique longe disso, a menos que você queira perder 5 horas descobrindo o que está danificando seu código ..... apenas para descobrir que essa é a causa. Agora eu tenho que refatorar um monte de coisas porque usei esse hack. É muito melhor usar fragmentTransaction.add se você quiser manter a interface do usuário do fragmento intacta ao exibir outra (mesmo na parte superior). fragmentTransaction.replace () tem como objetivo destruir as visões do fragmento ..... não lute contra o sistema.
precisa saber é o seguinte
2
@VinceYuan - testei com a biblioteca v7-appcompat mais recente no Android 5.1 e isso deixou 6 instâncias de um fragmento que deveria ter sido removido no FragmentManager da minha atividade. Mesmo que o GC o manipule corretamente (o que não acredito), isso está causando um desgaste desnecessário na memória do seu aplicativo e do dispositivo em geral. Simplesmente usar .add () remove completamente a necessidade de todo esse código hacky. Fazer isso é completamente contra o que usar FragmentTransaction.replace () deveria fazer em primeiro lugar.
Dell116 5/05
53

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:

Fragment fragment=new DestinationFragment();
FragmentManager fragmentManager = getFragmentManager();
android.app.FragmentTransaction ft=fragmentManager.beginTransaction();
ft.add(R.id.content_frame, fragment);
ft.hide(SourceFragment.this);
ft.addToBackStack(SourceFragment.class.getName());
ft.commit();

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.

kaushal trivedi
fonte
No meu caso, adicionar um fragmento em vez de substituir causa um problema ao usar a pesquisa ou qualquer outro tipo de solicitação da Web é usado no fragmento. Quero pausar esta pesquisa no fragmento A quando o fragmento B for adicionado. Alguma idéia disso?
Uma
Como você está usando a pesquisa no FirstFragment? Você tem que fazer isso manualmente, pois os dois fragmentos permanecem na memória; portanto, você pode usar as instâncias para executar as ações necessárias. Espero que isso ajude.
kaushal trivedi
1
Obrigado pela pista =). Eu tenho feito isso. Mas essa é a única maneira de fazer isso? E uma maneira apropriada? Além disso, quando pressiono o botão home e inicio o aplicativo novamente, todos os fragmentos ficam ativos novamente. Suponha que eu esteja aqui no fragmento B por esse caminho. Activity A{Fragment A --> Fragment B}quando onResume()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?
Uma
1
Infelizmente, você não pode, o Sistema não funciona no comportamento normal dessa maneira, ele considerará os dois fragmentos como filho direto da atividade. Embora tenha o objetivo de manter o estado do fragmento, outras coisas normais ficam muito difíceis de gerenciar. descobri todos esses problemas, agora minha sugestão é não seguir esse caminho.
kaushal trivedi
1
É claro que, por último, direi para não seguir essa abordagem até encontrar outra solução.Porque é difícil de gerenciar.
kaushal trivedi
7

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.

   private View fragmentView;

   public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);

        if (fragmentView != null) {
            return fragmentView;
        }
        View view = inflater.inflate(R.layout.yourfragment, container, false);
        fragmentView = view;
        return view;
    }
Mandeep Singh
fonte
1
Há uma chance de vazamento de memória se não removermos a variável 'fragmentView' em onDestroy ()
Arun PM
@ArunPM, como remover o fragmentView em onDestroy ()? if (_rootView.getParent() != null) { ((ViewGroup)_rootView.getParent()).removeView(_rootView); }é apropriado limpar a memória?
Mehmet Gür 16/04
1
@ MehmetGür eu estou usando esta solução muitas vezes. Até agora eu não recebi nenhum erro de vazamento de memória. Mas você pode usar a solução ArunPM com isso, se quiser. Eu acho que ele está dizendo para definir fragmentView para null no método OnDestroy ().
Mandeep Singh 17/04
1
Estou usando o LeakCanary para detectar vazamentos de memória e seus problemas de vazamento quando segui esse método. Mas, como o @Mandeep Sigh mencionado no comentário, podemos superar esse problema atribuindo nulla fragmentView variável no onDestroy()método
Arun PM
1
De acordo com o meu conhecimento, quando um fragmento é destruído, a visualização associada ao fragmento é limpa onDestroyView(). Essa limpeza não está acontecendo para a nossa variável de visualização de backup (aqui fragmentView ) 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.
Arun PM
6

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:

  • replace () - remova o fragmento A e substitua-o pelo fragmento B. O fragmento A será recriado uma vez trazido para a frente novamente
  • add () - (create and) adiciona um fragmento B e ele se sobrepõe ao fragmento A, que ainda está ativo em segundo plano
  • remove () - pode ser usado para remover o fragmento B e retornar ao A. O fragmento B será recriado quando chamado mais tarde

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

Teo Inke
fonte
você pode me dizer quando removemos o fragmento be retornamos a A? Qual método é chamado no fragmento A? Eu quero tomar alguma ação quando removermos o fragmento B. #
Google Google
5

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.

user2779311
fonte
onSaveInstanceState()também é chamado quando o sistema destrói Activity porque não possui recursos.
Marcel Bro
0

Aqui, já que onSaveInstanceStateno fragmento não chama quando você adiciona fragmento ao backstack. O ciclo de vida do fragmento no backstack quando restaurado, inicia onCreateViewe termina onDestroyViewenquanto onSaveInstanceStateé chamado entre onDestroyViewe onDestroy. Minha solução é criar variável de instância e init onCreate. Código de amostra:

private boolean isDataLoading = true;
private ArrayList<String> listData;
public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     isDataLoading = false;
     // init list at once when create fragment
     listData = new ArrayList();
}

E confira onActivityCreated:

public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    if(isDataLoading){
         fetchData();
    }else{
         //get saved instance variable listData()
    }
}

private void fetchData(){
     // do fetch data into listData
}
Ling Boo
fonte
0
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener()
    {
        @Override
        public void onBackStackChanged()
        {
            if (getSupportFragmentManager().getBackStackEntryCount() == 0)
            {
                //setToolbarTitle("Main Activity");
            }
            else
            {
                Log.e("fragment_replace11111", "replace");
            }
        }
    });


YourActivity.java
@Override
public void onBackPressed()
{
 Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.Fragment_content);
  if (fragment instanceof YourFragmentName)
    {
        fragmentReplace(new HomeFragment(),"Home Fragment");
        txt_toolbar_title.setText("Your Fragment");
    }
  else{
     super.onBackPressed();
   }
 }


public void fragmentReplace(Fragment fragment, String fragment_name)
{
    try
    {
        fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.replace(R.id.Fragment_content, fragment, fragment_name);
        fragmentTransaction.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
        fragmentTransaction.addToBackStack(fragment_name);
        fragmentTransaction.commitAllowingStateLoss();
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}
Hardik Vasani
fonte
0

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 onSaveInstanceStatepacote, 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.

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    Bundle args = getArguments();

    // this will be null the first time F1 is created. 
    // it will be populated once you replace fragment and provide bundle data
    if (args != null) {
        if (args.get("your_info") != null) {
            // do what you want with restored information
        }
    }
}

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.

@Override
public void onPhoneAdded(String phone) {
        //replace fragment
        F1 f1 = new F1 ();
        Bundle args = new Bundle();
        yourInfo.setPhone(phone);
        args.putSerializable("you_info", yourInfo);
        f1.setArguments(args);

        getFragmentManager().beginTransaction()
                .replace(R.id.fragmentContainer, f1).addToBackStack(null).commit();

    }
}

Mais informações sobre retornos de chamada podem ser encontradas aqui: https://developer.android.com/training/basics/fragments/communicating.html

Dodi
fonte
0

primeiro : basta usar o método add em vez de substituir o método da classe FragmentTransaction, então você precisa adicionar secondFragment à pilha pelo método addToBackStack

segundo : ao clicar novamente, você precisa chamar popBackStackImmediate ()

Fragment sourceFragment = new SourceFragment ();
final Fragment secondFragment = new SecondFragment();
final FragmentTransaction ft = getChildFragmentManager().beginTransaction();
ft.add(R.id.child_fragment_container, secondFragment );
ft.hide(sourceFragment );
ft.addToBackStack(NewsShow.class.getName());
ft.commit();
                                
((SecondFragment)secondFragment).backFragmentInstanceClick = new SecondFragment.backFragmentNewsResult()
{
        @Override
        public void backFragmentNewsResult()
        {                                    
            getChildFragmentManager().popBackStackImmediate();                                
        }
};
Milad Ahmadi
fonte
0

Substitua um fragmento usando o seguinte código:

Fragment fragment = new AddPaymentFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.frame, fragment, "Tag_AddPayment")
                .addToBackStack("Tag_AddPayment")
                .commit();

OnBackPressed () da atividade é:

  @Override
public void onBackPressed() {
    android.support.v4.app.FragmentManager fm = getSupportFragmentManager();
    if (fm.getBackStackEntryCount() > 1) {

        fm.popBackStack();
    } else {


        finish();

    }
    Log.e("popping BACKSTRACK===> ",""+fm.getBackStackEntryCount());

}
Pratibha Sarode
fonte
0
Public void replaceFragment(Fragment mFragment, int id, String tag, boolean addToStack) {
        FragmentTransaction mTransaction = getSupportFragmentManager().beginTransaction();
        mTransaction.replace(id, mFragment);
        hideKeyboard();
        if (addToStack) {
            mTransaction.addToBackStack(tag);
        }
        mTransaction.commitAllowingStateLoss();
    }
replaceFragment(new Splash_Fragment(), R.id.container, null, false);
Hitesh Manwani
fonte
1
Obrigado por este trecho de código, que pode fornecer ajuda imediata e limitada. Uma explicação adequada melhoraria bastante seu valor a longo prazo , mostrando por que essa é uma boa solução para o problema e a tornaria mais útil para futuros leitores com outras perguntas semelhantes. Por favor edite sua resposta para adicionar alguma explicação, incluindo as suposições que você fez.
Machavity
0

Solução perfeita que encontra fragmento antigo na pilha e carrega-o, se existir na pilha.

/**
     * replace or add fragment to the container
     *
     * @param fragment pass android.support.v4.app.Fragment
     * @param bundle pass your extra bundle if any
     * @param popBackStack if true it will clear back stack
     * @param findInStack if true it will load old fragment if found
     */
    public void replaceFragment(Fragment fragment, @Nullable Bundle bundle, boolean popBackStack, boolean findInStack) {
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        String tag = fragment.getClass().getName();
        Fragment parentFragment;
        if (findInStack && fm.findFragmentByTag(tag) != null) {
            parentFragment = fm.findFragmentByTag(tag);
        } else {
            parentFragment = fragment;
        }
        // if user passes the @bundle in not null, then can be added to the fragment
        if (bundle != null)
            parentFragment.setArguments(bundle);
        else parentFragment.setArguments(null);
        // this is for the very first fragment not to be added into the back stack.
        if (popBackStack) {
            fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        } else {
            ft.addToBackStack(parentFragment.getClass().getName() + "");
        }
        ft.replace(R.id.contenedor_principal, parentFragment, tag);
        ft.commit();
        fm.executePendingTransactions();
    }

use-o como

Fragment f = new YourFragment();
replaceFragment(f, null, boolean true, true); 
Khemraj
fonte