完美解决FragmentPagerAdapter刷新问题

转载请注明:(http://blog.csdn.net/u012854870/article/details/70231752)

先不废话,直接上Adapter代码:

public class SimpleFragmentPageAdapter extends FragmentPagerAdapter {

    private List<BaseFragment> mFragments;
    private FragmentManager fragmentManager;
    private List<String> tags;

    public SimpleFragmentPageAdapter(FragmentManager fm, List<BaseFragment> fragments) {
        super(fm);
        this.tags = new ArrayList<>();
        this.fragmentManager = fm;
        this.mFragments = fragments;
    }

    public void setNewFragments(List<BaseFragment> fragments) {
        if (this.tags != null) {
            FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
            for (int i = 0; i < tags.size(); i++) {
                fragmentTransaction.remove(fragmentManager.findFragmentByTag(tags.get(i)));
            }
            fragmentTransaction.commit();
            fragmentManager.executePendingTransactions();
            tags.clear();
        }
        this.mFragments = fragments;
        notifyDataSetChanged();
    }

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

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

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        tags.add(makeFragmentName(container.getId(), getItemId(position)));
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        this.fragmentManager.beginTransaction().show(fragment).commit();
        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        Fragment fragment = mFragments.get(position);
        fragmentManager.beginTransaction().hide(fragment).commit();
    }

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

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

接下来说一下为什么要这样写
1.先看FragmentPagerAdapter源码

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

        final long itemId = getItemId(position);

        // Do we already have this fragment?
        String name = makeFragmentName(container.getId(), itemId);
        Fragment fragment = mFragmentManager.findFragmentByTag(name);
        if (fragment != null) {
            if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
            mCurTransaction.attach(fragment);
        } else {
            fragment = getItem(position);
            if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
            mCurTransaction.add(container.getId(), fragment,
                    makeFragmentName(container.getId(), itemId));
        }
        if (fragment != mCurrentPrimaryItem) {
            fragment.setMenuVisibility(false);
            fragment.setUserVisibleHint(false);
        }

        return fragment;
    }


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

从源码中可以看到在从 FragmentManager 中取出 Fragment 时调用了 findFragmentByTag() 方法,而这个 Tag 是由 makeFragmentName() 方法生成的。继续往下可以看到每一个 Fragment 都打上了一个标签(在 mCurTransaction.add() 方法中)。

也就是说是 FragmentManager 通过 Tag 找相应的 Fragment,从而达到缓存 Fragment 的目的。如果可以找到,就不会创建新的 Fragment,Fragment 的 onCreate()、onCreateView() 等方法都不会再次调用。

所以我们需要缓存所有 Fragment 的 Tag,并且在其次,在更新数据时,使用相应的 Tag 去 FragmentManamager 中找相应的 Fragment,如果存在,就先将fragment移除掉。

2.重写 PagerAdapter 的 getItemPosition(Object object) 方法,将返回值固定为 POSITION_NONE。
为什么这么写请看viewPager源码:

 void dataSetChanged() {
        // This method only gets called if our observer is attached, so mAdapter is non-null.

        final int adapterCount = mAdapter.getCount();
        mExpectedAdapterCount = adapterCount;
        boolean needPopulate = mItems.size() < mOffscreenPageLimit * 2 + 1
                && mItems.size() < adapterCount;
        int newCurrItem = mCurItem;

        boolean isUpdating = false;
        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;
            }

            if (ii.position != newPos) {
                if (ii.position == mCurItem) {
                    // Our current item changed position. Follow it.
                    newCurrItem = newPos;
                }

                ii.position = newPos;
                needPopulate = true;
            }
        }

        if (isUpdating) {
            mAdapter.finishUpdate(this);
        }

        Collections.sort(mItems, COMPARATOR);

        if (needPopulate) {
            // Reset our known page widths; populate will recompute them.
            final int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                final View child = getChildAt(i);
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                if (!lp.isDecor) {
                    lp.widthFactor = 0.f;
                }
            }

            setCurrentItemInternal(newCurrItem, false, true);
            requestLayout();
        }
    }

上面的代码中 for 循环里面有两个 continue 语句,这可能是比较关键的代码,幸好不用我们继续深入了,官方给出了解释:

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.

大致的意思是:
如果 Item 的位置如果没有发生变化,则返回 POSITION_UNCHANGED。如果返回了 POSITION_NONE,表示该位置的 Item 已经不存在了。默认的实现是假设 Item 的位置永远不会发生变化,而返回 POSITION_UNCHANGED。

参考:ViewPager刷新问题详解

猜你喜欢

转载自blog.csdn.net/u012854870/article/details/70231752