Why it is not possible to use ViewPager within a Fragment? It actually is
Asked Answered
C

4

38

There are information that it is impossible to use ViewPager within a Fragment in many sources like "The Busy Coders Guide for Android Developers" by Mark Murphy, or posts like this on SO. I'm confused because I don't have such a problem and I successfully use ViewPager within my Fragment. The only distinction is that I instantiate a ViewPager not in onCreateView() method but in onActivityCreated(). And everything works perfectly fine.

So the question is - may be I just don't know something and this is not recommended for some reason to make UI instantiations in onActivityCreated()? But again - everything works just fine.

Here is the listing of the class and xml:

Class:

public class ViewPagerFragment extends Fragment {

    static final int NUM_ITEMS = 2;

    private ViewPagerAdapter mAdapter;
    private ViewPager mPager;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.view_pager_fragment, container, false);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        mAdapter = new ViewPagerAdapter(getFragmentManager());

        mPager = (ViewPager) getView().findViewById(R.id.pager);
        mPager.setAdapter(mAdapter);
    }

    public static class ViewPagerAdapter extends FragmentPagerAdapter {
        public ViewPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int num) {
            if (num == 0) {
                return new ItemsListFragment();
            } else {
                return new FavsListFragment();
            }
        }

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

Layout:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:orientation="vertical">

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

</LinearLayout>
Chinua answered 23/8, 2012 at 9:48 Comment(1)
i have same issue #31477278Rent
T
62

UPDATE: Since this answer was originally written, you now can have nested fragments, which means it is possible to have a ViewPager use fragments for pages and be in a fragment itself. This sample project demonstrates the technique.

I now return you to your regularly-scheduled answer, presented in its entirety...


Quoting myself from the book:

The simplest way to use a ViewPager is to have it page fragments in and out of the screen based on user swipes. This only works if the ViewPager itself is not contained within a fragment, as you cannot have fragments nested inside of other fragments.

Quoting Dianne Hackborn:

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.

It is perfectly possible to put a ViewPager inside a Fragment, so long as the contents of the ViewPager do not themselves contain fragments. Since the concrete implementations of PagerAdapter supplied by the Android Support package use fragments, you have to roll your own fragment-less PagerAdapter to put the ViewPager in a fragment.

I will endeavor to make this point clearer in the next edition of the book (unless you're British, in which case I'll endeavour to make this point clearer :-).

Thrombus answered 23/8, 2012 at 11:8 Comment(16)
I'm not British :) Thanks for reply. Can you add something about UI instantiation in onActivityCreated() method?Chinua
@silk: There is nothing intrinsically wrong about setting up your UI in onActivityCreated(). Sometimes that's necessary -- I can't seem to get a MapView to work with a native (non-backport) fragment unless I wait until onActivityCreated() to create it. That being said, since fragments-in-fragments is officially not supported, even if onActivityCreated() seems to help you today, it might not help you in some future version of the SDK or Android Support package.Thrombus
Good news, Nested Fragments are supported in 4.2. See Android 4.2 APIsLeslileslie
Hey @CommonsWare, because the support was just added to the new API, it doesn't mean it'll work on older APIs does it? That is, if we ran this on gingerbread, would it still work? If it worked, would it leak memory? I'm asking because I know in the past this was happening with one of my projects, so I'm wondering if you know about it. Thanks!Nannettenanni
@clu: "it doesn't mean it'll work on older APIs does it?" -- the Android Support package's backport of fragments includes nested fragment support. The sample project I link to above uses that.Thrombus
Hey @CommonsWare, thanks for the link. You're right, it does work on older devices. The issue I was facing earlier was when I had a ViewPager of Fragments within a ListView Header (using addHeaderView()). The ListView itself was also inside a fragment. I printed the onDestroy() and onCreate() method calls and basically, on rotate, the fragments in the viewpager were not being destroyed. Hence leaking the fragments. Have you ever seen anything like this?Nannettenanni
@clu: The only way what you indicated has a shot of working properly is if you are using nested fragments (e.g., the ViewPager is using a child FragmentManager). If you are, then in theory there should be no problems, but nested fragments are sometimes problematic in general, so I try to avoid them wherever possible.Thrombus
Hi @Thrombus thank you so much for your responses. Greatly appreciated! I just had one more question with regards to the sample demo you posted here (Fragment with ViewPager of Fragments). I'm doing the same thing, but I'm having an issue where my Fragment(State)PagerAdapter is keeping references to the old fragments. I can verify this with the getId() call. Hence my callback methods aren't being called on the correct fragments. Have you ever seen this happen?Nannettenanni
@clu: As I wrote, "I try to avoid [nested fragments] wherever possible". That sample is my one-and-only nested fragments sample. If you can reproduce the problems that you are seeing with the sample, contact me directly or file an issue on the cw-omnibus repo, and I can take a look at it. Otherwise, my nested fragments experience is very limited, and so I have not run into the problem that you describe. Note, though, that getId() on a fragment is the container the fragment is in, IIRC, not a unique ID of the fragment itself.Thrombus
Hi @CommonsWare, sorry I meant to respond to this a while ago! Thank you for your help. I actually figured this out. Basically I was passing the adapter already created fragments in getItem() call. This is wrong since after rotating, the FragmentManager re-instantiates them. I then try to access the original, already created Fragments, which are no longer being used by the FragmentManager. I fixed this by returning an Adapter that creates the fragments in getItem().Nannettenanni
@Thrombus I don't really understand what is nested fragments. What I have done is to create a viewpager and it holds several fragments, each fragments is responsible to create a listview content. But the list view is not visible in every fragments rather than the first one. Do you have example code at your github? I can't find similar example.Fiction
@Stallman: I have a link to an example in the "UPDATE" portion of my answer.Thrombus
Ok, I found it. But I still can't solve my problem for several days. If you can, please help this question: #25195981 THANK YOU!!!Fiction
@Thrombus thanks for your post. Is there way to implement nested ViewPager..please suggest.Maura
very intelligent implementation and very easy to implement, @Thrombus thank you so much againHydrolyze
@Thrombus unfortunately your solution doesnt work on my code sir, could you show my post? thanks #40448376Mazel
B
72

To implement the View pager within a fragment use getChildFragmentManager() instead of getFragmentManager(). You can call setAdapter() for the ViewPager from onCreateView() or onViewCreated(), that is not a matter.

I hope this will help you guys.

Bos answered 30/10, 2014 at 13:39 Comment(3)
@Raj, can you provide a sample?Zindman
Subtle yet so so important.Goldagoldarina
@AlexChe Can you share code snippet? I can't seem to implement it.Circassia
T
62

UPDATE: Since this answer was originally written, you now can have nested fragments, which means it is possible to have a ViewPager use fragments for pages and be in a fragment itself. This sample project demonstrates the technique.

I now return you to your regularly-scheduled answer, presented in its entirety...


Quoting myself from the book:

The simplest way to use a ViewPager is to have it page fragments in and out of the screen based on user swipes. This only works if the ViewPager itself is not contained within a fragment, as you cannot have fragments nested inside of other fragments.

Quoting Dianne Hackborn:

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.

It is perfectly possible to put a ViewPager inside a Fragment, so long as the contents of the ViewPager do not themselves contain fragments. Since the concrete implementations of PagerAdapter supplied by the Android Support package use fragments, you have to roll your own fragment-less PagerAdapter to put the ViewPager in a fragment.

I will endeavor to make this point clearer in the next edition of the book (unless you're British, in which case I'll endeavour to make this point clearer :-).

Thrombus answered 23/8, 2012 at 11:8 Comment(16)
I'm not British :) Thanks for reply. Can you add something about UI instantiation in onActivityCreated() method?Chinua
@silk: There is nothing intrinsically wrong about setting up your UI in onActivityCreated(). Sometimes that's necessary -- I can't seem to get a MapView to work with a native (non-backport) fragment unless I wait until onActivityCreated() to create it. That being said, since fragments-in-fragments is officially not supported, even if onActivityCreated() seems to help you today, it might not help you in some future version of the SDK or Android Support package.Thrombus
Good news, Nested Fragments are supported in 4.2. See Android 4.2 APIsLeslileslie
Hey @CommonsWare, because the support was just added to the new API, it doesn't mean it'll work on older APIs does it? That is, if we ran this on gingerbread, would it still work? If it worked, would it leak memory? I'm asking because I know in the past this was happening with one of my projects, so I'm wondering if you know about it. Thanks!Nannettenanni
@clu: "it doesn't mean it'll work on older APIs does it?" -- the Android Support package's backport of fragments includes nested fragment support. The sample project I link to above uses that.Thrombus
Hey @CommonsWare, thanks for the link. You're right, it does work on older devices. The issue I was facing earlier was when I had a ViewPager of Fragments within a ListView Header (using addHeaderView()). The ListView itself was also inside a fragment. I printed the onDestroy() and onCreate() method calls and basically, on rotate, the fragments in the viewpager were not being destroyed. Hence leaking the fragments. Have you ever seen anything like this?Nannettenanni
@clu: The only way what you indicated has a shot of working properly is if you are using nested fragments (e.g., the ViewPager is using a child FragmentManager). If you are, then in theory there should be no problems, but nested fragments are sometimes problematic in general, so I try to avoid them wherever possible.Thrombus
Hi @Thrombus thank you so much for your responses. Greatly appreciated! I just had one more question with regards to the sample demo you posted here (Fragment with ViewPager of Fragments). I'm doing the same thing, but I'm having an issue where my Fragment(State)PagerAdapter is keeping references to the old fragments. I can verify this with the getId() call. Hence my callback methods aren't being called on the correct fragments. Have you ever seen this happen?Nannettenanni
@clu: As I wrote, "I try to avoid [nested fragments] wherever possible". That sample is my one-and-only nested fragments sample. If you can reproduce the problems that you are seeing with the sample, contact me directly or file an issue on the cw-omnibus repo, and I can take a look at it. Otherwise, my nested fragments experience is very limited, and so I have not run into the problem that you describe. Note, though, that getId() on a fragment is the container the fragment is in, IIRC, not a unique ID of the fragment itself.Thrombus
Hi @CommonsWare, sorry I meant to respond to this a while ago! Thank you for your help. I actually figured this out. Basically I was passing the adapter already created fragments in getItem() call. This is wrong since after rotating, the FragmentManager re-instantiates them. I then try to access the original, already created Fragments, which are no longer being used by the FragmentManager. I fixed this by returning an Adapter that creates the fragments in getItem().Nannettenanni
@Thrombus I don't really understand what is nested fragments. What I have done is to create a viewpager and it holds several fragments, each fragments is responsible to create a listview content. But the list view is not visible in every fragments rather than the first one. Do you have example code at your github? I can't find similar example.Fiction
@Stallman: I have a link to an example in the "UPDATE" portion of my answer.Thrombus
Ok, I found it. But I still can't solve my problem for several days. If you can, please help this question: #25195981 THANK YOU!!!Fiction
@Thrombus thanks for your post. Is there way to implement nested ViewPager..please suggest.Maura
very intelligent implementation and very easy to implement, @Thrombus thank you so much againHydrolyze
@Thrombus unfortunately your solution doesnt work on my code sir, could you show my post? thanks #40448376Mazel
P
4

While Initializing the SetionPageAdapter for ViewPager use "getChildFragmentManager()".

mSectionsPagerAdapter = new SectionsPagerAdapter(getChildFragmentManager());

Preface answered 22/7, 2015 at 9:41 Comment(0)
M
0

it is possible try to do this code and save view_pager_fragment.xml

<android.support.design.widget.AppBarLayout
    android:id="@+id/appBarLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

    <android.support.design.widget.TabLayout
        android:id="@+id/tab"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</android.support.design.widget.AppBarLayout>

<android.support.v4.view.ViewPager
    android:id="@+id/viewpager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior" />

ViewPagerFragment

ViewPager viewPager;
TabLayout tabLayout;

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.tracks, container, false);

    viewPager = (ViewPager) view.findViewById(R.id.viewpager);
    tabLayout = (TabLayout) view.findViewById(R.id.tab);

    return view;
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    setupViewPager(viewPager);
    tabLayout.setupWithViewPager(viewPager);

    tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
        @Override
        public void onTabSelected(TabLayout.Tab tab) {

        }

        @Override
        public void onTabUnselected(TabLayout.Tab tab) {

        }

        @Override
        public void onTabReselected(TabLayout.Tab tab) {

        }
    });
}

private void setupViewPager(ViewPager viewPager) {
    ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager());``


    viewPager.setAdapter(viewPagerAdapter);
}

public class ViewPagerAdapter extends FragmentStatePagerAdapter {
    public ViewPagerAdapter(FragmentManager manager) {
        super(manager);
    }

    @Override
    public Fragment getItem(int position) {
        if(position == 0) return new SampleFragment();
        if(position == 1) return new SampleFragment2();
        if(position == 2) return new SampleFragment3();
        throw new IllegalStateException("Unexpected position " + position);
    }

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

    @Override
    public CharSequence getPageTitle(int position) {
        if(position == 0) return "Tab 1";
        if(position == 1) return "Tab 2";
        if(position == 2) return "Tab 3";
        throw new IllegalStateException("Unexpected position " + position);
    }
}
Mayemayeda answered 30/5, 2017 at 10:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.