[android] Remove Fragment Page from ViewPager in Android

I'm trying to dynamically add and remove Fragments from a ViewPager, adding works without any problems, but removing doesn't work as expected.

Everytime I want to remove the current item, the last one gets removed.

I also tried to use an FragmentStatePagerAdapter or return POSITION_NONE in the adapter's getItemPosition method.

What am I doing wrong?

Here's a basic example:

MainActivity.java

public class MainActivity extends FragmentActivity implements TextProvider {

    private Button mAdd;
    private Button mRemove;
    private ViewPager mPager;

    private MyPagerAdapter mAdapter;

    private ArrayList<String> mEntries = new ArrayList<String>();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mEntries.add("pos 1");
        mEntries.add("pos 2");
        mEntries.add("pos 3");
        mEntries.add("pos 4");
        mEntries.add("pos 5");

        mAdd = (Button) findViewById(R.id.add);
        mRemove = (Button) findViewById(R.id.remove);
        mPager = (ViewPager) findViewById(R.id.pager);

        mAdd.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                addNewItem();
            }
        });

        mRemove.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                removeCurrentItem();
            }
        });

        mAdapter = new MyPagerAdapter(this.getSupportFragmentManager(), this);

        mPager.setAdapter(mAdapter);

    }

    private void addNewItem() {
        mEntries.add("new item");
        mAdapter.notifyDataSetChanged();
    }

    private void removeCurrentItem() {
        int position = mPager.getCurrentItem();
        mEntries.remove(position);
        mAdapter.notifyDataSetChanged();
    }

    @Override
    public String getTextForPosition(int position) {
        return mEntries.get(position);
    }
    @Override
    public int getCount() {
        return mEntries.size();
    }


    private class MyPagerAdapter extends FragmentPagerAdapter {

        private TextProvider mProvider;

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

    }

}

TextProvider.java

public interface TextProvider {
    public String getTextForPosition(int position);
    public int getCount();
}

MyFragment.java

public class MyFragment extends Fragment {

    private String mText;

    public static MyFragment newInstance(String text) {
        MyFragment f = new MyFragment(text);
        return f;
    }

    public MyFragment() {
    }

    public MyFragment(String text) {
        this.mText = text;
    }

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

        View root = inflater.inflate(R.layout.fragment, container, false);

        ((TextView) root.findViewById(R.id.position)).setText(mText);

        return root;
    }

}

activity_main.xml

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

    <Button
        android:id="@+id/add"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="add new item" />

    <Button
        android:id="@+id/remove"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="remove current item" />

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

</LinearLayout>

fragment.xml

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

    <TextView
        android:id="@+id/position"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:textSize="35sp" />

</LinearLayout>

This question is related to android android-viewpager fragmentpageradapter

The answer is


i solved this problem by these steps

1- use FragmentPagerAdapter

2- in each fragment create a random id

fragment.id = new Random().nextInt();

3- override getItemPosition in adapter

@Override
public int getItemPosition(@NonNull Object object) {
    return PagerAdapter.POSITION_NONE;
}

4-override getItemId in adapter

 @Override
public long getItemId(int position) {
    return mDatasetFragments.get(position).id;
}

5- now delete code is

 adapter.mDatasetFragments.remove(< item to delete position >);
 adapter.notifyDataSetChanged();

this worked for me i hope help


add or remove fragment in viewpager dynamically.
Call setupViewPager(viewPager) on activity start. To load different fragment call setupViewPagerCustom(viewPager). e.g. on button click call: setupViewPagerCustom(viewPager);

    private void setupViewPager(ViewPager viewPager)
{
    ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
    adapter.addFrag(new fragmnet1(), "HOME");
    adapter.addFrag(new fragmnet2(), "SERVICES");

    viewPager.setAdapter(adapter);
}

private void setupViewPagerCustom(ViewPager viewPager)
{

    ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());

    adapter.addFrag(new fragmnet3(), "Contact us");
    adapter.addFrag(new fragmnet4(), "ABOUT US");


    viewPager.setAdapter(adapter);
}

//Viewpageradapter, handles the views

static class ViewPagerAdapter extends FragmentStatePagerAdapter
{
    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();

    public ViewPagerAdapter(FragmentManager manager){
        super(manager);
    }

    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }

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

    @Override
    public int getItemPosition(Object object){
        return PagerAdapter.POSITION_NONE;
    }

    public void addFrag(Fragment fragment, String title){
        mFragmentList.add(fragment);
        mFragmentTitleList.add(title);
    }

    @Override
    public CharSequence getPageTitle(int position){
        return mFragmentTitleList.get(position);
    }
}

The fragment must be already removed but the issue was viewpager save state

Try

myViewPager.setSaveFromParentEnabled(false);

Nothing worked but this solved the issue !

Cheers !


My final version code, fixed ALL bugs. It took me 3 days

Updated 2020/07/18: I had changed a lot of the source code and fix so many bugs, but I don't promise it still work today.

https://github.com/lin1987www/FragmentBuilder/blob/master/commonLibrary/src/main/java/android/support/v4/app/FragmentStatePagerAdapterFix.java

public class FragmentStatePagerAdapterFix extends PagerAdapter {
    private static final String TAG = FragmentStatePagerAdapterFix.class.getSimpleName();
    private static final boolean DEBUG = false;

    private WeakReference<FragmentActivity> wrFragmentActivity;
    private WeakReference<Fragment> wrParentFragment;
    private final FragmentManager mFragmentManager;
    private FragmentTransaction mCurTransaction = null;

    protected ArrayList<Fragment> mFragments = new ArrayList<>();
    protected ArrayList<FragmentState> mFragmentStates = new ArrayList<>();
    protected ArrayList<String> mFragmentTags = new ArrayList<>();
    protected ArrayList<String> mFragmentClassNames = new ArrayList<>();
    protected ArrayList<Bundle> mFragmentArgs = new ArrayList<>();

    private Fragment mCurrentPrimaryItem = null;
    private boolean[] mTempPositionChange;

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

    public FragmentActivity getFragmentActivity() {
        return wrFragmentActivity.get();
    }

    public Fragment getParentFragment() {
        return wrParentFragment.get();
    }

    public FragmentStatePagerAdapterFix(FragmentActivity activity) {
        mFragmentManager = activity.getSupportFragmentManager();
        wrFragmentActivity = new WeakReference<>(activity);
        wrParentFragment = new WeakReference<>(null);
    }

    public FragmentStatePagerAdapterFix(Fragment fragment) {
        mFragmentManager = fragment.getChildFragmentManager();
        wrFragmentActivity = new WeakReference<>(fragment.getActivity());
        wrParentFragment = new WeakReference<>(fragment);
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass) {
        add(fragClass, null, null);
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args) {
        add(fragClass, args, null);
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, String tag) {
        add(fragClass, null, tag);
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args, String tag) {
        add(fragClass, args, tag, getCount());
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args, String tag, int position) {
        mFragments.add(position, null);
        mFragmentStates.add(position, null);
        mFragmentTags.add(position, tag);
        mFragmentClassNames.add(position, fragClass.getName());
        mFragmentArgs.add(position, args);
        mTempPositionChange = new boolean[getCount()];
    }

    public void remove(int position) {
        if (position < getCount()) {
            mTempPositionChange = new boolean[getCount()];
            for (int i = position; i < mTempPositionChange.length; i++) {
                mTempPositionChange[i] = true;
            }
            mFragments.remove(position);
            mFragmentStates.remove(position);
            mFragmentTags.remove(position);
            mFragmentClassNames.remove(position);
            mFragmentArgs.remove(position);
        }
    }

    public void clear(){
        mFragments.clear();
        mFragmentStates.clear();
        mFragmentTags.clear();
        mFragmentClassNames.clear();
        mFragmentArgs.clear();
    }

    @Override
    public void startUpdate(ViewGroup container) {
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment;
        // If we already have this item instantiated, there is nothing
        // to do.  This can happen when we are restoring the entire pager
        // from its saved state, where the fragment manager has already
        // taken care of restoring the fragments we previously had instantiated.
        fragment = mFragments.get(position);
        if (fragment != null) {
            return fragment;
        }
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        FragmentState fs = mFragmentStates.get(position);
        if (fs != null) {
            fragment = fs.instantiate(getFragmentActivity(), getParentFragment());
            // Fix bug
            // http://stackoverflow.com/questions/11381470/classnotfoundexception-when-unmarshalling-android-support-v4-view-viewpagersav
            if (fragment.mSavedFragmentState != null) {
                fragment.mSavedFragmentState.setClassLoader(fragment.getClass().getClassLoader());
            }
        }
        if (fragment == null) {
            fragment = Fragment.instantiate(getFragmentActivity(), mFragmentClassNames.get(position), mFragmentArgs.get(position));
        }
        if (DEBUG) {
            Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
        }
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
        mFragments.set(position, fragment);
        mFragmentStates.set(position, null);
        mCurTransaction.add(container.getId(), fragment, mFragmentTags.get(position));
        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment) object;
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        if (DEBUG) {
            Log.v(TAG, "Removing item #" + position + ": f=" + object
                    + " v=" + ((Fragment) object).getView());
        }
        if (position < getCount()) {
            FragmentState fragmentState = new FragmentState(fragment);
            Fragment.SavedState savedState = mFragmentManager.saveFragmentInstanceState(fragment);
            if (savedState != null) {
                fragmentState.mSavedFragmentState = savedState.mState;
            }
            mFragmentStates.set(position, fragmentState);
            mFragments.set(position, null);
        }
        mCurTransaction.remove(fragment);
    }

    @Override
    public void setPrimaryItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment) object;
        if (fragment != mCurrentPrimaryItem) {
            if (mCurrentPrimaryItem != null) {
                mCurrentPrimaryItem.setMenuVisibility(false);
                mCurrentPrimaryItem.setUserVisibleHint(false);
            }
            if (fragment != null) {
                fragment.setMenuVisibility(true);
                fragment.setUserVisibleHint(true);
            }
            mCurrentPrimaryItem = fragment;
        }
    }

    @Override
    public void finishUpdate(ViewGroup container) {
        if (mCurTransaction != null) {
            mCurTransaction.commitAllowingStateLoss();
            mCurTransaction = null;
            mFragmentManager.executePendingTransactions();
            // Fix: Fragment is added by transaction. BUT didn't add to FragmentManager's mActive.
            for (Fragment fragment : mFragments) {
                if (fragment != null) {
                    fixActiveFragment(mFragmentManager, fragment);
                }
            }
        }
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return ((Fragment) object).getView() == view;
    }

    @Override
    public Parcelable saveState() {
        Bundle state = null;
        // ????? Fragments
        for (int i = 0; i < mFragments.size(); i++) {
            Fragment f = mFragments.get(i);
            if (f != null && f.isAdded()) {
                if (state == null) {
                    state = new Bundle();
                }
                String key = "f" + i;
                mFragmentManager.putFragment(state, key, f);
            }
        }
        if (mFragmentStates.size() > 0) {
            if (state == null) {
                state = new Bundle();
            }
            FragmentState[] fs = new FragmentState[mFragmentStates.size()];
            mFragmentStates.toArray(fs);
            state.putParcelableArray("states_fragment", fs);
        }
        return state;
    }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {
        if (state != null) {
            Bundle bundle = (Bundle) state;
            bundle.setClassLoader(loader);
            Parcelable[] fs = bundle.getParcelableArray("states_fragment");
            mFragments.clear();
            mFragmentStates.clear();
            mFragmentTags.clear();
            mFragmentClassNames.clear();
            mFragmentArgs.clear();
            if (fs != null) {
                for (int i = 0; i < fs.length; i++) {
                    FragmentState fragmentState = (FragmentState) fs[i];
                    mFragmentStates.add(fragmentState);
                    if (fragmentState != null) {
                        mFragmentArgs.add(fragmentState.mArguments);
                        mFragmentTags.add(fragmentState.mTag);
                        mFragmentClassNames.add(fragmentState.mClassName);
                    } else {
                        mFragmentArgs.add(null);
                        mFragmentTags.add(null);
                        mFragmentClassNames.add(null);
                    }
                    mFragments.add(null);
                }
            }
            Iterable<String> keys = bundle.keySet();
            for (String key : keys) {
                if (key.startsWith("f")) {
                    int index = Integer.parseInt(key.substring(1));
                    Fragment f = mFragmentManager.getFragment(bundle, key);
                    if (f != null) {
                        f.setMenuVisibility(false);
                        mFragments.set(index, f);
                        mFragmentArgs.set(index, f.mArguments);
                        mFragmentTags.set(index, f.mTag);
                        mFragmentClassNames.set(index, f.getClass().getName());
                    } else {
                        Log.w(TAG, "Bad fragment at key " + key);
                    }
                }
            }
            // If restore will change
            notifyDataSetChanged();
        }
    }

    public static void fixActiveFragment(FragmentManager fragmentManager, Fragment fragment) {
        FragmentManagerImpl fm = (FragmentManagerImpl) fragmentManager;
        if (fm.mActive != null) {
            int index = fragment.mIndex;
            Fragment origin = fm.mActive.get(index);
            if (origin != null) {
                if ((origin.mIndex != fragment.mIndex) || !(origin.equals(fragment))) {
                    Log.e(TAG,
                            String.format("fixActiveFragment: Not Equal! Origin: %s %s, Fragment: %s $s",
                                    origin.getClass().getName(), origin.mIndex,
                                    fragment.getClass().getName(), fragment.mIndex
                            ));
                }
            }
            fm.mActive.set(index, fragment);
        }
    }

    // Fix
    // http://stackoverflow.com/questions/10396321/remove-fragment-page-from-viewpager-in-android
    @Override
    public int getItemPosition(Object object) {
        int index = mFragments.indexOf(object);
        if (index < 0) {
            return PagerAdapter.POSITION_NONE;
        }
        boolean isPositionChange = mTempPositionChange[index];
        int result = PagerAdapter.POSITION_UNCHANGED;
        if (isPositionChange) {
            result = PagerAdapter.POSITION_NONE;
        }
        return result;
    }
}

You can combine both for better :

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

    @Override
    public int getItemPosition(Object object){

      if(Any_Reason_You_WantTo_Update_Positions) //this includes deleting or adding pages
 return PagerAdapter.POSITION_NONE;
    }
else
return PagerAdapter.POSITION_UNCHANGED; //this ensures high performance in other operations such as editing list items.

}

Try this solution. I have used databinding for binding view. You can use common "findViewById()" function.

public class ActCPExpense extends BaseActivity implements View.OnClickListener,  {
private static final String TAG = ActCPExpense.class.getSimpleName();
private Context mContext;
private ActCpLossBinding mBinding;

private ViewPagerAdapter adapter;



@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    try {
        setContentView(R.layout.act_cp_loss);
        mBinding = DataBindingUtil.setContentView(this, R.layout.act_cp_loss);
        mContext = ActCPExpense.this;
        initViewsAct();


    } catch (Exception e) {
        LogUtils.LOGE(TAG, e);
    }

}


private void initViewsAct() {

    adapter = new ViewPagerAdapter(getSupportFragmentManager());
    adapter.addFragment(FragmentCPPayee.newInstance(), "Title");
    mBinding.viewpager.setAdapter(adapter);
    mBinding.tab.setViewPager(mBinding.viewpager);



}



@Override
public boolean onOptionsItemSelected(MenuItem itemActUtility) {
    int i = itemActUtility.getItemId();
    if (i == android.R.id.home) {
        onBackPressed();

    } 

    return super.onOptionsItemSelected(itemActUtility);
}

@Override
public void onClick(View view) {
    super.onClick(view);
    int id = view.getId();
    if (id == R.id.btnAdd) {
        addFragment();

    } else if (id == R.id.btnDelete) {
        removeFragment();

    }


}
    private void addFragment(){
    adapter.addFragment(FragmentCPPayee.newInstance("Title");
    adapter.notifyDataSetChanged();
    mBinding.tab.setViewPager(mBinding.viewpager);
}
private void removeFragment(){
    adapter.removeItem(mBinding.viewpager.getCurrentItem());
    mBinding.tab.setViewPager(mBinding.viewpager);
}


class ViewPagerAdapter extends FragmentStatePagerAdapter {
    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();


    public ViewPagerAdapter(FragmentManager manager) {
        super(manager);
    }

    @Override
    public int getItemPosition(@NonNull Object object) {
        return PagerAdapter.POSITION_NONE;
    }


    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }

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


    public void addFragment(Fragment fragment, String title) {
        mFragmentList.add(fragment);
        mFragmentTitleList.add(title);

    }

    public void removeItem(int pos) {

        destroyItem(null, pos, mFragmentList.get(pos));
        mFragmentList.remove(pos);
        mFragmentTitleList.remove(pos);
        adapter.notifyDataSetChanged();
        mBinding.viewpager.setCurrentItem(pos - 1, false);
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return "Title " + String.valueOf(position + 1);
    }
}


}

Louth's answer works fine. But I don't think always return POSITION_NONE is a good idea. Because POSITION_NONE means that fragment should be destroyed and a new fragment will be created. You can check that in dataSetChanged function in the source code of ViewPager.

        if (newPos == PagerAdapter.POSITION_NONE) {
            mItems.remove(i);
            i--;
            ... not related code
            mAdapter.destroyItem(this, ii.position, ii.object);

So I think you'd better use an arraylist of weakReference to save all the fragments you have created. And when you add or remove some page, you can get the right position from your own arraylist.

 public int getItemPosition(Object object) {
    for (int i = 0; i < mFragmentsReferences.size(); i ++) {
        WeakReference<Fragment> reference = mFragmentsReferences.get(i);
        if (reference != null && reference.get() != null) {
            Fragment fragment = reference.get();
            if (fragment == object) {
                return i;
            }
        }
    }
    return POSITION_NONE;
}

According to the comments, getItemPosition is Called when the host view is attempting to determine if an item's position has changed. And the return value means its new position.

But this is not enought. We still have an important step to take. In the source code of FragmentStatePagerAdapter, there is an array named "mFragments" caches the fragments which are not destroyed. And in instantiateItem function.

if (mFragments.size() > position) {
        Fragment f = mFragments.get(position);
        if (f != null) {
            return f;
        }
    }

It returned the cached fragment directly when it find that cached fragment is not null. So there is a problem. From example, let's delete one page at position 2, Firstly, We remove that fragment from our own reference arraylist. so in getItemPosition it will return POSITION_NONE for that fragment, and then that fragment will be destroyed and removed from "mFragments".

 mFragments.set(position, null);

Now the fragment at position 3 will be at position 2. And instantiatedItem with param position 3 will be called. At this time, the third item in "mFramgents" is not null, so it will return directly. But actually what it returned is the fragment at position 2. So when we turn into page 3, we will find an empty page there.

To work around this problem. My advise is that you can copy the source code of FragmentStatePagerAdapter into your own project, and when you do add or remove operations, you should add and remove elements in the "mFragments" arraylist.

Things will be simpler if you just use PagerAdapter instead of FragmentStatePagerAdapter. Good Luck.


I hope this can help what you want.

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

    @Override
    public int getItemPosition(Object object){
        return PagerAdapter.POSITION_UNCHANGED;
    }
}

You could just override the destroyItem method

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    fragmentManager.beginTransaction().remove((Fragment) object).commitNowAllowingStateLoss();
}

I added a function "clearFragments" and I used that function to clear adapter before setting the new fragments. This calls the proper remove actions of Fragments. My pagerAdapter class:

private class ChartPagerAdapter extends FragmentPagerAdapter{
    private ArrayList<Fragment> fragmentList;

    ChartPagerAdapter(FragmentManager fm){
        super(fm);
        fragmentList = new ArrayList<>();
    }

    void setFragments(ArrayList<? extends Fragment> fragments){
        fragmentList.addAll(fragments);
    }

    void clearFragments(){
        for(Fragment fragment:fragmentList)
            getChildFragmentManager().beginTransaction().remove(fragment).commit();
        fragmentList.clear();
    }

    @Override
    public Fragment getItem(int i) {
        return fragmentList.get(i);
    }

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

}

2020 now.

Simple add this to PageAdapter:

override fun getItemPosition(`object`: Any): Int {
    return PagerAdapter.POSITION_NONE
}

I had the idea of simply copy the source code from android.support.v4.app.FragmentPagerAdpater into a custom class named CustumFragmentPagerAdapter. This gave me the chance to modify the instantiateItem(...) so that every time it is called, it removes / destroys the currently attached fragment before it adds the new fragment received from getItem() method.

Simply modify the instantiateItem(...) in the following way:

@Override
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);

    // remove / destroy current fragment
    if (fragment != null) {
        mCurTransaction.remove(fragment);
    }

    // get new fragment and add it
    fragment = getItem(position);
    mCurTransaction.add(container.getId(), fragment,    makeFragmentName(container.getId(), itemId));

    if (fragment != mCurrentPrimaryItem) {
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
    }

    return fragment;
}

I had some problems with FragmentStatePagerAdapter. After removing an item:

  • there was another item used for a position (an item which did not belong to the position but to a position next to it)
  • or some fragment was not loaded (there was only blank background visible on that page)

After lots of experiments, I came up with the following solution.

public class SomeAdapter extends FragmentStatePagerAdapter {

    private List<Item> items = new ArrayList<Item>();

    private boolean removing;

    @Override
    public Fragment getItem(int position) {
        ItemFragment fragment = new ItemFragment();
        Bundle args = new Bundle();
        // use items.get(position) to configure fragment
        fragment.setArguments(args);
        return fragment;
    }

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

    @Override
    public int getItemPosition(Object object) {
        if (removing) {
            return PagerAdapter.POSITION_NONE;
        }

        Item item = getItemOfFragment(object);

        int index = items.indexOf(item);
        if (index == -1) {
            return POSITION_NONE;
        } else {
            return index;
        }
    }

   public void addItem(Item item) {
        items.add(item);
        notifyDataSetChanged();
    }

    public void removeItem(int position) {
        items.remove(position);

        removing = true;
        notifyDataSetChanged();
        removing = false;
    }
}

This solution only uses a hack in case of removing an item. Otherwise (e.g. when adding an item) it retains the cleanliness and performance of an original code.

Of course, from the outside of the adapter, you call only addItem/removeItem, no need to call notifyDataSetChanged().


For future readers!

Now you can use ViewPager2 for dynamically adding, removing fragment from the viewpager.

Quoting form API reference

ViewPager2 replaces ViewPager, addressing most of its predecessor’s pain-points, including right-to-left layout support, vertical orientation, modifiable Fragment collections, etc.

Take look at MutableCollectionFragmentActivity.kt in googlesample/android-viewpager2 for an example of adding, removing fragments dynamically from the viewpager.


For your information:

Articles:

API reference

Release notes

Samples Repo: https://github.com/googlesamples/android-viewpager2


The ViewPager doesn't remove your fragments with the code above because it loads several views (or fragments in your case) into memory. In addition to the visible view, it also loads the view to either side of the visible one. This provides the smooth scrolling from view to view that makes the ViewPager so cool.

To achieve the effect you want, you need to do a couple of things.

  1. Change the FragmentPagerAdapter to a FragmentStatePagerAdapter. The reason for this is that the FragmentPagerAdapter will keep all the views that it loads into memory forever. Where the FragmentStatePagerAdapter disposes of views that fall outside the current and traversable views.

  2. Override the adapter method getItemPosition (shown below). When we call mAdapter.notifyDataSetChanged(); the ViewPager interrogates the adapter to determine what has changed in terms of positioning. We use this method to say that everything has changed so reprocess all your view positioning.

And here's the code...

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

    @Override
    public int getItemPosition(Object object){
        return PagerAdapter.POSITION_NONE;
    }

}

my working solution to remove fragment page from view pager

public class MyFragmentAdapter extends FragmentStatePagerAdapter {

    private ArrayList<ItemFragment> pages;

    public MyFragmentAdapter(FragmentManager fragmentManager, ArrayList<ItemFragment> pages) {
        super(fragmentManager);
        this.pages = pages;
    }

    @Override
    public Fragment getItem(int index) {
        return pages.get(index);
    }

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

    @Override
    public int getItemPosition(Object object) {
        int index = pages.indexOf (object);

        if (index == -1)
            return POSITION_NONE;
        else
            return index;
    }
}

And when i need to remove some page by index i do this

pages.remove(position); // ArrayList<ItemFragment>
adapter.notifyDataSetChanged(); // MyFragmentAdapter

Here it is my adapter initialization

MyFragmentAdapter adapter = new MyFragmentAdapter(getSupportFragmentManager(), pages);
viewPager.setAdapter(adapter);

Examples related to android

Under what circumstances can I call findViewById with an Options Menu / Action Bar item? How to implement a simple scenario the OO way My eclipse won't open, i download the bundle pack it keeps saying error log getting " (1) no such column: _id10 " error java doesn't run if structure inside of onclick listener Cannot retrieve string(s) from preferences (settings) strange error in my Animation Drawable how to put image in a bundle and pass it to another activity FragmentActivity to Fragment A failure occurred while executing com.android.build.gradle.internal.tasks

Examples related to android-viewpager

TabLayout tab selection How to disable or enable viewpager swiping in android Android ViewPager with bottom dots What is the difference between FragmentPagerAdapter and FragmentStatePagerAdapter? Getting the current Fragment instance in the viewpager How to implement a ViewPager with different Fragments / Layouts Update Fragment from ViewPager Getting the error "Java.lang.IllegalStateException Activity has been destroyed" when using tabs with ViewPager Android Viewpager as Image Slide Gallery dynamically add and remove view to viewpager

Examples related to fragmentpageradapter

What is the difference between FragmentPagerAdapter and FragmentStatePagerAdapter? How to implement a ViewPager with different Fragments / Layouts Remove Fragment Page from ViewPager in Android