Fragments within Fragments
Asked Answered
S

6

149

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?

Shabby answered 27/7, 2011 at 15:56 Comment(2)
@rds this is an ancient question, bit pointless to mark as duplicate.Subway
Example project demonstrating a fragment within a fragmentProtomartyr
E
204

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

Executor answered 27/7, 2011 at 16:17 Comment(13)
Not supported because of a bug or lack of functionality? Will this be fixed in a future release?Shabby
Not supported because it was not a design goal for the initial implementation. I have heard lots of requests for the feature, so it will probably be done at some point, but as usual there are lots of other things competing with it in priority.Executor
Thanks. Where can you request functionality for the Android platform / View progress of other requests - do you know?Shabby
There is the bug database at android.com, but for this I wouldn't suggest doing anything -- I am already well aware of the requests, and when something will be done is just a matter of where it gets juggled with everything else.Executor
I managed this by extending FragmentActivity, FragmentManager, and FragmentTransaction. Basic premise is extend DeferringFragmentActivity in my activities, providing same api so no other code changes. When I call getFragmentManager, I get an instance that DeferringFragmentManager, and when I call beginTransaction, I get a DeferredTransaction. This transaction stores POJOs with the called method and arguments. When commit is call, we look for any pending DeferredTransactions first. Once all transactions have been committed, we start a real transaction and run all the stored methods with args.Armistead
How about the ViewPager? It can be included in a fragment and display other fragments as its children. Looking at the code, it's clear that it is not actually inserting fragments on other fragments but adding them to the activity but using the fragments' view into it's internal ScrollView. We faced strange behaviors with the ViewPager, BTW.Ignite
That point is now. Nested Fragments are now part of the Android API, yay! developer.android.com/about/versions/….Vaticide
@Executor sorry I'm a little new to fragments, but can you tell me hwo to remove it? so that I won't get an exception thrownCaridadcarie
Wow, what a nightmare: if you use <fragment> on a Fragment, and that Fragment happens to use child fragments, it doesn't fail with a clear error ("can't add child fragments to layout fragments")--it fails mysteriously with exceptions like "fragment did not create a view". There goes several hours of time debugging...Seleucia
@GlennMaynard The truth is, avoid "hardcoding" fragments as <fragment> in your layouts. You're almost always better off using a <FrameLayout> and inserting the fragment there programmatically. At least that's what I always do and even tho sometimes it requires a few lines of extra code, the flexibility pays off in moments like these ;)Harmonia
@MartínMarconcini sure but that's not at all apparent based on the functionality provided by the API. If something is not allowed it should be clearly documented, not left to the developer to pull their hair out over because something is not working the way you would expect.Joshia
@DavidCowden Oh, I agree, but that's Google's Fault ;) And if you do iOS programming, you'd be surprised at the amount of bad docs in the Cocoa framework coming from Apple. Documenting is hard :)Harmonia
Is there compatibility for this on older version?Couteau
L
99

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.

Lumumba answered 13/6, 2012 at 17:51 Comment(2)
main disadvantage of Nestedfragment is we can't call optionmenu from childfragment :( if we are using ABS!Frail
Can you please look in my issue?? Its very similar.. #32240638. For me the child framnet is not getting inflated from the codeSweeten
M
34

.. 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();
    }
Matteo answered 31/10, 2011 at 12:37 Comment(2)
If you do some lifecycle testing with SetAlwaysFinish (bricolsoftconsulting.com/2011/12/23/…), you'll see that this code causes an error when another activity goes on top with always finish enabled (IllegalStateException: Can not perform this action after onSaveInstanceState). Wrapping the code above in try / catch is not the most elegant solution but it seems to make everything work.Zeppelin
This almost worked. Later i got a Stackoverflow on drawing UI. Definitly avoid nested fragments...Joannajoanne
S
14

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.

Secrete answered 26/10, 2011 at 15:34 Comment(2)
Can anyone comment on the effectiveness of this approach? I find it unfortunate to be able to use Fragments only one level deep - might as well not use them at all then. Adding them programmatically onto placeholder viewgroups will work without caveats?Bilabial
Still seems to be working for me, I swap them in and out of viewholder no problem as well. One caveat I am only doing this on honeycomb not with compatibility to Ice cream sandwich.Secrete
D
4

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.

Deanedeaner answered 19/7, 2012 at 11:28 Comment(0)
S
0

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

Synopsis answered 19/6, 2016 at 14:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.