ViewPager中動態(tài)更換Fragment,調(diào)用notifyDataSetChanged()方法Fragment不更新的問題及解決方案

最近在使用ViewPage+FragmentPagerAdapter時遇到一個問題,就是在Fragment數(shù)據(jù)集合改變時候,調(diào)用adapter的notifyDataSetChanged()方法Fragement根本不刷新。

原因

研究FragmentPagerAdapter.instantiateItem源碼發(fā)現(xiàn):

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

在instantiateItem的時候,生成的fragment會存到FragmentManager中,下次再instantiateItem同一位置的item時候,會根據(jù)名字在FragmentManager尋找是否有緩存的fragment,如果有的話就會直接只用緩存的fragment,這就是導(dǎo)致Fragment數(shù)據(jù)集改變之后調(diào)用notifyDataSetChanged()方法也不會刷新的原因。

解決方案

1、在設(shè)置了新的Fragment數(shù)據(jù)集之后,設(shè)置標(biāo)記位,在instantiateItem中替換FragmentManager緩存的fragment,代碼如下:

private ArrayList<Fragment> fragments;
private FragmentManager fm;
private boolean[] flags;//標(biāo)識,重新設(shè)置fragment時全設(shè)為true

@Override
public Object instantiateItem(ViewGroup container, int position) {
    if (flags != null && flags[position]) {
        /**得到緩存的fragment, 拿到tag并替換成新的fragment*/
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        String fragmentTag = fragment.getTag();
        FragmentTransaction ft = fm.beginTransaction();
        ft.remove(fragment);
        fragment = fragments.get(position);
        ft.add(container.getId(), fragment, fragmentTag);
        ft.attach(fragment);
        ft.commit();
        /**替換完成后設(shè)為false*/
        flags[position] = false;
        if (fragment != null){
            return fragment;
        }else {
            return super.instantiateItem(container, position);
        }
    } else {
        return super.instantiateItem(container, position);
    }
}

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

public void setFragments(ArrayList<Fragment> fragments) {
    if (this.fragments != null) {
        flags = new boolean[fragments.size()];
        for (int i = 0; i < fragments.size(); i++) {
            flags[i] = true;
        }
    }
    this.fragments = fragments;
    notifyDataSetChanged();
}

這其中還有一個問題要注意的就是需要重寫getItemPosition()方法并返回POSITION_NONE
參見源碼可以發(fā)現(xiàn)notifyDataSetChanged()方法會回調(diào)ViewPager的dataSetChanged()方法,在該方法中會根據(jù)getItemPosition()的返回值進(jìn)行判斷,如果是POSITION_UNCHANGED(默認(rèn)返回)則什么都不做直接continue,如果是POSITION_NONE,則會調(diào)用PagerAdapter.destroyItem()并設(shè)置needPopulate為true,才會觸發(fā)instantiateItem()方法去生成新的對象。

2、因為FragmentPagerAdapter.instantiateItem()中mFragmentManager是通過name去尋找是否有緩存的fragment:

    final long itemId = getItemId(position);
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);
    
    private static String makeFragmentName(int viewId, long id) {
        return "android:switcher:" + viewId + ":" + id;
}

而name是根據(jù)viewId和getItemId(position)確定的,我們可以通過修改getItemId(position)方法來讓mFragmentManager找不到fragment從而去使用新的fragment替換。getItemId(position)默認(rèn)返回的是position,我這里使用position+時間戳來替換。

private long time;
public void setFragments(ArrayList<Fragment> fragments) {
    time = System.currentTimeMillis();
    this.fragments = fragments;
    notifyDataSetChanged();
}

@Override
public long getItemId(int position) {
    return super.getItemId(position) + time;
}

兩種方法都親測有效,當(dāng)然大家也可以繼續(xù)研究源碼使用其他方法。

參考文檔:
http://www.cnblogs.com/dancefire/archive/2013/01/02/why-notifydatasetchanged-does-not-work.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容