Viewpager+Fragment如何动态添加、删除Fragment

前言:有时候需要动态改变tab条目数,需要动态去改变Fragment,根据API不管是使用FragmentPagerAdapter或者
FragmentStatePagerAdapter都无法直接实现。

原因分析:

根据习惯我们都会调用 mAdapter.notifyDataSetChanged(),但是viewpager+FragmentPagerAdapter(或FragmentStatePagerAdapter)并无反应,因此我们来看看源代码在notifyDataSetChanged()后执行了啥,

  private DataSetObserver mViewPagerObserver; 
    .
    .
    .   
    public void notifyDataSetChanged() {
        synchronized(this) {
            if (this.mViewPagerObserver != null) {
                //调用了onChanged
                this.mViewPagerObserver.onChanged();
            }
        }

        this.mObservable.notifyChanged();
    }

DataSetObserver 为一个抽象类,在ViewPager中实现

 private class PagerObserver extends DataSetObserver {
        PagerObserver() {
        }

        public void onChanged() {
            //此处调用dataSetChanged
            ViewPager.this.dataSetChanged();
        }

        public void onInvalidated() {
            ViewPager.this.dataSetChanged();
        }
    }

关键方法dataSetChanged(),来看

 void dataSetChanged() {
        int adapterCount = this.mAdapter.getCount();
        this.mExpectedAdapterCount = adapterCount;
        boolean needPopulate = this.mItems.size() < this.mOffscreenPageLimit * 2 + 1 && this.mItems.size() < adapterCount;
        int newCurrItem = this.mCurItem;
        boolean isUpdating = false;

        int childCount;
        for(childCount = 0; childCount < this.mItems.size(); ++childCount) {
            ViewPager.ItemInfo ii = (ViewPager.ItemInfo)this.mItems.get(childCount);
            //关键处getItemPosition()的值  只有等于-2的时候才会执行里面方法
            int newPos = this.mAdapter.getItemPosition(ii.object);
            if (newPos != -1) {
                if (newPos == -2) {
                    this.mItems.remove(childCount);
                    --childCount;
                    if (!isUpdating) {
                        this.mAdapter.startUpdate(this);
                        isUpdating = true;
                    }

                    this.mAdapter.destroyItem(this, ii.position, ii.object);
                    needPopulate = true;
                    if (this.mCurItem == ii.position) {
                        newCurrItem = Math.max(0, Math.min(this.mCurItem, adapterCount - 1));
                        needPopulate = true;
                    }
                } else if (ii.position != newPos) {
                    if (ii.position == this.mCurItem) {
                        newCurrItem = newPos;
                    }

                    ii.position = newPos;
                    needPopulate = true;
                }
            }
        }
            .....省去部分源码
          
        }

    }

再看getItemPosition()

//public abstract class PagerAdapter
public static final int POSITION_UNCHANGED = -1;
    public static final int POSITION_NONE = -2;
    .......
//默认返回-1
public int getItemPosition(@NonNull Object object) {
        return -1;
    }

因此我们只需重写FragmentPagerAdapter和 FragmentStatePagerAdapter的父类PagerAdapter里面的getItemPosition方法:

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

到这里当我没调用 mAdapter.notifyDataSetChanged();时数据就会更新了,但是并不能做到动态删除、添加。因此再看FragmentPagerAdapter和FragmentStatePagerAdapter 对Fragment对象的操作方法,
也就是instantiateItem()创建和destroyItem()移除fragment的方法。

我们先来看FragmentPagerAdapter的instantiateItem()创建和destroyItem()移除fragment的方法,源码已做好注释

  @Override
    @NonNull
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        if (this.mCurTransaction == null) {
            this.mCurTransaction = this.mFragmentManager.beginTransaction();
        }
         //这里获取的itemId,就是position
        long itemId = this.getItemId(position);
       // fragment都是以container的id+itemId(gettemId()) 两个参数计算出来作为fragment的储存的tag key值,
        String name = makeFragmentName(container.getId(), itemId);
        //其在instantiateItem方法中并不是直接去List里面拿到Fragment的,而是先从FragmentManager中通过Tag找对应的Fragment
        Fragment fragment = this.mFragmentManager.findFragmentByTag(name);
        if (fragment != null) {
            this.mCurTransaction.attach(fragment);
        } else {
            fragment = this.getItem(position);
            this.mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId));
        }

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

解决方法一

  private List<Fragment> mFragments = new ArrayList<>();
    class MyAdapter extends FragmentPagerAdapter {

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

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

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

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

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

        @NonNull
        @Override
        public Object instantiateItem(@NonNull ViewGroup container, int position) {
            return super.instantiateItem(container, position);
        }

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

        @Override
        public long getItemId(int position) {
            //修改getItemId   不与位置对应  返回List<Fragment>中Fragment的hashCode
            return mFragments.get(position).hashCode();
        }
    }

解决方法二,原理是将FragmentManager换成的fragmengt对应的key保存下来,在删除新增和改变tab时,将原来的集合清空和FragmentManager中的缓存移除,相当于重新setAdapter

  private class ChangePagerAdapter extends FragmentPagerAdapter {
        private FragmentManager fm;
        private List<Fragment> mFragments;
        private List<String> myTag = new ArrayList<>();

        public ChangePagerAdapter (FragmentManager fm, List<Fragment> mFragments) {
            super(fm);
            this.mFragments = mFragments;
            this.fm = fm;
        }

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


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

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
	//将生成的key保存起来
            myTag .add(makeFragmentName(container.getId(), getItemId(position)));
            Fragment fragment = (Fragment) super.instantiateItem(container, position);
            this.fm.beginTransaction().show(fragment).commitAllowingStateLoss();
            return fragment;
        }

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

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


        private String makeFragmentName(int viewId, long id) {
            return "android:switcher:" + viewId + ":" + id;
        }


	//改变的时候调用   将所有的fragment缓存全部删除    
        public void reMoveAll() {
            if (this.myTag != null) {
                FragmentTransaction fragmentTransaction = fm.beginTransaction();
                for (int i = 0; i < tags.size(); i++) {
                    Fragment fragmentO = fm.findFragmentByTag(myTag.get(i));
                    fragmentTransaction.remove(fragmentO);
                }
                fragmentTransaction.commitAllowingStateLoss();
                fm.executePendingTransactions();
                tags.clear();
            }
            notifyDataSetChanged();
        }
   	//清空集合   然后在重新set数据  相当于重新setAdapter了	
        public void clearAll() {
            mFragments.clear();
            notifyDataSetChanged();
        }
    }

三、我们再看FragmentStatePagerAdapter,只需要重写getItemPosition并返回PagerAdapter.POSITION_NONE

class MyAdapter extends FragmentStatePagerAdapter {

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

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

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

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

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

解决办法四、参考FragmentPagerAdapter和FragmentStatePagerAdapter的实现方法,自己重写PagerAdapter父类的instantiateItem和destroyItem方法,自己维护缓存。

.......................

 

到此ViewPager就可以动态删除和调整fragment了。

 

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

揽m月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值