Replacing a fragment with another fragment of the same class

ilomambo picture ilomambo · Jun 17, 2013 · Viewed 10.5k times · Source

I have a fragment (let's call it MyFragment) that inflates different layouts according to a parameter passed in the arguments.
All works well if MyFragment is started from a different fragment. But if MyFragment is active and I want to launch a new MyFragment with a different layout parameter, the fragmentManager does not create a new fragment at all.

data.setInt("Layout index",i);
fragmentTab0 = (Fragment) new MyFragment();
fragmentTab0.setArguments(data);
fragmentTransaction.replace(R.id.fragmentContent, fragmentTab0, "MY");
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();

How can I force convince the fragmentTransaction to launch the fragment again?

NOTE: The important point here is I need to inflate again a layout, which is different from the layout inflated before. The code looks like:

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    switch( getArguments().getInt("Layout index") ) {
    case 1:
        rootView = inflater.inflate(R.layout.firstlayout, container, false);
        break;
    case 2:
        rootView = inflater.inflate(R.layout.secondlayout, container, false);
        break;
    case 3:
        rootView = inflater.inflate(R.layout.thirdlayout, container, false);
        break;
    default: break;
    }       

Answer

ilomambo picture ilomambo · Jun 18, 2013

Bypass Solution
Explanation (hoover to see it)

Since the source code for fragmentTransaction.replace/add/remove is not available I could not find what really happens. But it is reasonable to think that at some point it compares the current class name with the replacement class name and it exits if they are the same.. Thanks to @devconsole for pointing out the source code. I know now why this happens. The FragmentManager.removeFragment() method does not reset the fragment state, it remains RESUMED, then the method moveToState(CREATED) only initilizes a fragment if (f.mState < newState) = if (RESUMED < CREATED) = false. Else, ergo, it just resumes the fragment.

So to solve this problem I created an almost empty fragment which only purpose is to replace itself with the target fragment.

public class JumpFragment {

    public JumpFragment() {
    }

    @Override
    public void onStart() {
        Bundle data = getArguments();
        int containerId = data.getString("containerID");
        String tag = data.getString("tag");
        //Class<?> c = data.get???("class");            
        //Fragment f = (Fragment) c.newInstance();          
        Fragment f = (Fragment) new MyFragment();
        FragmentManager fragmentManager = getFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        f.setArguments(data);
        fragmentTransaction.replace(containerId, f, tag);
        fragmentTransaction.addToBackStack(null);
        fragmentTransaction.commit();
    }

}

And I use it:

data.setInt("Layout index",i);
data.setInt("containerID",R.id.fragmentContent);
data.setString("tag","MY");
fragmentTab0 = (Fragment) new JumpFragment();
fragmentTab0.setArguments(data);
fragmentTransaction.replace(R.id.fragmentContent, fragmentTab0);
fragmentTransaction.commit();

Now no fragment is replaced by a same class fragment:

MyFragment -> JumpFragment -> MyFragment

I haven't figured out how to pass the class through the arguments bundle, to make it totally generic.