FragmentStatePagerAdapter的刷新问题

来源:互联网 发布:微信域名检测原理 编辑:程序博客网 时间:2024/05/22 01:49

众所周知,FragmentStatePagerAdapter是谷歌官方专门为Fragment和ViewPager推出的Adapter,其特点是为Fragment提供缓存,避免重复加载Fragment。这个我们从源代码就可以看得出来:

@Override    public Object instantiateItem(ViewGroup container, int position) {        // If we already have this item instantiated, there is nothing        // to do.  This can happen when we are restoring the entire pager        // from its saved state, where the fragment manager has already        // taken care of restoring the fragments we previously had instantiated.        if (mFragments.size() > position) {        // 存在缓存表示该Fragment已经加载过了        // 值得注意的是,这个地方Fragment的缓存是与position绑定在一起的            Fragment f = mFragments.get(position);            if (f != null) {                return f;            }        }        if (mCurTransaction == null) {            mCurTransaction = mFragmentManager.beginTransaction();        }        Fragment fragment = getItem(position);        if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);        if (mSavedState.size() > position) {            Fragment.SavedState fss = mSavedState.get(position);            if (fss != null) {                //Fragment未加载,但是存在缓存的状态信息                fragment.setInitialSavedState(fss);            }        }        while (mFragments.size() <= position) {            mFragments.add(null);        }        fragment.setMenuVisibility(false);        fragment.setUserVisibleHint(false);        mFragments.set(position, fragment);        mCurTransaction.add(container.getId(), fragment);        return fragment;    }

这就是FragmentStatePagerAdapter加载缓存的代码,其中两个关键点,我都加了中文注释。在这里我要讲的是第一个地方,也就是Fragment是和position绑定在一起的,这样的话就会有一个问题,当我们想要隐藏或者显示(插入)其中某一个Fragment,我们就必须要让所有的Fragment重新走一遍生命周期。

这是因为,想要刷新ViewPager的Fragment必须修改getItemPosition(Object object)这个方法,使其返回POSITION_NONE,但是这样的话,所有的Fragment都会先被remove,然后再重新add。

    /**     * Called when the host view is attempting to determine if an item's position     * has changed. Returns {@link #POSITION_UNCHANGED} if the position of the given     * item has not changed or {@link #POSITION_NONE} if the item is no longer present     * in the adapter.     */    public int getItemPosition(Object object) {        return POSITION_UNCHANGED;    }

然儿这并不是我们想要,因此就有必要寻找一种替代方案。我们依然可以从getItemPosition这个方法入手,getItemPosition返回值表示的是page的位置,也就是说我们可以用这个方法来调整page的位置,而fragment也不需要重新加载。

既然知道问题在哪,只要对症下药就好了。修改一下FragmentStatePagerAdapter的部分逻辑即可,具体代码如下:

  /** * Description: Copied from android.support.v4.app.FragmentStatePagerAdapter, Fix bug for that fragments cache not refreshed when adapter items's position changed. * FixBug#修复Fragment的位置更新而缓存未更新导致显示错误以及crash的问题 * Author: xuqingqi * E-mail: xuqingqi01@gmail.com * Date: 2017/12/8 */@Keeppublic abstract class FragmentStatePagerAdapterCompat extends PagerAdapter {    private static final String TAG = FragmentStatePagerAdapterCompat.class.getSimpleName();    private static final boolean DEBUG = false;    private final FragmentManager mFragmentManager;    private FragmentTransaction mCurTransaction = null;    private SparseArray<Fragment.SavedState> mSavedState = new SparseArray<Fragment.SavedState>();    private SparseArray<Fragment> mFragments = new SparseArray<Fragment>();    private Fragment mCurrentPrimaryItem = null;    public FragmentStatePagerAdapterCompat(FragmentManager fm) {        mFragmentManager = fm;    }    /**     * Return the Fragment associated with a specified position.     */    public abstract Fragment getItem(int position);    /**     * Return a unique identifier for the item at the given position.     *     * <p>The default implementation returns the given position.     * Subclasses should override this method if the positions of items can change.</p>     *     * @param position Position within this adapter     * @return Unique identifier for the item at position     */    //添加一个方法,将Fragment和Id绑定在一起而不是position    public int getItemId(int position) {        return position;    }    @Override    public void startUpdate(ViewGroup container) {        if (container.getId() == View.NO_ID) {            throw new IllegalStateException("ViewPager with adapter " + this                    + " requires a view id");        }    }    @Override    public Object instantiateItem(ViewGroup container, int position) {        // If we already have this item instantiated, there is nothing        // to do.  This can happen when we are restoring the entire pager        // from its saved state, where the fragment manager has already        // taken care of restoring the fragments we previously had instantiated.        int itemId = getItemId(position);        Fragment f = mFragments.get(itemId);        if (f != null) {            return f;        }        if (mCurTransaction == null) {            mCurTransaction = mFragmentManager.beginTransaction();        }        Fragment fragment = getItem(position);        if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);        Fragment.SavedState fss = mSavedState.get(itemId);        if (fss != null) {            fragment.setInitialSavedState(fss);        }        fragment.setMenuVisibility(false);        fragment.setUserVisibleHint(false);        mFragments.put(itemId, fragment);        mCurTransaction.add(container.getId(), fragment);        return fragment;    }    @Override    public void destroyItem(ViewGroup container, int position, Object object) {        Fragment fragment = (Fragment) object;        if (mCurTransaction == null) {            mCurTransaction = mFragmentManager.beginTransaction();        }        if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object                + " v=" + ((Fragment)object).getView());        int index = mFragments.indexOfValue(fragment);        if (index >= 0) {            int itemId = mFragments.keyAt(index);            mSavedState.put(itemId, fragment.isAdded()                    ? mFragmentManager.saveFragmentInstanceState(fragment) : null);            mFragments.remove(itemId);        }        mCurTransaction.remove(fragment);    }    @Override    public void setPrimaryItem(ViewGroup container, int position, Object object) {        Fragment fragment = (Fragment)object;        if (fragment != mCurrentPrimaryItem) {            if (mCurrentPrimaryItem != null) {                mCurrentPrimaryItem.setMenuVisibility(false);                mCurrentPrimaryItem.setUserVisibleHint(false);            }            if (fragment != null) {                fragment.setMenuVisibility(true);                fragment.setUserVisibleHint(true);            }            mCurrentPrimaryItem = fragment;        }    }    @Override    public void finishUpdate(ViewGroup container) {        if (mCurTransaction != null) {            mCurTransaction.commitNowAllowingStateLoss();            mCurTransaction = null;        }    }    @Override    public boolean isViewFromObject(View view, Object object) {        return ((Fragment)object).getView() == view;    }    @Override    public Parcelable saveState() {        Bundle state = null;        if (mSavedState.size() > 0) {            state = new Bundle();            for (int i=0; i< mSavedState.size(); i++) {                int itemId = mFragments.keyAt(i);                Fragment.SavedState ss = mSavedState.get(itemId);                if (ss != null) {                    String key = "s" + itemId;                    state.putParcelable(key, ss);                }            }        }        for (int i=0; i< mFragments.size(); i++) {            int itemId = mFragments.keyAt(i);            Fragment f = mFragments.get(itemId);            if (f != null && f.isAdded()) {                if (state == null) {                    state = new Bundle();                }                String key = "f" + itemId;                mFragmentManager.putFragment(state, key, f);            }        }        return state;    }    @Override    public void restoreState(Parcelable state, ClassLoader loader) {        if (state != null) {            Bundle bundle = (Bundle)state;            bundle.setClassLoader(loader);            mSavedState.clear();            mFragments.clear();            Iterable<String> keys = bundle.keySet();            for (String key: keys) {                if (key.startsWith("f")) {                    int index = Integer.parseInt(key.substring(1));                    Fragment f = mFragmentManager.getFragment(bundle, key);                    if (f != null) {                        f.setMenuVisibility(false);                        mFragments.put(index, f);                    } else {                        Log.w(TAG, "Bad fragment at key " + key);                    }                }                else if (key.startsWith("s")) {                    int index = Integer.parseInt(key.substring(1));                    Parcelable parcelable = bundle.getParcelable(key);                    if (parcelable instanceof Fragment.SavedState) {                        mSavedState.put(index, (Fragment.SavedState) parcelable);                    }                }            }        }    }}  
阅读全文
0 0
原创粉丝点击