7

I'm trying to decide and show a fragment in activity's onResume method, but in case a previously added fragment is chosen again, then the activity goes blank.

Sample code (with one fragment):

@Override
protected void onResume(){
    FragmentTransaction trans = getSupportFragmentManager().beginTransaction();

    trans.replace(R.id.myLayout, fragA);

    trans.commit();
    getSupportFragmentManager().executePendingTransactions();
}

With above code, when the activity is created for the first time, it shows fragA correctly, but in case I press Home Key and then switch back to my activity (in order to provoke onResume again), it all goes blank (seems like fragA is removed).

Is replacing a previously added fragment removes itself? or how not to loose a fragment if it is replaced by itself?

waqaslam
  • 67,549
  • 16
  • 165
  • 178

4 Answers4

5

You can't replace a fragment with itself. The first half of a replace is a removal of the previous fragment at that id. Once a fragment is removed it can no longer be added or used by the fragment manager (so the add portion of the replace will not work properly).

Depending on your use case, you have two options:

  1. Create a new fragment instead of reusing the existing instance
  2. Use some other method to see if its necessary to replace your fragment

Finally, you probably don't need to call executePendingTransactions.

Justin Breitfeller
  • 13,737
  • 4
  • 39
  • 47
  • I initialize **fragA** in *onCreate*, which means i do keep reference to my fragment. So why doesn't it allow to add it after removing that fragment? – waqaslam Feb 19 '13 at 14:26
  • Because of exactly what I said. Once you remove an instance of a fragment, you can NOT add it any longer. Its destroyed, finished, no longer usable. – Justin Breitfeller Feb 19 '13 at 14:58
  • One other thing to check is that R.id.myLayout is the id of the container you are wishing to put your fragment into. If it is, try switching your replace call to be replace(R.id.myLayout, new FragmentAClass()) – Justin Breitfeller Feb 19 '13 at 15:03
  • 1
    @Waqas Although this answer is marked as accepted, it did not work for me, when I created a new instance of the fragment (suggestion #1) nothing happened, the current fragment was not replaced. – ilomambo Jun 17 '13 at 13:33
  • 3
    I doubt about this "Once a fragment is removed it can no longer be added or used by the fragment manager", I've tried with two fragments and replace one by another again and again, it works well. – Lei Guo Jul 17 '13 at 09:12
  • @JustinBreitfeller this answer is simply wrong. No argument about it. – dcow Nov 23 '13 at 23:31
  • I could change my answer to be "it *should* no longer be added...". But just because it may be possible to re-use a fragment after destruction doesn't mean its a good idea. There are state issues, memory leak issues, and etc that would all have to be thoroughly addressed. – Justin Breitfeller Nov 25 '13 at 17:42
  • @JustinBreitfeller ~"You can't replace a fragment with itself." Then how do you refresh the fragment's layout? – IgorGanapolsky May 02 '14 at 22:08
  • Surely popping the back stack reuses the old fragment that was there? – Karu Jun 02 '14 at 06:12
  • Placing a fragment on the backstack does not call onDestroy of the fragment like replace does. – Justin Breitfeller Jun 04 '14 at 20:53
4

You can try:

if( !(getSupportFragmentManager().findFragmentById(R.id.myLayout) instanceof FragmentA) ) {
    FragmentTransaction trans = getSupportFragmentManager().beginTransaction();

    trans.replace(R.id.myLayout, fragA);

    trans.commit();
}

And I assume that fragA is FragmentA class object.

Michał Z.
  • 4,119
  • 1
  • 22
  • 32
  • `R.id.myLayout` points to the layout in which **fragA** is injected. I dont think `findFragmentById(R.id.myLayout)` will return me reference to my fragment. – waqaslam Feb 19 '13 at 14:28
  • If your fragment is in this container then it will return a reference to this injected fragment. Then you can check if it is instance of your fragment's class. – Michał Z. Feb 19 '13 at 14:31
  • this works, and it's pretty interesting solution if you are creating fragments inside fragments. – Avinash R May 13 '14 at 17:57
3

Finally, I had to put a check before replacing fragments. In case, an (already added) fragment is requested for replace again, I had to check if its already added then ignore the replacement, else proceed. For example:

@Override
protected void onResume() {
    if (!fragA.isAdded()) {
        FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
        trans.replace(R.id.myLayout, fragA);
        trans.commit();
        //getSupportFragmentManager().executePendingTransactions();   //unnecessary
    }
}
TWiStErRob
  • 44,762
  • 26
  • 170
  • 254
waqaslam
  • 67,549
  • 16
  • 165
  • 178
0

When referencing back to a created Fragment please do make sure to try adding the

fragmentTransaction.addToBackStack(null); 

method right before committing so that your Fragment is resumed instead of destroyed as mentioned in the developer guides.

If you don't call addToBackStack() when you perform a transaction that removes a fragment, then that fragment is destroyed when the transaction is committed and the user cannot navigate back to it. Whereas, if you do call addToBackStack() when removing a fragment, then the fragment is stopped and is later resumed if the user navigates back.

You can find this at the end of this page.

Panos Gr
  • 667
  • 5
  • 13