Replace Fragment inside a ViewPager

There is another solution that does not need modifying source code of ViewPager and FragmentStatePagerAdapter, and it works with the FragmentPagerAdapter base class used by the author.

I’d like to start by answering the author’s question about which ID he should use; it is ID of the container, i.e. ID of the view pager itself. However, as you probably noticed yourself, using that ID in your code causes nothing to happen. I will explain why:

First of all, to make ViewPager repopulate the pages, you need to call notifyDataSetChanged() that resides in the base class of your adapter.

Second, ViewPager uses the getItemPosition() abstract method to check which pages should be destroyed and which should be kept. The default implementation of this function always returns POSITION_UNCHANGED, which causes ViewPager to keep all current pages, and consequently not attaching your new page. Thus, to make fragment replacement work, getItemPosition() needs to be overridden in your adapter and must return POSITION_NONE when called with an old, to be hidden, fragment as argument.

This also means that your adapter always needs to be aware of which fragment that should be displayed in position 0, FirstPageFragment or NextFragment. One way of doing this is supplying a listener when creating FirstPageFragment, which will be called when it is time to switch fragments. I think this is a good thing though, to let your fragment adapter handle all fragment switches and calls to ViewPager and FragmentManager.

Third, FragmentPagerAdapter caches the used fragments by a name which is derived from the position, so if there was a fragment at position 0, it will not be replaced even though the class is new. There are two solutions, but the simplest is to use the remove() function of FragmentTransaction, which will remove its tag as well.

That was a lot of text, here is code that should work in your case:

public class MyAdapter extends FragmentPagerAdapter
{
    static final int NUM_ITEMS = 2;
    private final FragmentManager mFragmentManager;
    private Fragment mFragmentAtPos0;

    public MyAdapter(FragmentManager fm)
    {
        super(fm);
        mFragmentManager = fm;
    }

    @Override
    public Fragment getItem(int position)
    {
        if (position == 0)
        {
            if (mFragmentAtPos0 == null)
            {
                mFragmentAtPos0 = FirstPageFragment.newInstance(new FirstPageFragmentListener()
                {
                    public void onSwitchToNextFragment()
                    {
                        mFragmentManager.beginTransaction().remove(mFragmentAtPos0).commit();
                        mFragmentAtPos0 = NextFragment.newInstance();
                        notifyDataSetChanged();
                    }
                });
            }
            return mFragmentAtPos0;
        }
        else
            return SecondPageFragment.newInstance();
    }

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

    @Override
    public int getItemPosition(Object object)
    {
        if (object instanceof FirstPageFragment && mFragmentAtPos0 instanceof NextFragment)
            return POSITION_NONE;
        return POSITION_UNCHANGED;
    }
}

public interface FirstPageFragmentListener
{
    void onSwitchToNextFragment();
}

Hope this helps anyone!

Leave a Comment