Fragments within Fragments

AndroidAndroid FragmentsAndroid Nested-Fragment

Android Problem Overview


I'm wondering if this is actually a bug in the Android API:

I have a setup like so:

----┬---------┐
|    |         |
|  1 |    2    |
|    |┌-------┐|
|    ||       ||
|    ||   3   ||
└----┴┴-------┴┘
  1. Is a menu which loads fragment #2 (A search screen) in the right pane.
  2. Is a search screen which contains fragment #3, which is a result list.
  3. The result list is used in several places (including as a functioning high level fragment in it's own right).

This functionality works perfectly well on a phone (Where 1 & 2 and 3 are ActivityFragments).

However, when I used this code:

	FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();		
	Fragment frag = new FragmentNumber2();
	if(toLoad != null) frag.setArguments(toLoad);
	transaction.replace(R.id.rightPane, frag);      
	transaction.commit();

Where R.id.leftPane and R.id.rightPane are <fragment>s in a horizontal linear layout.

It is my understanding that the above code removes the fragment which is resident and then replaces it with a new fragment. Brilliant... Obviously that isn't what happens because when this code runs the second time you get the following exception:

07-27 15:22:55.940: ERROR/AndroidRuntime(8105): Caused by: java.lang.IllegalArgumentException: Binary XML file line #57: Duplicate id 0x7f080024, tag null, or parent id 0x0 with another fragment for FragmentNumber3

This is caused because the the container for FragmentNumber3 has been duplicated and it no longer has a unique ID. The initial Fragment hasn't been destroyed (?) before the new one is added (in my mind that means it hasn't been replaced).

Can someone tell me if this is possible (this answer suggests it isn't) or is it a bug?

Android Solutions


Solution 1 - Android

Nested fragments are not currently supported. Trying to put a fragment within the UI of another fragment will result in undefined and likely broken behavior.

> Update: Nested fragments are supported as of Android 4.2 (and Android Support Library rev 11) : http://developer.android.com/about/versions/android-4.2.html#NestedFragments

NOTE (as per this docs): "Note: You cannot inflate a layout into a fragment when that layout includes a <fragment>. Nested fragments are only supported when added to a fragment dynamically."

Solution 2 - Android

> Nested fragments are supported in android 4.2 and later

The Android Support Library also now supports nested fragments, so you can implement nested fragment designs on Android 1.6 and higher.

To nest a fragment, simply call getChildFragmentManager() on the Fragment in which you want to add a fragment. This returns a FragmentManager that you can use like you normally do from the top-level activity to create fragment transactions. For example, here’s some code that adds a fragment from within an existing Fragment class:

Fragment videoFragment = new VideoPlayerFragment();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.add(R.id.video_fragment, videoFragment).commit();

To get more idea about nested fragments, please go through these tutorials
Part 1
Part 2
Part 3

and here is a SO post which discuss about best practices for nested fragments.

Solution 3 - Android

.. you can cleanup your nested fragment in the parent fragment's destroyview method:

@Override
    public void onDestroyView() {

      try{
        FragmentTransaction transaction = getSupportFragmentManager()
                .beginTransaction();

        transaction.remove(nestedFragment);
        
        transaction.commit();
      }catch(Exception e){
      }

        super.onDestroyView();
    }

Solution 4 - Android

I have an application that I am developing that is laid out similar with Tabs in the Action Bar that launches fragments, some of these Fragments have multiple embedded Fragments within them.

I was getting the same error when I tried to run the application. It seems like if you instantiate the Fragments within the xml layout after a tab was unselected and then reselected I would get the inflator error.

I solved this replacing all the fragments in xml with Linearlayouts and then useing a Fragment manager/ fragment transaction to instantiate the fragments everything seems to working correctly at least on a test level right now.

I hope this helps you out.

Solution 5 - Android

I've faced with the same problem, have struggled a couple of day with it and should say that the most easiest way to overcome I found this is to use fragment.hide() / fragment.show() when tab is selected/unselected().

public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft)
{
    if (mFragment != null)
        ft.hide(mFragment);
}

When screen rotation occurs all parent and child fragments get correctly destroyed.

This approach has also one additional advantage - using hide()/show() does not cause fragment views to loose their state, so there is no need to restore the previous scroll position for ScrollViews for example.

The problem is that I don't know whether it is correct to not detach fragments when they are not visible. I think the official example of TabListener is designed with a thought in mind that fragments are reusable and you should not pollute with them memory, however, I think if you have just a few tabs and you know that users will be switching between them frequently it will be appropriate to keep them attached to the current activity.

I would like to hear comments from more experienced developers.

Solution 6 - Android

If you find your nested fragment not being removed or being duplicated (eg. on Activity restart, on screen rotate) try changing:

transaction.add(R.id.placeholder, newFragment);

to

transaction.replace(R.id.placeholder, newFragment);

If above doesn't help, try:

Fragment f = getChildFragmentManager().findFragmentById(R.id.placeholder);

FragmentTransaction transaction = getChildFragmentManager().beginTransaction();

if (f == null) {
    Log.d(TAG, "onCreateView: fragment doesn't exist");
    newFragment= new MyFragmentType();
    transaction.add(R.id.placeholder, newFragment);
} else {
    Log.d(TAG, "onCreateView: fragment already exists");
    transaction.replace(R.id.placeholder, f);
}
transaction.commit();

Learnt here

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGraemeView Question on Stackoverflow
Solution 1 - AndroidhackbodView Answer on Stackoverflow
Solution 2 - AndroidRaneez AhmedView Answer on Stackoverflow
Solution 3 - AndroidfurykidView Answer on Stackoverflow
Solution 4 - AndroiddraksiaView Answer on Stackoverflow
Solution 5 - AndroidievgenView Answer on Stackoverflow
Solution 6 - AndroidVoyView Answer on Stackoverflow