Remove Fragment Page from ViewPager in Android
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.
Source Code:
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.
Why this solution works
Overriding getItemPosition():
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.
Overriding getItemId():
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.
How to Remove Fragment from FragmentPagerAdapter?
First, I suggest that you consider altering your FragmentPagerAdapter
, to look more like the sample. You normally do not hold a list of fragments, any more than an ArrayAdapter
normally holds a list of Views
for the rows. Normally, you create the fragments on demand, and somebody else holds the list.
Then, to delete something, delete it from your model data (what the FragmentPagerAdapter
normally wraps). Make sure that getCount()
will then return the right number of items. Then, call notifyDataSetChanged()
on the FragmentPagerAdapter
, which should trigger a redraw of the ViewPager
.
Removing Fragments from ViewPager
Try this
private class CookingRecipesPagerAdapter extends FragmentPagerAdapter {
final SparseArray registeredFragments = new SparseArray();
public CookingRecipesPagerAdapter() {
super(getChildFragmentManager());
}
@Override
public Fragment getItem(final int pos) {
CookingRecipeFragment fragment = new CookingRecipeFragment();
Bundle bundle = new Bundle();
bundle.putInt("recipe",i);
fragment.setArguments(bundle);
return fragment;
}
/**
* Implementing this method is IMPORTANT for detorying the fragment.
* Use unique id for each item on pagerData.
*/
@Override
public long getItemId(int position) {
return fragmentRecipeList.get(position).getId(); // Unique id by position and question id.
}
@NonNull
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
@Override
public int getCount() {
return fragmentRecipeList.size();
}
}
Update 1
protected Fragment getCurrentFragment() {
if (pager.getAdapter() instanceof CookingRecipesPagerAdapter)
return ((CookingRecipesPagerAdapter) pager.getAdapter()).getRegisteredFragment(currentIndex);
return null;
}
public void removeCurrentRecipeIfUnpinned(){
int index = viewPager.getCurrentItem();
fragmentRecipeList.remove(index);
adapter.notifyDataSetChanged();
}
add/remove fragment in viewpager using FragmentPagerAdapter
Well the solution i have found for this is to add my 5 fragments on initialization of viewpager
private ArrayList frags = new ArrayList();
private SmallViewPagerAdapter vpAdapter;
viewPager.setPagingEnabled(false);
viewPager.setOffscreenPageLimit(4);
frags.add(new MapContainerFragment(this));
frags.add(new FavoriteContainerFragment(this));
frags.add(new AllOffersFragment(this));
frags.add(new RegisterFragment(this));
frags.add(new DetailFragment(this));
vpAdapter = new SmallViewPagerAdapter(getSupportFragmentManager(),
frags);
viewPager.setAdapter(vpAdapter);
and created a static function for updating the view on every click
public static void updateView(Context context, String offerId) {
offer = new DBHandler(context).getOffersById(offerId);
if (offer != null) {
offerName.setText(offer.getTitle());
offerSave.setText(offer.getListTitle());
ImageLoader.getInstance().displayImage(
Constants.URLHelper.getUrlForOfferImage(offer.getId(),
offer.getLogoId()), offerImage);
detailText.setText(Html.fromHtml(offer.getDetailDesc().replace(
"COLOR=\"#0B333C\"", " ")));
} else {
Toast.makeText(context, "No Offer detail found!!",
Toast.LENGTH_LONG).show();
}
}
and on the button click i just change the current selected page of the viewpager.
NOTE: I know its a bad approach but that's all I can come up for the solution to my problem. If someone have a better idea or thinks that this approach isn't better, please tell me.
How to remove pages in Android ViewPager?
Extend FragmentStatePagerAdapter, and override getItemPosition
methode:
public class ViewPagerAdapter extends FragmentStatePagerAdapter
{
//... your existing code
@Override
public int getItemPosition(Object object) {
int position = mList.indexOf(object);
return position == -1 ? POSITION_NONE : position;
}
}
Related Topics
How to Install Trusted Ca Certificate on Android Device
Android Edittext Delete(Backspace) Key Event
How to Set Navigation Drawer to Be Opened from Right to Left
Runtimeexception: Unable to Instantiate Application
How to Start Activity in Another Application
How to Sign an Android APK File
Android - How to Investigate an Anr
Generate Sha-1 for Flutter/React-Native/Android-Native App
Android Update Activity UI from Service
Android Writing Logs to Text File
How to Pass Data from 2Nd Activity to 1St Activity When Pressed Back? - Android
Android: How to Bind Spinner to Custom Object List
Cursorloader Usage Without Contentprovider
How to Hide Soft Keyboard on Android After Clicking Outside Edittext
Android Create Shortcuts on the Home Screen
Checkbox in Recyclerview Keeps on Checking Different Items
Android - Android.View.Inflateexception: Binary Xml File Line #8: Error Inflating Class Fragment