1. 程式人生 > >FragmentPagerAdapter中Fragment不能重新整理問題

FragmentPagerAdapter中Fragment不能重新整理問題

FragmentPagerAdapter中Fragment不重新整理終極解決方案:

最近手裡一個專案有個下拉重新整理的功能:(下拉重新整理要更新所有fragment頁面中的資料),在網上看了很多部落格都沒解決問題。最後自己研究了一下原始碼,既然奇蹟般的解決了!

專案主要結構如下圖(版權問題就直接寫了個demo):

Paste_Image.png

下面是我的解決方案:

我們先開啟FragmentPagerAdapter的原始碼去檢視裡面的instantiateItem()方法:

    @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; }

他會先去FragmentManager裡面去查詢有沒有相關的fragment如果有就直接使用Manage中快取的fragment,如果沒有才會觸發FragmentPagerAdatper的getItem方法獲取一個fragment。所以這個時候你直接去更新fragments集合是沒有作用的。

這個時候我們要做的是清除FragmentManager裡面全部快取的fragment。然後更新fragment後再重新載入。

怎麼清除FragmentManager中快取是現在的問題,當我看FragmentPagerAdapter時,我發現裡面有個makeFragmentName()方法:原始碼如下:

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

根據方法名字可以猜測應該得到fragment的名字。當我們得到fragment名字了就可以直接通過名字來找到具體快取的fragment了;這個時候我只需要重寫FragmentPagerAdapter中的instantiateItem();

下面貼上最終程式碼(註釋都很詳細就不做過多解釋了)

/**
 * 時 間: 2017/4/15
 * 作 者: 鄭亮
 * Q  Q : 1023007219
 */

public class BaseFragmentAdapter extends FragmentPagerAdapter {
    private List<BaseFragment> fragments;
    private FragmentManager fm;


    public BaseFragmentAdapter(FragmentManager fm,List<BaseFragment> fragments) {
        super(fm);
        this.fm = fm;
        this.fragments= fragments;
    }

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

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


    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        FragmentTransaction ft = fm.beginTransaction();
        for (int i = 0; i < getCount(); i++) {//通過遍歷清除所有快取
            final long itemId = getItemId(i);
            //得到快取fragment的名字
            String name = makeFragmentName(container.getId(), itemId);
            //通過fragment名字找到該物件
            BaseFragment fragment = (BaseFragment) fm.findFragmentByTag(name);
            if (fragment != null) {
                //移除之前的fragment
                ft.remove(fragment);
            }
        }
        //重新新增新的fragment:最後記得commit
        ft.add(container.getId(), getItem(position)).attach(getItem(position)).commit();
        return getItem(position);
    }

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

    /**
     * 得到快取fragment的名字
     * @param viewId
     * @param id
     * @return
     */
    private String makeFragmentName(int viewId, long id) {
        return "android:switcher:" + viewId + ":" + id;
    }
}

最後看一看效果圖:

效果.gif

不足之處:我這個專案是將FragmentManager裡面全部快取的fragment都清除了,全部清除會造成fragment全部重新載入,不用更新的地方也會重洗載入,如果介面很多很複雜,資料很多,可能會造成螢幕卡頓,造成不必要的效能損失。