1

Docs say you shouldn't nest fragments 'cause they are designed to work another way. But!

Scope

But what if I use ViewPager and it's fragment (visible screen) should be constructed from other fragments. Let's say we have a messenger app. On the main screen there's a ViewPager with fragment called "Last messages" (MessagesFragment) and on the submain screen (let's say Discussion or Chat room) we also have a ViewPager but Messages screen consists of MessagesFragment, NewMessageBarFragment and e.g. header fragment with other info.

Problem

One can say we shouldn't use ViewPager for that and we can use ViewFlow open source library but believe me the code, holded in such activity is a mess even with fragments. Also there's an approach to fill extra layouts, not fragments for NewMessageBar and HeaderInfo within MessagesFragment — but it's double ugly as for me.

Question

Is it OK to use nested fragments in this particular situation? What would you recommend?

Upadate

Sometimes it's really 'must have' feature. But! In most cases I would recommend follow 'responsive design' way: http://www.slideshare.net/kirillcool/responsive-mobile-design-in-practice

Community
  • 1
  • 1
Oleksii Malovanyi
  • 6,780
  • 6
  • 24
  • 27

3 Answers3

5

A quick update:

Nested Fragments were officially introduced in Android API version 17.

Alex Lockwood
  • 83,063
  • 39
  • 206
  • 250
2

But what if I use ViewPager and it's fragment (visible screen) should be constructed from other fragments.

Don't do that.

Is it OK to use nested fragments in this particular situation?

Absolutely not.

What would you recommend?

Replace your "outer" fragments with simple layouts. Then use some other PagerAdapter implementation that does not itself require a fragment (as does the FragmentPagerAdapter interface). You may need to pinch some code from FragmentPagerAdapter to make sure this all works well.

Or, combine "MessagesFragment, NewMessageBarFragment and e.g. header fragment with other info" into a single fragment for use on screen sizes where you want to have this pager behavior. This may result in some amount of code duplication, to have that portion of the UI broken out separately for smaller screen sizes.

Community
  • 1
  • 1
CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Thanks, that's a clear answer. I'm trying 2nd way (combining layout in fragment depending on current screen) and technically it's ok ('cause you can inflate only things you want to be inflated) but code seems to be so clear as before. As an alternative I really look at PagerAdapter. Thanks again! – Oleksii Malovanyi Oct 31 '11 at 14:48
1

Here is my solution. Now you can user getChildFragmentManager from fragment. It is included in new SupportLibrary. In my production solution I first test if fragments exist and add them only when necessary.

public class TestActivity extends SherlockFragmentActivity {

    private ViewPager mPager;

    @Override
    protected void onCreate(Bundle arg0) {
        super.onCreate(arg0);
        setContentView(R.layout.pager);
        mPager = (ViewPager) findViewById(R.id.viewPager);
        mPager.setAdapter(new TestAdapter(getSupportFragmentManager()));
    }

    public class TestAdapter extends FragmentStatePagerAdapter {

        public TestAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            return TestFragment.newInstance(position);
        }

        @Override
        public int getCount() {
            return 4;
        }
    }

    public static class TestFragment extends SherlockFragment {

        private static final int ID = 9129345;
        private static final String ARG_INDEX = "TestFragment.pageindex";

        public static TestFragment newInstance(int page) {
            TestFragment fragment = new TestFragment();
            Bundle args = new Bundle();
            args.putInt(ARG_INDEX, page);
            fragment.setArguments(args);
            return fragment;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.root_container, null);
            int pageId = getArguments().getInt(ARG_INDEX);

            FragmentTransaction transaction = getChildFragmentManager().beginTransaction();

            transaction.add(R.id.live_radio, TextFragment.newInstance("PAGE", pageId + 1));
            transaction.add(R.id.breaking_news, TextFragment.newInstance("TEXT", 1));
            transaction.add(R.id.main_container, TextFragment.newInstance("TEXT", 2));


            transaction.commit();

            return view;
        }

    }

    public static class TextFragment extends SherlockFragment {

        private static final String ARG_INDEX = "TextFragment.index";
        private static final String ARG_TEXT = "TextFragment.text";

        public static TextFragment newInstance(String text, int index) {
            TextFragment fragment = new TextFragment();
            Bundle args = new Bundle();
            args.putString(ARG_TEXT, text);
            args.putInt(ARG_INDEX, index);
            fragment.setArguments(args);
            return fragment;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,   Bundle savedInstanceState) {
            TextView textView = new TextView(getActivity());
            textView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
            textView.setText(getArguments().getString(ARG_TEXT) + " " + getArguments().getInt(ARG_INDEX));
            return textView;
        }

    }

}

Main Layout with viewpager

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <FrameLayout
        android:id="@+id/player"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true" />

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@id/player"
        android:layout_alignParentTop="true" >
    </android.support.v4.view.ViewPager>

</RelativeLayout>

Fragment layout

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <FrameLayout
        android:id="@+id/live_radio"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <FrameLayout
        android:id="@+id/breaking_news"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <FrameLayout
        android:id="@+id/main_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

</LinearLayout>
pcu
  • 2,745
  • 4
  • 18
  • 22
  • 1
    Thanks for your details. Sometimes it's really 'must have' feature. But! In most cases I would recommend follow 'responsive design' way introduced by play store developers: http://www.slideshare.net/kirillcool/responsive-mobile-design-in-practice – Oleksii Malovanyi Nov 16 '12 at 13:31
  • Hi, my code is similar to yours but I have a strange bug that i can't fix. On orientation change the child fragment isn't deatached from the old activity. What i get is a child fragment in the view pager that has a reference to the old activity and can't act with the new one. I don't have to explain you that this can lead to multiple bugs and weird behavior. Did you have this issue? – Mario Lenci Jan 22 '13 at 14:51
  • I have no idea whats wrong. I implemented something like this but more complicated in application which is in market and everything works OK. I needed to use same fragments on different places sometimes together and somtimes alone on this was only way to not duplicate code. – pcu Feb 06 '13 at 13:26