Dynamically remove an item from a ViewPager with FragmentStatePagerAdapter
Asked Answered
Y

2

13

There are quite a few discussions around this topic

I have tried various solutions (including the invalidation with POSITION_NONE) . But I still donT know how to remove an item properly.

What happens is

  • either I get a blank page (meaning the fragment is destroyed, but the instantiateItem was not called for a replacement)
  • or the whole thing crashes probably because the way the Android manages the fragment instances do not match how I keep them in my arraylist/sparsearray

Here s my adapter

private class DatePickerPagerAdapter extends FragmentStatePagerAdapter {

    ArrayList<Fragment> registeredFragments = new ArrayList<Fragment>();

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

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

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

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

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        registeredFragments.add(position, fragment);
        return fragment;
    }

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

    public void removePage(ViewGroup pager, int position) {
        destroyItem(pager, position, null);
        registeredFragments.remove(position);
        iPageCount--;
        pagerIndicator.notifyDataSetChanged();
        pagerAdapter.notifyDataSetChanged();
    }

    public void addPage() {

        iPageCount++;
        pagerIndicator.notifyDataSetChanged();
        pagerAdapter.notifyDataSetChanged();
    }
}

I am using a view pager with ViewPagerIndicator and I want to be able to remove a page in between, for example.

Hence remains the question, what is the proper way handling addition and removal of fragments in a ViewPager?

Thanks!

Yeo answered 23/1, 2015 at 11:29 Comment(0)
I
24

If you want to remove items from a ViewPager, this following code does not make sense:

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

Essentially, you create a Fragment based on the position. No matter which page you remove, the range of the position will change from [0, iPageCount) to [0, iPageCount-1), which means that it will always get rid of the last Fragment.


What you need is more or less the following:

public class DatePickerPagerAdapter extends FragmentStatePagerAdapter
{
    private ArrayList<Integer> pageIndexes;

    public DatePickerPagerAdapter(FragmentManager fm) {
        super(fm);
        pageIndexes = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            pageIndexes.add(new Integer(i));
        }
    }

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

    @Override
    public Fragment getItem(int position) {
        Integer index = pageIndexes.get(position);
        return CreateWishFormDatePaginationFragment.newInstance(index.intValue());
    }

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

    // Delete a page at a `position`
    public void deletePage(int position)
    {
        // Remove the corresponding item in the data set 
        pageIndexes.remove(position);
        // Notify the adapter that the data set is changed
        notifyDataSetChanged();
    }
}

Please refer to this complete example for more details about removing item from FragmentStatePagerAdapter.

Ioannina answered 31/8, 2015 at 3:55 Comment(3)
This only works if you try to delte on fragment. If you try to delete another item again it seems removing does not work.Reorder
Works like a charm for me, after modifying a bit for my exact use case. What I especially like about this is letting the FragmentStatePagerAdapter's FragmentManager care about the state of each Fragment.Sn
but this code @Override public int getItemPosition(Object object) { // refresh all fragments when data set changed return PagerAdapter.POSITION_NONE; } takes too much time to remove a fragment. can u helpXylotomous
M
11

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;
        }
    }
    
Mokpo answered 15/11, 2017 at 11:39 Comment(1)
yaaaaa @PaulBurkeMokpo

© 2022 - 2024 — McMap. All rights reserved.