ViewPager设置setAdapter不会重新创建Fragment的问题

刚开始一顿面向谷歌编程,各种网友提供的思路都试了一遍发现都无效,最后找到了下面这篇博客终于解决我的问题

解决ViewPager展示Fragment时重新设置setAdapter不会重置Fragment的bug

我遇到的问题:

创建了新的fragment也设置了setAdapter但是结果还是出现旧的fragment

解决方法如下:

1在你的PageAdapter中增加下面一个方法

public  void  clear(ViewPager viewPager){
        if (viewPager!=null&&viewPager.getAdapter() != null) {
            //获取FragmentManager实现类的class对象,这里指的就是FragmentManagerImpl
            Class aClass = fragmentManager.getClass();
            try {
                //1.获取其mAdded字段
                Field f = aClass.getDeclaredField("mAdded");
                f.setAccessible(true);
                //强转成ArrayList
                ArrayList list = (ArrayList) f.get(fragmentManager);
                //清空缓存
                list.clear();

                //2.获取mActive字段
                f = aClass.getDeclaredField("mActive");
                f.setAccessible(true);
                //强转成SparseArray
                SparseArray array  = (SparseArray) f.get(fragmentManager);
                //清空缓存
                array.clear();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

2 使用setAdapter之前需要clear

mRecordVpAdapter.clear(viewPager);
viewPager.setAdapter(mRecordVpAdapter);

如果我们想知道原理的话请继续往下看,如果只是解决问题那么到此就可以为止了。

3 FragmentPagerAdapter:

@NonNull
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        if (this.mCurTransaction == null) {
            this.mCurTransaction = this.mFragmentManager.beginTransaction();
        }

        long itemId = this.getItemId(position);
        String name = makeFragmentName(container.getId(), itemId);
        //在这里去碎片管理器查找我们的碎片,这句话是关键!!!
        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;
    }

4 FragmentManager

这是一个抽象方法

@Nullable
    public abstract Fragment findFragmentByTag(@Nullable String var1);

5 FragmentManagerImpl

这里需要去判断mAdded和mActive集合

 @Nullable
    public Fragment findFragmentByTag(@Nullable String tag) {
        int i;
        Fragment f;
        if (tag != null) {
            for(i = this.mAdded.size() - 1; i >= 0; --i) {
                f = (Fragment)this.mAdded.get(i);
                if (f != null && tag.equals(f.mTag)) {
                    return f;
                }
            }
        }

        if (this.mActive != null && tag != null) {
            for(i = this.mActive.size() - 1; i >= 0; --i) {
                f = (Fragment)this.mActive.valueAt(i);
                if (f != null && tag.equals(f.mTag)) {
                    return f;
                }
            }
        }

        return null;
    }
 final ArrayList mAdded = new ArrayList();
    SparseArray mActive;

为什么我们直接setAdapter无效呢?因为我们碎片管理器维持了一个存放碎片集合,布局id和position id没有变化所以获取的Tag也没有变,最后取到的还是旧的碎片,所以无效。
所以我们的思路就是反射去把这两个集合直接清空,所以就可以setAdapter有效了

你可能感兴趣的:(ProjectProblems,SourceCode)