Difference between add(), replace(), and addToBackStack()

From: http://stackoverflow.com/questions/18634207/difference-between-add-replace-and-addtobackstack

I've got a massive problem with the way the android fragment backstack seems to work and would be most grateful for any help that is offered.

Imagine you have 3 Fragments

[1] [2] [3]

I want the user to be able to navigate [1] > [2] > [3] but on the way back (pressing back button) [3] > [1].

As I would have imagined this would be accomplished by not calling addToBackStack(..) when creating the transaction that brings fragment [2] into the fragment holder defined in XML.

The reality of this seems as though that if I dont want [2] to appear again when user presses back button on [3], I must not call addToBackStack in the transaction that shows fragment [3]. This seems completely counter-intuitive (perhaps coming from the iOS world).

Anyway if i do it this way, when I go from [1] > [2] and press back I arrive back at [1] as expected.

If I go [1] > [2] > [3] and then press back I jump back to [1] (as expected).Now the strange behavior happens when I try and jump to [2] again from [1]. First of all [3] is briefly displayed before [2] comes into view. If I press back at this point [3] is displayed, and if I press back once again the app exits.

Can anyone help me to understand whats going on here?


And here is the layout xml file for my main activity:

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

        />



UpdateThis is the code I'm using to build by nav heirarchy

    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]

Many thanks

share | improve this question
 
up vote 77 down vote accepted

EXPLANATION (on what's going on here?):

If we keep in mind that .replace() = .remove().add() (that we know by documentation )

Replace an existing fragment that was added to a container. This is essentially the same as calling remove(Fragment) for all currently added fragments that were added with the same containerViewId and then add(int, Fragment, String) with the same arguments given here.

then what's happening is like this (I'm adding numbers to the frag to make it more clear):

// transaction.replace(R.id.detailFragment, frag1);
Transaction.remove(null).add(frag1)  // frag1 on view

// transaction.replace(R.id.detailFragment, frag2).addToBackStack(null);
Transaction.remove(frag1).add(frag2).addToBackStack(null)  // frag2 on view

// transaction.replace(R.id.detailFragment, frag3);
Transaction.remove(frag2).add(frag3)  // frag3 on view

(here all misleading stuff starts to happen)

Remember that .addToBackStack() is saving only TRANSACTION not the FRAGMENT as itself!

So now we have frag3 on the layout:

< press back button >
// System pops the back stack and find the following saved back entry to be reversed:
// [Transaction.remove(frag1).add(frag2)]
// so the system makes that transaction backward!!!
// tries to remove frag2 (is not there, so it ignores) and re-add(frag1)
// make notice that system doesn't realise that there's a frag3 and does nothing with it
// so it still there attached to view
Transaction.remove(null).add(frag1) //frag1, frag3 on view (OVERLAPPING)

// transaction.replace(R.id.detailFragment, frag2).addToBackStack(null);
Transaction.remove(frag3).add(frag2).addToBackStack(null)  //frag2 on view

< press back button >
// system makes saved transaction backward
Transaction.remove(frag2).add(frag3) //frag3 on view

< press back button >
// no more entries in BackStack
< app exits >

Possible solution:

consider implementing FragmentManager.BackStackChangedListener to watch for changes in the back stack and apply your logic in onBackStackChanged() methode:

share | improve this answer
 
 
Thank you very much this was the most helpful explanation on Fragment Transactions! Saved me hours of time. –  Jesse Farebrother Mar 21 '13 at 3:28
 
Good explanation @arvis. However, how can we prevent this behaviour without resorting to hacky ways like the one from DexterMoon, or the one from Nemanja using popBackStack which shows the Fragment while playing the transition animation? –  momo Sep 27 '13 at 7:31
 
@momo you might implement FragmentManager.BackStackChangedListener to watch for changes to the back stack. Monitor all your transactions with onBackStackChanged() methode and act as necessary: for ex. trace a count of transaction in BackStack; check particular transaction by name (FragmentTransaction addToBackStack (String name)) etc. –  Arvis Sep 27 '13 at 13:03
 
Thanks for answering. I actually tried that earlier today, registered the listener and removed the fragment from onBackstackChange. While the popping transition played the fragment becomes a white empty area. I guess the method is fired when popping starts the animation and not when ends... –  momo Sep 27 '13 at 17:50
1 
Avoid using back stacks! it doesn't really help with the overall efficiency! use plain replace() or even better remove/add every time you want to navigate! –  stack_ved Sep 29 at 6:04

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值