Rectangle 27 46

The method in the linked answer doesn't work with FragmentStatePagerAdapter, but the second answer to the same linked question has a method that does.

Whilst this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference.

android - Is it possible to access the current Fragment being viewed b...

android android-fragments android-viewpager
Rectangle 27 46

The method in the linked answer doesn't work with FragmentStatePagerAdapter, but the second answer to the same linked question has a method that does.

Whilst this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference.

android - Is it possible to access the current Fragment being viewed b...

android android-fragments android-viewpager
Rectangle 27 91

Assuming you have created the correct xml layouts. It is now very simple to display fragments in a ViewPager that is hosted by another Fragment.

The code looks something like this in a parent Fragment:

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

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        ViewPager mViewPager = (ViewPager) view.findViewById(R.id.viewPager);
        mViewPager.setAdapter(new MyAdapter(getChildFragmentManager()));
    }

    public static class MyAdapter extends FragmentPagerAdapter {

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

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

        @Override
        public Fragment getItem(int position) {
            Bundle args = new Bundle();
            args.putInt(TextViewFragment.POSITION_KEY, position);
            return TextViewFragment.newInstance(args);
        }
    }

It is important to use Fragment.getChildFragmentManager() when instantiating the FragmentPagerAdapter. Also note that you cannot use Fragment.setRetainInstance() on the children fragments or you'll get an exception.

I'm seeing problems doing this (Fragment containing ViewPager, that is) with SherlockFragments and support.v4 release11, the ViewPager content gets set via adapter however the child fragments do not display :(

Hi Straya. I have tested my example with SherlockFragments and I didnt see an error. What is the error you are seeing?

Heya, I've also got Android-ViewPagerIndicator in the mix. ViewPager and ViewPagerIndicator are displayed (I have a TextView above ViewPager in its Fragment so I can see that that Fragment is displayed), but the ViewPager's child Fragments (provided via a FragmentPagerAdapter) are not displayed. I can swipe between pages, Indicator updates but still not child fragments.

Hi Straya, that could be the problem but I'm unsure. I have updated my github sample code to include a PagerTabStrip which is similar to the ViewPagerIndicator and it seems to work just fine.

Found that my FragmentPagerAdapter implemented its own isViewObject method, which wasn't a problem before nesting the ViewPager in its own fragment, once removed I now see the pages :) Pulled your code and ran it to boost my confidence and desire to get this working - so thanks Marco!

How to add a Fragment inside a ViewPager using Nested Fragment (Androi...

android android-fragments android-viewpager android-support-library android-nested-fragment
Rectangle 27 21

I just ran into this same problem. I had a Fragment that needed to host a ViewPager of Fragments. When I stacked another Fragment on top of my ViewPagerFragment, and then hit back, I would get an IllegalStateException. After checking the logs (when stacking a new Fragment), I found that my ViewPagerFragment would go through its lifecycle methods to stop and destroy, however its children Fragments in the the ViewPager would stay in onResume. I realized that the children Fragments should be managed by the FragmentManager for the ViewPagerFragment, not the FragmentManager of the Activity.

I understand at the time, the answers above were to get around the limitations of Fragments not being able to have children Fragments. However, now that the latest support library has support for Fragment nesting, you no longer need to hack around setting the adapter for your ViewPager, and passing getChildFragmentManager() is all you need. This has been working perfectly for me so far.

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    mView = inflater.inflate(R.layout.team_card_master, container, false);
    mViewPager = (ViewPager) mView.findViewById(R.id.team_card_master_view_pager);

    mAdapter = new ViewPagerAdapter(getChildFragmentManager());
    mViewPager.setAdapter(mAdapter);

    return mView;
}

getChildFragmentManager is for fragment How can I solve the problem inside the activity?

ViewPager
Fragments
Activity
FragmentManager
getFragmentManager()
Adapter

android - Display fragment viewpager within a fragment - Stack Overflo...

android android-fragments android-viewpager android-lifecycle android-nested-fragment
Rectangle 27 18

I've solved this problem the other way round. Instead of searching for the fragment from the activity, I'm registering the Fragment during it's onAttach() method at it's owner activity and de-registering it in the onStop() method. Basic Idea:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try{
        mActivity = (IMyActivity)activity;
    }catch(ClassCastException e){
        throw new ClassCastException(activity.toString() +" must be a IMyActivity");
    }

    mActivity.addFragment(this);
}

@Override
public void onStop() {
    mActivity.removeFragment(this);
    super.onStop();
}
public interface IFriendActivity {
    public void addFragment(Fragment f);
    public void removeFragment(Fragment f); 
}
public class MyActivity implements IMyActivity{

    [...]

    @Override
    public void addFragment(Fragment f) {
        mFragments.add(f);
    }

    @Override
    public void removeFragment(Fragment f) {
        mFragments.remove(f);
    }

}

android - Is it possible to access the current Fragment being viewed b...

android android-fragments android-viewpager
Rectangle 27 18

I've solved this problem the other way round. Instead of searching for the fragment from the activity, I'm registering the Fragment during it's onAttach() method at it's owner activity and de-registering it in the onStop() method. Basic Idea:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try{
        mActivity = (IMyActivity)activity;
    }catch(ClassCastException e){
        throw new ClassCastException(activity.toString() +" must be a IMyActivity");
    }

    mActivity.addFragment(this);
}

@Override
public void onStop() {
    mActivity.removeFragment(this);
    super.onStop();
}
public interface IFriendActivity {
    public void addFragment(Fragment f);
    public void removeFragment(Fragment f); 
}
public class MyActivity implements IMyActivity{

    [...]

    @Override
    public void addFragment(Fragment f) {
        mFragments.add(f);
    }

    @Override
    public void removeFragment(Fragment f) {
        mFragments.remove(f);
    }

}

android - Is it possible to access the current Fragment being viewed b...

android android-fragments android-viewpager
Rectangle 27 130

by selecting an option, I need to update the fragment that is currently visible.

A simple way of doing this is using a trick related to the FragmentPagerAdapter implementation:

case R.id.addText:
     Fragment page = getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":" + ViewPager.getCurrentItem());
     // based on the current position you can then cast the page to the correct 
     // class and call the method:
     if (ViewPager.getCurrentItem() == 0 && page != null) {
          ((FragmentClass1)page).updateList("new item");     
     } 
return true;

Please rethink your variable naming convention, using as the variable name the name of the class is very confusing(so no ViewPager ViewPager, use ViewPager mPager for example).

Is that fragment tag future-proof, if the API changes?

@Maarten No, it's just a common hack that has been used since the adapter appeared(and its source code was available), it hasn't changed yet. If you want something more reliable you'll need to use other options, most notable overriding the instantiateItem() method and getting references to the proper fragments yourself.

After hours of frustration and asking the pages directly from the mAdapter.getItem(position), awesome... At least now I know that the other 8512 trials didn't work. Thanks

Note this does not work if you use FragmentStatePagerAdapter rather than FragmentPagerAdapter.

android - Getting the current Fragment instance in the viewpager - Sta...

android android-fragments android-viewpager
Rectangle 27 129

by selecting an option, I need to update the fragment that is currently visible.

A simple way of doing this is using a trick related to the FragmentPagerAdapter implementation:

case R.id.addText:
     Fragment page = getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":" + ViewPager.getCurrentItem());
     // based on the current position you can then cast the page to the correct 
     // class and call the method:
     if (ViewPager.getCurrentItem() == 0 && page != null) {
          ((FragmentClass1)page).updateList("new item");     
     } 
return true;

Please rethink your variable naming convention, using as the variable name the name of the class is very confusing(so no ViewPager ViewPager, use ViewPager mPager for example).

Is that fragment tag future-proof, if the API changes?

@Maarten No, it's just a common hack that has been used since the adapter appeared(and its source code was available), it hasn't changed yet. If you want something more reliable you'll need to use other options, most notable overriding the instantiateItem() method and getting references to the proper fragments yourself.

After hours of frustration and asking the pages directly from the mAdapter.getItem(position), awesome... At least now I know that the other 8512 trials didn't work. Thanks

Note this does not work if you use FragmentStatePagerAdapter rather than FragmentPagerAdapter.

android - Getting the current Fragment instance in the viewpager - Sta...

android android-fragments android-viewpager
Rectangle 27 36

As of November 13th 2012, repacing fragments in a ViewPager seems to have become a lot easier. Google released Android 4.2 with support for nested fragments, and it's also supported in the new Android Support Library v11 so this will work all the way back to 1.6

It's very similiar to the normal way of replacing a fragment except you use getChildFragmentManager. It seems to work except the nested fragment backstack isn't popped when the user clicks the back button. As per the solution in that linked question, you need to manually call the popBackStackImmediate() on the child manager of the fragment. So you need to override onBackPressed() of the ViewPager activity where you'll get the current fragment of the ViewPager and call getChildFragmentManager().popBackStackImmediate() on it.

Getting the Fragment currently being displayed is a bit hacky as well, I used this dirty "android:switcher:VIEWPAGER_ID:INDEX" solution but you can also keep track of all fragments of the ViewPager yourself as explained in the second solution on this page.

So here's my code for a ViewPager with 4 ListViews with a detail view shown in the ViewPager when the user clicks a row, and with the back button working. I tried to include just the relevant code for the sake of brevity so leave a comment if you want the full app uploaded to GitHub.

public class HomeActivity extends SherlockFragmentActivity {
FragmentAdapter mAdapter;
ViewPager mPager;
TabPageIndicator mIndicator;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    mAdapter = new FragmentAdapter(getSupportFragmentManager());
    mPager = (ViewPager)findViewById(R.id.pager);
    mPager.setAdapter(mAdapter);
    mIndicator = (TabPageIndicator)findViewById(R.id.indicator);
    mIndicator.setViewPager(mPager);
}

// This the important bit to make sure the back button works when you're nesting fragments. Very hacky, all it takes is some Google engineer to change that ViewPager view tag to break this in a future Android update.
@Override
public void onBackPressed() {
    Fragment fragment = (Fragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":"+mPager.getCurrentItem());
    if (fragment != null) // could be null if not instantiated yet
    {
        if (fragment.getView() != null) {
            // Pop the backstack on the ChildManager if there is any. If not, close this activity as normal.
            if (!fragment.getChildFragmentManager().popBackStackImmediate()) {
                finish();
            }
        }
    }
}

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

    @Override
    public Fragment getItem(int position) {
        switch (position) {
        case 0:
            return ListProductsFragment.newInstance();
        case 1:
            return ListActiveSubstancesFragment.newInstance();
        case 2:
            return ListProductFunctionsFragment.newInstance();
        case 3:
            return ListCropsFragment.newInstance();
        default:
            return null;
        }
    }

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

 }
}
public class ListProductsFragment extends SherlockFragment {
private ListView list;

public static ListProductsFragment newInstance() {
    ListProductsFragment f = new ListProductsFragment();
    return f;
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View V = inflater.inflate(R.layout.list, container, false);
    list = (ListView)V.findViewById(android.R.id.list);
    list.setOnItemClickListener(new OnItemClickListener() {
        public void onItemClick(AdapterView<?> parent, View view,
            int position, long id) {
          // This is important bit
          Fragment productDetailFragment = FragmentProductDetail.newInstance();
          FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
          transaction.addToBackStack(null);
          transaction.replace(R.id.products_list_linear, productDetailFragment).commit();
        }
      });       
    return V;
}
}

@georgiecasey: I downloaded API 17, Android 4.2 which allowed me to start using getChildFragmentManager(). But I must be missing something from your solution, as I am now getting overlapping old + new fragments on the screen. Note: I'm trying to use your solution in concert with the Google example TabsAdapter code from developer.android.com/reference/android/support/v4/view/. Thanks for any suggestions and/or reference code.

What does R.id.products_list_linear refer to? Please link to your code as was offered.

@howettl the ID references to outer fragment member. Key to solve this problem is to use nested fragments (that way you don't have to manipulate viewpager adapter).

android - Replace Fragment inside a ViewPager - Stack Overflow

android view android-fragments android-viewpager pager
Rectangle 27 71

The solution by Louth was not enough to get things working for me, as the existing fragments were not getting destroyed. Motivated by this answer, I found that the solution is to override the getItemId(int position) method of FragmentPagerAdapter to give a new unique ID whenever there has been a change in the expected position of a Fragment.

private class MyPagerAdapter extends FragmentPagerAdapter {

    private TextProvider mProvider;
    private long baseId = 0;

    public MyPagerAdapter(FragmentManager fm, TextProvider provider) {
        super(fm);
        this.mProvider = provider;
    }

    @Override
    public Fragment getItem(int position) {
        return MyFragment.newInstance(mProvider.getTextForPosition(position));
    }

    @Override
    public int getCount() {
        return mProvider.getCount();
    }


    //this is called when notifyDataSetChanged() is called
    @Override
    public int getItemPosition(Object object) {
        // refresh all fragments when data set changed
        return PagerAdapter.POSITION_NONE;
    }


    @Override
    public long getItemId(int position) {
        // give an ID different from position when position has been changed
        return baseId + position;
    }

    /**
     * Notify that the position of a fragment has been changed.
     * Create a new ID for each position to force recreation of the fragment
     * @param n number of items which have been changed
     */
    public void notifyChangeInPosition(int n) {
        // shift the ID returned by getItemId outside the range of all previous fragments
        baseId += getCount() + n;
    }
}

Now, for example if you delete a single tab or make some change to the order, you should call notifyChangeInPosition(1) before calling notifyDataSetChanged(), which will ensure that all the Fragments will be recreated.

When notifyDataSetChanged() is called, the adapter calls the notifyChanged() method of the ViewPager which it is attached to. The ViewPager then checks the value returned by the adapter's getItemPosition() for each item, removing those items which return POSITION_NONE (see the source code) and then repopulating.

This is necessary to prevent the adapter from reloading the old fragment when the ViewPager is repopulating. You can easily understand why this works by looking at the source code for instantiateItem() in FragmentPagerAdapter.

final long itemId = getItemId(position);

    // Do we already have this fragment?
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);
    if (fragment != null) {
        if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
        mCurTransaction.attach(fragment);
    } else {
        fragment = getItem(position);
        if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
        mCurTransaction.add(container.getId(), fragment,
                makeFragmentName(container.getId(), itemId));
    }

As you can see, the getItem() method is only called if the fragment manager finds no existing fragments with the same Id. To me it seems like a bug that the old fragments are still attached even after notifyDataSetChanged() is called, but the documentation for ViewPager does clearly state that:

Note this class is currently under early design and development. The API will likely change in later updates of the compatibility library, requiring changes to the source code of apps when they are compiled against the newer version.

So hopefully the workaround given here will not be necessary in a future version of the support library.

Thanks so much for this answer! It has been grueling trying to solve this issue without using FragmentStatePagerAdapter. Great explanation too. Have you found a way to get getItem to trigger when coming back to the fragment? I've tried using your clever notifyDataSetChanged() in various places (ie onStop) without success

I don't really understand your question... Please post a new question including source code if you can't figure it out.

This works like a charm!

notifyDataSetChanged()

Wow! Amazing explanation. I had a pretty complex viewpager where I remove and insert items dynamically and wouldn't get it to work without understanding all this.

Remove Fragment Page from ViewPager in Android - Stack Overflow

android android-viewpager fragmentpageradapter
Rectangle 27 11

Edit: If you want to replace all the content of a page in a ViewPager you could still use nested fragments, but some changes are needed. Check the sample below(the FragmentActivity, setting the ViewPager and the PagerAdapter are the same as the previous snippet of code):

// this will act as a fragment container, representing one page in the ViewPager
public static class WrapperFragment extends Fragment implements
        ReplaceListener {

    public static WrapperFragment newInstance(int position) {
        WrapperFragment wp = new WrapperFragment();
        Bundle args = new Bundle();
        args.putInt("position", position);
        wp.setArguments(args);
        return wp;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        FrameLayout fl = new FrameLayout(getActivity());
        fl.setId(10000);
        if (getChildFragmentManager().findFragmentByTag("initialTag") == null) {
            InitialInnerFragment iif = new InitialInnerFragment();
            Bundle args = new Bundle();
            args.putInt("position", getArguments().getInt("position"));
            iif.setArguments(args);
            getChildFragmentManager().beginTransaction()
                    .add(10000, iif, "initialTag").commit();
        }
        return fl;
    }

    // required because it seems the getChildFragmentManager only "sees"
    // containers in the View of the parent Fragment.   
    @Override
    public void onReplace(Bundle args) {
        if (getChildFragmentManager().findFragmentByTag("afterTag") == null) {
            InnerFragment iif = new InnerFragment();
            iif.setArguments(args);
            getChildFragmentManager().beginTransaction()
                    .replace(10000, iif, "afterTag").addToBackStack(null)
                    .commit();
        }
    }

}

// the fragment that would initially be in the wrapper fragment
public static class InitialInnerFragment extends Fragment {

    private ReplaceListener mListener;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        mListener = (ReplaceListener) this.getParentFragment();
        LinearLayout ll = new LinearLayout(getActivity());
        Button b = new Button(getActivity());
        b.setGravity(Gravity.CENTER_HORIZONTAL);
        b.setText("Frame " + getArguments().getInt("position"));
        b.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Bundle args = new Bundle();
                args.putInt("positionInner",
                        getArguments().getInt("position"));
                if (mListener != null) {
                    mListener.onReplace(args);
                }
            }
        });
        ll.setOrientation(LinearLayout.VERTICAL);
        ll.addView(b, new LinearLayout.LayoutParams(250,
                LinearLayout.LayoutParams.WRAP_CONTENT));
        return ll;
    }

}

public static class InnerFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        TextView tv = new TextView(getActivity());
        tv.setText("InnerFragment in the outher Fragment with position "
                + getArguments().getInt("positionInner"));
        return tv;
    }

}

public interface ReplaceListener {
    void onReplace(Bundle args);
}

At a quick look it works, but issues may appear as I haven't tested it to much.

Can somebody show a simple example of how to do this?

Using nested fragments seems pretty easy, until Commonsware comes with a more elaborated sample you can try the code below:

public class NestedFragments extends FragmentActivity {

    @Override
    protected void onCreate(Bundle arg0) {
        super.onCreate(arg0);
        ViewPager vp = new ViewPager(this);
        vp.setId(5000);
        vp.setAdapter(new MyAdapter(getSupportFragmentManager()));
        setContentView(vp);
    }

    private static class MyAdapter extends FragmentPagerAdapter {

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

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

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

    public static class WrapperFragment extends Fragment {

        public static WrapperFragment newInstance(int position) {
            WrapperFragment wp = new WrapperFragment();
            Bundle args = new Bundle();
            args.putInt("position", position);
            wp.setArguments(args);
            return wp;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            LinearLayout ll = new LinearLayout(getActivity());
            FrameLayout innerFragContainer = new FrameLayout(getActivity());
            innerFragContainer.setId(1111);
            Button b = new Button(getActivity());
            b.setText("Frame " + getArguments().getInt("position"));
            b.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    InnerFragment innerFragment = new InnerFragment();
                    Bundle args = new Bundle();
                    args.putInt("positionInner",
                            getArguments().getInt("position"));
                    innerFragment.setArguments(args);
                    FragmentTransaction transaction = getChildFragmentManager()
                            .beginTransaction();
                    transaction.add(1111, innerFragment).commit();
                }
            });
            ll.setOrientation(LinearLayout.VERTICAL);
            ll.addView(b, new LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.MATCH_PARENT,
                    LinearLayout.LayoutParams.WRAP_CONTENT));
            ll.addView(innerFragContainer, new LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.MATCH_PARENT,
                    LinearLayout.LayoutParams.MATCH_PARENT));
            return ll;
        }

    }

    public static class InnerFragment extends Fragment {

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            TextView tv = new TextView(getActivity());
            tv.setText("InnerFragment in the outher Fragment with position "
                    + getArguments().getInt("positionInner"));
            return tv;
        }

    }

}

I was lazy and made everything in code but I'm sure it can work with inflated xml layouts.

I think that where you use transaction.add(1111, innerFragment).commit(); should use the replace method, because every times the button is pressed add to the hierarchy a layout. Also I refactor the example to use XML Layouts and try to change to replace all the content of the page. But I couldn't made it yet, always the Button keep on top. Any suggestion?

@Luksprog: This isn't the scenario I was thinking of. I'm going to be working on a sample where the ViewPager itself is hosted by a fragment and has fragments for its pages. That should just be a matter of passing getChildFragmentManager() to the PagerAdapter, but I want to try it first. Your example is demonstrating a ViewPager holding fragments that themselves hold other fragments -- perfectly reasonable, just not what I was thinking when I read the question.

@sabadow I used add as an example, you could use replace it it fits your needs. The Button remains there in my sample because the replace(or add) method doesn't remove the existing views from the container(set the height of innerFragContainer to WRAP_CONTENT and you'll see that the existing views remain with the addition of the new fragment at the bottom). And it will be difficult to make what you're trying to do because of the way the ViewPager works. I'll into a solution tomorrow.

@CommonsWare I actually hope the use wants that. Anyway, I've tested passing getChildFragmentManager to a PagerAdapter set for a ViewPager contained in a Fragment and it works quite well.

How to add a Fragment inside a ViewPager using Nested Fragment (Androi...

android android-fragments android-viewpager android-support-library android-nested-fragment
Rectangle 27 412

When the FragmentPagerAdapter adds a fragment to the FragmentManager, it uses a special tag based on the particular position that the fragment will be placed. FragmentPagerAdapter.getItem(int position) is only called when a fragment for that position does not exist. After rotating, Android will notice that it already created/saved a fragment for this particular position and so it simply tries to reconnect with it with FragmentManager.findFragmentByTag(), instead of creating a new one. All of this comes free when using the FragmentPagerAdapter and is why it is usual to have your fragment initialisation code inside the getItem(int) method.

Even if we were not using a FragmentPagerAdapter, it is not a good idea to create a new fragment every single time in Activity.onCreate(Bundle). As you have noticed, when a fragment is added to the FragmentManager, it will be recreated for you after rotating and there is no need to add it again. Doing so is a common cause of errors when working with fragments.

A usual approach when working with fragments is this:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ...

    CustomFragment fragment;
    if (savedInstanceState != null) {
        fragment = (CustomFragment) getSupportFragmentManager().findFragmentByTag("customtag");
    } else {
        fragment = new CustomFragment();
        getSupportFragmentManager().beginTransaction().add(R.id.container, fragment, "customtag").commit(); 
    }

    ...

}

When using a FragmentPagerAdapter, we relinquish fragment management to the adapter, and do not have to perform the above steps. By default, it will only preload one Fragment in front and behind the current position (although it does not destroy them unless you are using FragmentStatePagerAdapter). This is controlled by ViewPager.setOffscreenPageLimit(int). Because of this, directly calling methods on the fragments outside of the adapter is not guaranteed to be valid, because they may not even be alive.

To cut a long story short, your solution to use putFragment to be able to get a reference afterwards is not so crazy, and not so unlike the normal way to use fragments anyway (above). It is difficult to obtain a reference otherwise because the fragment is added by the adapter, and not you personally. Just make sure that the offscreenPageLimit is high enough to load your desired fragments at all times, since you rely on it being present. This bypasses lazy loading capabilities of the ViewPager, but seems to be what you desire for your application.

Another approach is to override FragmentPageAdapter.instantiateItem(View, int) and save a reference to the fragment returned from the super call before returning it (it has the logic to find the fragment, if already present).

For a fuller picture, have a look at some of the source of FragmentPagerAdapter (short) and ViewPager (long).

Loved the last part. Had a cache for the fragments and moved the put in the cache logic inside the FragmentPageAdapter.instantiateItem(View, int). Finally fixed a long lasting bug that only appears in the rotation/config change and was driving me crazy...

This fixed an issue I was having on rotation change. It maybe that I just can't see for looking, but is this documented? i.e. using the tag to recover from a previous state? It maybe an obvious answer, but I'm fairly new to Android development.

@Industrial-antidepressant It is the id of the container (e.g. a FrameLayout) to which the Fragment is to be added.

android - ViewPager and fragments — what's the right way to store frag...

android design-patterns android-fragments android-viewpager
Rectangle 27 410

When the FragmentPagerAdapter adds a fragment to the FragmentManager, it uses a special tag based on the particular position that the fragment will be placed. FragmentPagerAdapter.getItem(int position) is only called when a fragment for that position does not exist. After rotating, Android will notice that it already created/saved a fragment for this particular position and so it simply tries to reconnect with it with FragmentManager.findFragmentByTag(), instead of creating a new one. All of this comes free when using the FragmentPagerAdapter and is why it is usual to have your fragment initialisation code inside the getItem(int) method.

Even if we were not using a FragmentPagerAdapter, it is not a good idea to create a new fragment every single time in Activity.onCreate(Bundle). As you have noticed, when a fragment is added to the FragmentManager, it will be recreated for you after rotating and there is no need to add it again. Doing so is a common cause of errors when working with fragments.

A usual approach when working with fragments is this:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ...

    CustomFragment fragment;
    if (savedInstanceState != null) {
        fragment = (CustomFragment) getSupportFragmentManager().findFragmentByTag("customtag");
    } else {
        fragment = new CustomFragment();
        getSupportFragmentManager().beginTransaction().add(R.id.container, fragment, "customtag").commit(); 
    }

    ...

}

When using a FragmentPagerAdapter, we relinquish fragment management to the adapter, and do not have to perform the above steps. By default, it will only preload one Fragment in front and behind the current position (although it does not destroy them unless you are using FragmentStatePagerAdapter). This is controlled by ViewPager.setOffscreenPageLimit(int). Because of this, directly calling methods on the fragments outside of the adapter is not guaranteed to be valid, because they may not even be alive.

To cut a long story short, your solution to use putFragment to be able to get a reference afterwards is not so crazy, and not so unlike the normal way to use fragments anyway (above). It is difficult to obtain a reference otherwise because the fragment is added by the adapter, and not you personally. Just make sure that the offscreenPageLimit is high enough to load your desired fragments at all times, since you rely on it being present. This bypasses lazy loading capabilities of the ViewPager, but seems to be what you desire for your application.

Another approach is to override FragmentPageAdapter.instantiateItem(View, int) and save a reference to the fragment returned from the super call before returning it (it has the logic to find the fragment, if already present).

For a fuller picture, have a look at some of the source of FragmentPagerAdapter (short) and ViewPager (long).

Loved the last part. Had a cache for the fragments and moved the put in the cache logic inside the FragmentPageAdapter.instantiateItem(View, int). Finally fixed a long lasting bug that only appears in the rotation/config change and was driving me crazy...

This fixed an issue I was having on rotation change. It maybe that I just can't see for looking, but is this documented? i.e. using the tag to recover from a previous state? It maybe an obvious answer, but I'm fairly new to Android development.

@Industrial-antidepressant It is the id of the container (e.g. a FrameLayout) to which the Fragment is to be added.

android - ViewPager and fragments — what's the right way to store frag...

android design-patterns android-fragments android-viewpager
Rectangle 27 2

FOR NESTED FRAGMENTS (for example, when using a ViewPager)

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    YourFragment frag = (YourFragment) getChildFragmentManager().getFragments().get(viewPager.getCurrentItem());
    frag.yourMethod(data);  // Method for callback in YourFragment
    super.onActivityResult(requestCode, resultCode, data);
}
public void yourMethod(Intent data){
    // Do whatever you want with your data
}

android - onActivityResult is not being called in Fragment - Stack Ove...

android android-fragments android-activity
Rectangle 27 2

Replacing fragments in a viewpager is quite involved but is very possible and can look super slick. First, you need to let the viewpager itself handle the removing and adding of the fragments. What is happening is when you replace the fragment inside of SearchFragment, your viewpager retains its fragment views. So you end up with a blank page because the SearchFragment gets removed when you try to replace it.

The solution is to create a listener inside of your viewpager that will handle changes made outside of it so first add this code to the bottom of your adapter.

public interface nextFragmentListener {
    public void fragment0Changed(String newFragmentIdentification);
}

Then you need to create a private class in your viewpager that becomes a listener for when you want to change your fragment. For example you could add something like this. Notice that it implements the interface that was just created. So whenever you call this method, it will run the code inside of the class below.

private final class fragmentChangeListener implements nextFragmentListener {


    @Override
    public void fragment0Changed(String fragment) {
        //I will explain the purpose of fragment0 in a moment
        fragment0 = fragment;
        manager.beginTransaction().remove(fragAt0).commit();

        switch (fragment){
            case "searchFragment":
                fragAt0 = SearchFragment.newInstance(listener);
                break;
            case "searchResultFragment":
                fragAt0 = Fragment_Table.newInstance(listener);
                break;
        }

        notifyDataSetChanged();
    }

There are two main things to point out here:

  • fragAt0 is a "flexible" fragment. It can take on whatever fragment type you give it. This allows it to become your best friend in changing the fragment at position 0 to the fragment you desire.

Notice the listeners that are placed in the 'newInstance(listener)constructor. These are how you will callfragment0Changed(String newFragmentIdentification)`. The following code shows how you create the listener inside of your fragment.

public static Fragment_Journals newInstance(nextFragmentListener listener){
            listenerSearch = listener;
            return new Fragment_Journals();
        }

You could call the change inside of your onPostExecute

private class SearchAsyncTask extends AsyncTask<Void, Void, Void>{

    protected Void doInBackground(Void... params){
        .
        .//some more operation
        .
    }
    protected void onPostExecute(Void param){

        listenerSearch.fragment0Changed("searchResultFragment");
    }

}

This would trigger the code inside of your viewpager to switch your fragment at position zero fragAt0 to become a new searchResultFragment. There are two more small pieces you would need to add to the viewpager before it became functional.

One would be in the getItem override method of the viewpager.

@Override
public Fragment getItem(int index) {

    switch (index) {
    case 0:
        //this is where it will "remember" which fragment you have just selected. the key is to set a static String fragment at the top of your page that will hold the position that you had just selected.  

        if(fragAt0 == null){

            switch(fragment0){
            case "searchFragment":
                fragAt0 = FragmentSearch.newInstance(listener);
                break;
            case "searchResultsFragment":
                fragAt0 = FragmentSearchResults.newInstance(listener);
                break;
            }
        }
        return fragAt0;
    case 1:
        // Games fragment activity
        return new CreateFragment();

    }

Now without this final piece you would still get a blank page. Kind of lame, but it is an essential part of the viewPager. You must override the getItemPosition method of the viewpager. Ordinarily this method will return POSITION_UNCHANGED which tells the viewpager to keep everything the same and so getItem will never get called to place the new fragment on the page. Here's an example of something you could do

public int getItemPosition(Object object)
{
    //object is the current fragment displayed at position 0.  
    if(object instanceof SearchFragment && fragAt0 instanceof SearchResultFragment){
        return POSITION_NONE;
    //this condition is for when you press back
    }else if{(object instanceof SearchResultFragment && fragAt0 instanceof SearchFragment){
        return POSITION_NONE;
    }
        return POSITION_UNCHANGED
}

Like I said, the code gets very involved, but you basically have to create a custom adapter for your situation. The things I mentioned will make it possible to change the fragment. It will likely take a long time to soak everything in so I would be patient, but it will all make sense. It is totally worth taking the time because it can make a really slick looking application.

Here's the nugget for handling the back button. You put this inside your MainActivity

public void onBackPressed() {
    if(mViewPager.getCurrentItem() == 0) {
        if(pagerAdapter.getItem(0) instanceof FragmentSearchResults){
            ((Fragment_Table) pagerAdapter.getItem(0)).backPressed();
        }else if (pagerAdapter.getItem(0) instanceof FragmentSearch) {
            finish();
        }
    }

You will need to create a method called backPressed() inside of FragmentSearchResults that calls fragment0changed. This in tandem with the code I showed before will handle pressing the back button. Good luck with your code to change the viewpager. It takes a lot of work, and as far as I have found, there aren't any quick adaptations. Like I said, you are basically creating a custom viewpager adapter, and letting it handle all of the necessary changes using listeners

android - Replace Fragment inside a ViewPager - Stack Overflow

android view android-fragments android-viewpager pager
Rectangle 27 134

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();
}

works for me too, but how to implement back button to show the first fragment?

Great explanation but could you post full source code of this, specifically FirstPageFragment and SecondPageFragment classes.

I works for me as well, but I haven't been able to reverse the action, it's to say, and I think what @user1159819 means, once you are in NextFragment, you might want to go back to FirstFragment, I don't see how to do it with this implementation

How do you add the FirstPageFragmentListener to the Bundle in newInstance() ?

Can you add the code where you handle with FirstPageFragment.newInstance() listener param?

android - Replace Fragment inside a ViewPager - Stack Overflow

android view android-fragments android-viewpager pager
Rectangle 27 94

First of all keep track of all the "active" fragment pages. In this case, you keep track of the fragment pages in the FragmentStatePagerAdapter, which is used by the ViewPager.

@Override
public Fragment getItem(int index) {
    Fragment myFragment = MyFragment.newInstance();
    mPageReferenceMap.put(index, myFragment);
    return myFragment;
}

To avoid keeping a reference to "inactive" fragment pages, you need to implement the FragmentStatePagerAdapter's destroyItem(...) method:

@Override
public void destroyItem (ViewGroup container, int position, Object object) {
    super.destroyItem(container, position, object);
    mPageReferenceMap.remove(position);
}

and when you need to access the currently visible page, you then call:

int index = mViewPager.getCurrentItem();
MyAdapter adapter = ((MyAdapter)mViewPager.getAdapter());
MyFragment fragment = adapter.getFragment(index);

Where the MyAdapter's getFragment(int) method looks like this:

public MyFragment getFragment(int key) {
    return mPageReferenceMap.get(key);
}

Can you please explain clearly? Where do I need to keep this FragmentStatePagerAdapter class in my code? BTW I have 3 fragment classes, not single MyFragmentClass.

Even better if you update mPageReferenceMap in instantiateItem instead of getItem because it also works after orientation change. '@Override public Object instantiateItem(ViewGroup container, int position) { Fragment fragment = (Fragment)Super.instantiateItem(container, position); mPageReferenceMap.put(position, fragment); return fragment; }'

This answer is really bad! You shouldn't keep the cache of a ViewPager manually since it handle it by itself. The ViewPager decide when fragment should be allocated or not, what to pause and when, keeping a reference on every fragment will add some useless overhead and is not optimized for the memory.

android - Getting the current Fragment instance in the viewpager - Sta...

android android-fragments android-viewpager
Rectangle 27 94

First of all keep track of all the "active" fragment pages. In this case, you keep track of the fragment pages in the FragmentStatePagerAdapter, which is used by the ViewPager.

@Override
public Fragment getItem(int index) {
    Fragment myFragment = MyFragment.newInstance();
    mPageReferenceMap.put(index, myFragment);
    return myFragment;
}

To avoid keeping a reference to "inactive" fragment pages, you need to implement the FragmentStatePagerAdapter's destroyItem(...) method:

@Override
public void destroyItem (ViewGroup container, int position, Object object) {
    super.destroyItem(container, position, object);
    mPageReferenceMap.remove(position);
}

and when you need to access the currently visible page, you then call:

int index = mViewPager.getCurrentItem();
MyAdapter adapter = ((MyAdapter)mViewPager.getAdapter());
MyFragment fragment = adapter.getFragment(index);

Where the MyAdapter's getFragment(int) method looks like this:

public MyFragment getFragment(int key) {
    return mPageReferenceMap.get(key);
}

Can you please explain clearly? Where do I need to keep this FragmentStatePagerAdapter class in my code? BTW I have 3 fragment classes, not single MyFragmentClass.

Even better if you update mPageReferenceMap in instantiateItem instead of getItem because it also works after orientation change. '@Override public Object instantiateItem(ViewGroup container, int position) { Fragment fragment = (Fragment)Super.instantiateItem(container, position); mPageReferenceMap.put(position, fragment); return fragment; }'

This answer is really bad! You shouldn't keep the cache of a ViewPager manually since it handle it by itself. The ViewPager decide when fragment should be allocated or not, what to pause and when, keeping a reference on every fragment will add some useless overhead and is not optimized for the memory.

android - Getting the current Fragment instance in the viewpager - Sta...

android android-fragments android-viewpager
Rectangle 27 104

You are running into a problem because you are instantiating and keeping references to your fragments outside of PagerAdapter.getItem, and are trying to use those references independently of the ViewPager. As Seraph says, you do have guarantees that a fragment has been instantiated/added in a ViewPager at a particular time - this should be considered an implementation detail. A ViewPager does lazy loading of its pages; by default it only loads the current page, and the one to the left and right.

If you put your app into the background, the fragments that have been added to the fragment manager are saved automatically. Even if your app is killed, this information is restored when you relaunch your app.

Now consider that you have viewed a few pages, Fragments A, B and C. You know that these have been added to the fragment manager. Because you are using FragmentPagerAdapter and not FragmentStatePagerAdapter, these fragments will still be added (but potentially detached) when you scroll to other pages.

Consider that you then background your application, and then it gets killed. When you come back, Android will remember that you used to have Fragments A, B and C in the fragment manager and so it recreates them for you and then adds them. However, the ones that are added to the fragment manager now are NOT the ones you have in your fragments list in your Activity.

The FragmentPagerAdapter will not try to call getPosition if there is already a fragment added for that particular page position. In fact, since the fragment recreated by Android will never be removed, you have no hope of replacing it with a call to getPosition. Getting a handle on it is also pretty difficult to obtain a reference to it because it was added with a tag that is unknown to you. This is by design; you are discouraged from messing with the fragments that the view pager is managing. You should be performing all your actions within a fragment, communicating with the activity, and requesting to switch to a particular page, if necessary.

Now, back to your problem with the missing activity. Calling pagerAdapter.getItem(1)).update(id, name) after all of this has happened returns you the fragment in your list, which has yet to be added to the fragment manager, and so it will not have an Activity reference. I would that suggest your update method should modify some shared data structure (possibly managed by the activity), and then when you move to a particular page it can draw itself based on this updated data.

I like your solution since it is very elegant and will likely refactor my code however as you said I wanted to keep 100% of the logic and data within the fragment. Your solution would require pretty much Keeping all the data in the FragmentActivity and then use each Fragment simply to handle display Logic. As I said I prefer this but the interaction between fragments is super heavy and It might get annoying to manage. Anyways Thank you for your detailed explanation. You explained this much better than I could.

Short: never hold a reference to a Fragment outside of the Adapter

So how does an Activity instance access the FragmentPagerAdapter instance if it did not instantiate the FragmentPagerAdapter? Won't future instances just reinstantiate the FragmentPagerAdapter and all of its fragment instances? Should the FragmentPagerAdapter implement all fragment interfaces to manage communication between fragments?

the statement "Getting a handle on it is also pretty difficult to obtain a reference to it because it was added with a tag that is unknown to you. This is by design; you are discouraged from messing with the fragments that the view pager is managing." is false. it is very easy to obtain a reference by calling instantiateItem and you actually should do so in onCreate of your activity. see details here: stackoverflow.com/questions/14035090/

in general it's dodgy to instantiate fragments yourself without loading because in a "destroy and recreate" scenario you might end up handling a separate fragment to the one actually recreated and shown

Sign up for our newsletter and get our top new questions delivered to your inbox (see an example).

android - support FragmentPagerAdapter holds reference to old fragment...

android android-fragments
Rectangle 27 3

For me the best solution is to have 3 network calls, each for the fragment in the ViewPager. Let the fragments manage their calls on their own as you would do on a single fragment Activity. This way you'll even be able to show and hide progress on each fragment if you want the proper UX. I recommend using Switcher for that

The question is can you make separate calls? Bacause if you can't it would be making the same big call 3 times which is not the smartest idea. In this second case you could start every fragment with progress spinning and let the activity make the call. For me the prefferable solution to communicate with ViewPager fragments would be using events. You just make the call in the activity and after it's loaded you split the data respectively and send 3 events with the data inside it and let ach fragment response to them. Just register to EventBus inside onAttach and unregister in onDetach methods

android - Update Fragment in ViewPager From getItemPosition - Stack Ov...

android android-fragments android-viewpager fragmentpageradapter android-tablayout