ViewPager2 解决Fragment数据不刷新的问题

一、前言

在最近使用ViewPager2显示多个Fragment的时候遇到一个问题,就是我在删除某个fragment的时候发现更新适配器后页面并没有和如期的一样删除这个fragment,看下到底发生了什么?

二、问题剖析

一般我们刷新页面常用的方法是调用适配器的notifyDataSetChanged方法,我们先看下ViewPager2的适配器更新方法都干啥了?

public class RecyclerView{

    public abstract static class Adapter<VH extends ViewHolder> {

          ...这次省略部分代码

          public final void notifyDataSetChanged() {
              mObservable.notifyChanged();
          }

    }
}

可以看出,更新调用的是RecycleView的适配器方法,所以从这里我们也可以很直观的知道,ViewPager2ViewPager不一样的是使用的适配器不一样,ViewPager2是基于RecycleView的适配器策略基础上实现的,在这里就不细说了。然后接着查看源码,这里就贴一些关键的代码:

final LongSparseArray<Fragment> mFragments = new LongSparseArray<>();

private void ensureFragment(int position) {
    //通过下标获取当前项的id
    long itemId = getItemId(position);
    if (!mFragments.containsKey(itemId)) {
        // TODO(133419201): check if a Fragment provided here is a new Fragment
        Fragment newFragment = createFragment(position);
        newFragment.setInitialSavedState(mSavedStates.get(itemId));
        //保存Fragment
        mFragments.put(itemId, newFragment);
    }
}

先看这个方法,从上面可以看出,适配器内部用LongSparseArray变量存储Fragment,存入Fragment调用的是ensureFragment方法,从方法上面可以看到,getItemId作为key,createFragment作为value存入到mFragments当中,也就是说mFragments通过键值对的形式存储Fragment,此后读取fragment,它的key是一个关键,那么看下它的key是怎么生成的?

public long getItemId(int position) {
    return position;
}

可以看出默认key就是当前item的下标。那么现在可以猜测到不刷新的原因可能是因为下标没有改变,所以新的fragment是存不进去的,且通过key,也就是下标获取到的fragment还是之前那一个fragment。为了验证我们的猜想,我们再看下一个方法updateFragmentMaxLifecycle

void updateFragmentMaxLifecycle(boolean dataSetChanged) {
     
   ...此处省略部分代码
     
    //获取当前项的下标
    final int currentItem = mViewPager.getCurrentItem();
    if (currentItem >= getItemCount()) {
        /** current item is yet to be updated; it is guaranteed to change, so we will be
         * notified via {@link ViewPager2.OnPageChangeCallback#onPageSelected(int)}  */
        return;
    }
    //获取当前项的id,如果上一次的
    long currentItemId = getItemId(currentItem);
     //如果获取的key还是之前那一个,则直接返回
    if (currentItemId == mPrimaryItemId && !dataSetChanged) {
        return; // nothing to do
    }
    //通过id获取当前的Fragment
    Fragment currentItemFragment = mFragments.get(currentItemId);
    if (currentItemFragment == null || !currentItemFragment.isAdded()) {
        return;
    }
    //保存当前项的id
    mPrimaryItemId = currentItemId;
    FragmentTransaction transaction = mFragmentManager.beginTransaction();

    Fragment toResume = null;
    for (int ix = 0; ix < mFragments.size(); ix++) {
        long itemId = mFragments.keyAt(ix);
        Fragment fragment = mFragments.valueAt(ix);

        if (!fragment.isAdded()) {
            continue;
        }

        if (itemId != mPrimaryItemId) {
            transaction.setMaxLifecycle(fragment, STARTED);
        } else {
            toResume = fragment; // itemId map key, so only one can match the predicate
        }

        fragment.setMenuVisibility(itemId == mPrimaryItemId);
    }
    if (toResume != null) { // in case the Fragment wasn't added yet
        transaction.setMaxLifecycle(toResume, RESUMED);
    }

    if (!transaction.isEmpty()) {
        transaction.commitNow();
    }
}

从这个以看出,果然,首先获取当前的下标,如果当前的下标没有改变,则直接返回,这个判断验证了我之前删除第一个Fragment后为什么不刷新,它直接return了。再看下面直接对mFragments进行处理,到这里想必答案已经有了。所有的问题源头就是这个key,如果我们把生成key的规则改一下,让它不会像下标一样容易重复,那么问题解决了。

三、问题解决

其实只要保证生成的ItemId不重复就行,在这里我直接用fragmenthashCode值,到这里问题就解决了。

public class SimpleChipViewPage2Adapter extends FragmentStateAdapter {

 private List<Fragment> fragments;

 public SimpleChipViewPage2Adapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle, List<Fragment> fragments) {
     super(fragmentManager, lifecycle);
     this.fragments = fragments;
 }

 @NonNull
 @Override
 public Fragment createFragment(int position) {
     return fragments == null ? null : fragments.get(position);
 }

 @Override
 public int getItemCount() {
     return fragments == null ? 0 : fragments.size();
 }
//重载getItemId方法
 @Override
 public long getItemId(int position) {
     return fragments == null ? 0 : fragments.get(position).hashCode();
 }
}

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值