ViewPager+Fragment数据源变化后无法刷新问题

FragmentPagerAdapter 执行 notifyDataSetChanged刷新无效的根本原因是,其维护的FragmentManager会缓存其加入的Fragment导致,解决方法自然也是从FragmentManager入手。

一、问题场景

使用ViewPager+Fragment实现首页的翻页效果,在未登录的情况下,以包含跳转登录的Frament填充:FragmentLoginNeeded

Log如下:5c88c3ca1f667

注:MainFragmemtMine对未登录情况有自己的处理

但是在登录后,执行notifyDataSetChanged操作,页面并没有刷新,刷新代码如下:

boolean login = isLogin();  
if (login) {
    
      
    FragmentMain fragmentMain = //...
  fragments.add(fragmentMain);  
  MainFragmentPerson fragmentPerson = //。。。 
  fragments.add(fragmentPerson);  
  fragments.add(FragmentRecord.getInstance());  
  fragments.add(MainFragmentMessage.getInstance());  
} else {
    
      
    fragments.add(FragmentLoginNeeded.getInstance());  
  fragments.add(FragmentLoginNeeded.getInstance());  
  fragments.add(FragmentLoginNeeded.getInstance());  
  fragments.add(FragmentLoginNeeded.getInstance());  
}  
fragments.add(MainFragmentMine.getInstance("我的"));  
mFragments.clear();  
mFragments.addAll(fragments);  
mFragmentAdapter.notifyDataSetChanged();

此时log如下:5c88c4cb35e14

以上可以看出,虽然执行了destory操作,但instantiateItem初始化的目标仍然是原来的对象,所以导致明明ViewPager的Adapter数据源都已经有了变化,但界面数据并没有发生变化。这就是现有的问题。

二、源码查看

同样是继承Adapter,在我们自己实现的Adapter中,在数据源发生变化后,执行Adapter.notifyDataSetChanged()时数据会刷新至最新;但是FramentPagerAdapter却没有执行刷新,所以需要从FramentPagerAdapter的源码查起。

FragmentPagerAdapter的源码中,其他方法未发现异常,但instantiateItem中对Fragment的处理有点意思代码如下:5c8a3361efd1f

对于Fragment的处理是通过FragmentManager.findFragmentByTag()来实现的(FragmentManager会缓存添加到其事务中的Fragment,下次通过TAG来取出)其中Tag的组成方式如下:5c8a33c5e100f
在此,可以看出,Tag是由container.getId()getItemId()决定的,其中container.getId()的值便是Adapter中View的Id,getItemId()则默认返回的是position的值:

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

container的ID和position的组合来控制是否初始化新的Fragment,并没有给Fragment任何改变的机会…,因此问题解决的方法,也在每次初始化Item时,对Fragment的判断上,但是因为源码上对Fragment的判断和操作涉及缓存优化,所以,退而求其次,在destoryItem的时候,将Fragment从FragmentManager清除掉,问题就解决了(仅仅局限于数量少,且一次性初始化完毕的情况)。

三、问题解决

经过以上的分析,解决的方法夜呼之欲出了,重写destoryItem()方法:如下
5c8a33d54bb11
最后模仿FragmentPagerAdapter源码中的处理方式,将事务提交就OK了。

    @Override
    public void finishUpdate(@NonNull ViewGroup container) {
    
    
        super.finishUpdate(container);
        if (mTransaction != null) {
    
    
            mTransaction.commitNowAllowingStateLoss();
            mTransaction = null;
        }
    }

注:以上处理方法仅限于通过设置mVpFragmentMain.setOffscreenPageLimit(4);来实现,初次初始化就会全部初始化整个列表的情况。对于Fragment+ViewPager的情况,基本都是用于首页、所以能解决大部分的场景

猜你喜欢

转载自blog.csdn.net/u014235093/article/details/88560889
今日推荐