从源码的角度分析为什么fragmentPagerAdapter.notifyDataSetChanged()无效!

首先转载一篇博客,了解fragmentPagerAdapter和fragmentPagerStateAdapter的区别,对后面的分析很重要:
                 https://blog.csdn.net/DJH2717/article/details/81101834

通过上面的博客,我们大致知道了这两个适配器对fragment的生命周期影响是不一样的,主要是因为他们destoryItem的方式不一样.
                 
下面我们来分析为什么这两个适配器的notifyDataSetChanged()都无效的原因:
 
首先,查看源码发现,notifyDataSetChanged()最终会调用viewPager的void dataSetChanged() 方法,这个方法中有这么一段:
 

for (int i = 0; i < mItems.size(); i++) {
    final ItemInfo ii = mItems.get(i);
    final int newPos = mAdapter.getItemPosition(ii.object);

    if (newPos == PagerAdapter.POSITION_UNCHANGED) {
        continue;
    }

    if (newPos == PagerAdapter.POSITION_NONE) {
        mItems.remove(i);
        i--;

        if (!isUpdating) {
            mAdapter.startUpdate(this);
            isUpdating = true;
        }

        mAdapter.destroyItem(this, ii.position, ii.object);
        needPopulate = true;

        if (mCurItem == ii.position) {
            // Keep the current item in the valid range
            newCurrItem = Math.max(0, Math.min(mCurItem, adapterCount - 1));
            needPopulate = true;
        }
        continue;
    }

注意加粗的部分,这个mAdapter.getItemPosition方法官方描述是这样的:

Called when the host view is attempting to determine if an item's position has changed. Returns POSITION_UNCHANGED if the position of the given item has not changed or POSITION_NONE if the item is no longer present in the adapter.
The default implementation assumes that items will never change position and always returns POSITION_UNCHANGED.

官方说道,这个方法默认实现是返回的POSITION_UNCHANGED,而此时在dataSetChange中,如果返回的是UNChange,就直接continue了.....我们更新后的数据根本就没有得到调用,,所以这也是为什么notifyDataSetChanged()无效的最根本的原因了!!

 

怎么解决?

 

网上流传了一种说法,不要使用fragmentPagerAdapter,直接继承fragmentStatePagerAdapter并且重写getItemPosition方法然后返回POSITION_NONE,没错,这样确实可以奏效.

但是为什么会奏效呢?

如果读者注意到我前面转载的那篇博客,仔细看fragmentStatePagerAdapter的destroyItem方法和instantiateItem方法就会发现,

如果getItemPosition返回的是POSITION_NONE,会触发destoryItem,然后有这么两句:

mFragments.set(position, null);

mCurTransaction.remove(fragment);

然后在instantiateItem中有这么几句:

Fragment f = mFragments.get(position);
if (f != null) {
    return f;
}
Fragment fragment = getItem(position);
mFragments.set(position, fragment);
mCurTransaction.add(container.getId(), fragment);
return fragment;

相信看到这里应该明白为什么这个方法会奏效了吧,在destroyItem中彻底的移除了当前fragment,然后在instantiateItem中获取不到缓存的fragment,然后就会调用我们自己实现的getItem方法,去数据集合中拿数据,所以就奏效了!

 

但是我想说,这种解决方法不区分这两个适配器之间的特性,胡乱使用是不好的,因此如果我们在使用有限个fragment时,仍然希望使用fragmentPagerAdapter来提高效率,经过本人对fragmentPagerAdapter源码的分析,有了如下解决方法:

大致思路为,重写getItemPosition,返回NONE.重写instantiateItem,在这个方法中对缓存的fragment做判断,判断当前更新后的数据集合中的fragment和缓存的fragment是否是同一个fragment,如果是就直接返回缓存的fragment,如果不是就替换,同时设置

viewPager.setOffscreenPageLimit(),如果不设置,会有bug出现.

解决代码如下:

 

@NonNull
@Override
public Object instantiateItem(ViewGroup container, int position) {
    this.position = position;
    Fragment cacheFragment = (Fragment) super.instantiateItem(container, position);
    Fragment currentFragment = fragmentPagerList.get(position);
    //Judge the cache fragment and current fragment from data set is the same.
    if (judgeCurrentSameToCache(currentFragment, cacheFragment)) {
        return cacheFragment;
    } else {
        //This tag is see the source code found it set every fragment a tag,and use the tag to find aging the cache fragment.
        //So we also need set the tag,then we replace the cache fragment by current fragment,it is from data set.
        String tag = cacheFragment.getTag();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.remove(cacheFragment);
        fragmentTransaction.add(container.getId(), currentFragment, tag);
        fragmentTransaction.attach(currentFragment);
        fragmentTransaction.commit();
        return currentFragment;
    }
}

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

/**
 * Judge the current fragment is the same to cache fragment.
 */
private boolean judgeCurrentSameToCache(Fragment currentFragment, Fragment cacheFragment) {
    return currentFragment == cacheFragment && currentFragment.equals(cacheFragment);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值