Remove Fragment Page from Viewpager in Android

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



Leave a reply



Submit