Remove Fragment Page from ViewPager in Android

前端 未结 17 1162
庸人自扰
庸人自扰 2020-11-22 17:20

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

相关标签:
17条回答
  • 2020-11-22 17:24

    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 !

    0 讨论(0)
  • 2020-11-22 17:25

    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:

    • Exploring the ViewPager2
    • Look deep into ViewPager2

    API reference

    Release notes

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

    0 讨论(0)
  • 2020-11-22 17:27

    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();
        }
    
    }
    
    0 讨论(0)
  • 2020-11-22 17:30

    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);
    
    0 讨论(0)
  • 2020-11-22 17:33

    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.

    0 讨论(0)
  • 2020-11-22 17:35

    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);
        }
    }
    
    0 讨论(0)
提交回复
热议问题