The accepted answer is a non-optimal answer. Returning POSITION_NONE
from int getItemPosition(Object)
just destroys any hope of efficient fragment management, requiring all fragnents to be reinstantiated. It also ignores another problem. The FragmentPageAdapter keeps a cached copy of the Fragment in the FragmentManager, and it looks for those copies when instantiating a new Fragment. If it finds what it thinks is a matching fragment, the public Fragment getItem(int)
method is not called and the cached copy is used.
For example, lets say pages 0 and 1 are loaded, there will be cached fragments tagged with 0 and 1 in the FragmentManager. Now a page is inserted at index 0 (don't forget to call notifyDataSetChanged()
), the old index 0 becomes 1 and 1 becomes 2 (this is signalled using the method public int FragmentPageAdapter.getItemPosition(Object)
). For item 0 POSITION_NONE was returned (as it is the new position), so the method public Object instantiateItem(ViewGroup, int)
is called for position 0:
public Object instantiateItem(ViewGroup container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
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);
...
See what happens, a cached Fragment is found for position 0, and the fragment you wanted in position 1 is now in position 0, the fragment you wanted in position 2 is now in position 1 and in position 2 you get a new fragment returned by FragmentPageAdapter.getItem(int) that is a duplicate of position 1.
How to solve this? I have seen many suggestions on SO including:
- Always return POSITION_NONE from FragmentPageAdapter.getItemPosition() https://stackoverflow.com/a/7386616/2351246 - this answer ignores efficiciency and memory management (and will eventually not work without clearing out cached fragments)
- Tracking the fragment tags https://stackoverflow.com/a/12104399/2351246, this relies on implementation details that could change.
- And worst of all use magic by reverse engineering the FragmentPageAdapter implementation https://stackoverflow.com/a/13925130/2351246, this is just horrible.
There is no need to ruin the memory management or to track the internal implementation details of FragmentPagerAdapter
. The missing detail in all the answers is that a FragmentPagerAdapter that wants to reorder fragments must implement the method public long getItemId(int position)
also:
@Override
public long getItemId(int position) {
return System.identityHashCode(fragments.get(position));
}
This provides a non position based ID that can be used to look up the correct fragment in the FragmentManager cache even if it moves page.