帶你走過ViewPager不刷新界面的坑

最近在項(xiàng)目中用到ViewPager+FragmentPagerAdapter的方式來做界面,其中當(dāng)adapter的數(shù)據(jù)源數(shù)據(jù)更新時(shí),調(diào)用adapter.notifyDataSetChanged()更新數(shù)據(jù),發(fā)現(xiàn)ViewPager并沒有更新,還是原來的數(shù)據(jù)。

參考了別人的文章以及部分解決的的方法,加上自己的理解,拿出了下面這套解決方案。

目錄:

  1. 問題展示
  2. 解決方案
  3. 問題追究

問題展示

國際慣例,先上問題圖(圖一)以及正常圖(圖二)。


圖一

圖二

解決方案

  1. 在數(shù)據(jù)源更新的前面加入以下代碼
if (viewPager.getAdapter() != null) {
    FragmentManager fm = getSupportFragmentManager();
    FragmentTransaction ft = fm.beginTransaction();
    List<Fragment> fragments = fm.getFragments();
    if(fragments != null && fragments.size() >0){
        for (int i = 0; i < fragments.size(); i++) {
            ft.remove(fragments.get(i));
        }
    }
    ft.commit();}
  1. 在你的adapter類中加入以下代碼
private int mChildCount = 0;
@Overridepublic void notifyDataSetChanged() {
    // 重寫這個(gè)方法,取到子Fragment的數(shù)量,用于下面的判斷,以執(zhí)行多少次刷新    
    mChildCount = getCount();
    super.notifyDataSetChanged();
}
@Override
public int getItemPosition(Object object) {
    if ( mChildCount > 0) {
        // 這里利用判斷執(zhí)行若干次不緩存,刷新
        mChildCount --;
        // 返回這個(gè)是強(qiáng)制ViewPager不緩存,每次滑動都刷新視圖
        return POSITION_NONE;
    }
    // 這個(gè)則是緩存不刷新視圖
    return super.getItemPosition(object);}
較完整代碼一覽

初始化數(shù)據(jù)

viewPager= (ViewPager) findViewById(R.id.pager);
mList=new ArrayList<Fragment>();
for (int i=1;i<4;i++){
    Bundle bundle=new Bundle();
    bundle.putString("text","第"+i+"頁");
    MyFragment myFragment=new MyFragment();
    myFragment.setArguments(bundle);
    mList.add(myFragment);
}
adapter=new MyAdapter(getSupportFragmentManager());
viewPager.setAdapter(adapter);

自定義adapter

public class MyAdapter extends FragmentPagerAdapter{
    public MyAdapter(FragmentManager fm) {
        super(fm);
    }
    @Override    public Fragment getItem(int position) {
        return mList.get(position);
    }
    @Override    public int getCount() {
        return mList.size();
    }
    // start
    // 可以刪除這段代碼看看,數(shù)據(jù)源更新而viewpager不更新的情況
    private int mChildCount = 0;
    @Override    public void notifyDataSetChanged() {
        // 重寫這個(gè)方法,取到子Fragment的數(shù)量,用于下面的判斷,以執(zhí)行多少次刷新
        mChildCount = getCount();
        super.notifyDataSetChanged();
    }
    @Override    public int getItemPosition(Object object) {
        if ( mChildCount > 0) {
            // 這里利用判斷執(zhí)行若干次不緩存,刷新
            mChildCount --;
            // 返回這個(gè)是強(qiáng)制ViewPager不緩存,每次滑動都刷新視圖
            return POSITION_NONE;
        }
        // 這個(gè)則是緩存不刷新視圖
        return super.getItemPosition(object);
    }
    // end
}

更新數(shù)據(jù)源的方法

public void update(){
    // start
    // 可以刪除這段代碼看看,數(shù)據(jù)源更新而viewpager不更新的情況
    // 在數(shù)據(jù)源更新前增加的代碼,將上一次數(shù)據(jù)源的fragment對象從FragmentManager中刪除
    if (viewPager.getAdapter() != null) {
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        List<Fragment> fragments = fm.getFragments();
        if(fragments != null && fragments.size() >0){
            for (int i = 0; i < fragments.size(); i++) {
                ft.remove(fragments.get(i));
            }
        }
        ft.commit();
    }
    // End
    mList.clear();
    for (int i=4;i<7;i++){
        Bundle bundle=new Bundle();
        bundle.putString("text","第"+i+"頁");
        MyFragment myFragment=new MyFragment();
        myFragment.setArguments(bundle);
        mList.add(myFragment);
    }
    // 重寫adapter的notifyDataChanged方法
    adapter.notifyDataSetChanged();}
demo源碼下載

前往github下載源碼
可根據(jù)注釋刪除對應(yīng)的代碼,體驗(yàn)有問題以及正常的情況。

問題追究

首先來理解兩個(gè)adapter,都是繼承與pageradapter

  1. FragmentPagerAdapter:該類更專注于每一頁均為 Fragment 的情況。該類內(nèi)的每一個(gè)生成的 Fragment 都將保存在內(nèi)存之中,因此適用于那些相對靜態(tài)的頁,數(shù)量也比較少的那種;如果需要處理有很多頁,并且數(shù)據(jù)動態(tài)性較大、占用內(nèi)存較多的情況,應(yīng)該使用FragmentStatePagerAdapter
  2. FragmentStatePagerAdapter:和 FragmentPagerAdapter不一樣的是,正如其類名中的 'State' 所表明的含義一樣,該 PagerAdapter 的實(shí)現(xiàn)將只保留當(dāng)前頁面,當(dāng)頁面離開視線后,就會被消除,釋放其資源;而在頁面需要顯示時(shí),生成新的頁面(就像 ListView 的實(shí)現(xiàn)一樣)。這么實(shí)現(xiàn)的好處就是當(dāng)擁有大量的頁面時(shí),不必在內(nèi)存中占用大量的內(nèi)存。
  3. 這兩個(gè)adapter最大的不同在于instantiateItem()這個(gè)方法

接下來看看adapter里面getItemPosition這個(gè)方法
可以返回的值為POSITION_UNCHANGEDPOSITION_NONE這兩個(gè)值。
而默認(rèn)都是返回POSITION_UNCHANGED
這個(gè)返回值會在adapter的instantiateItem()方法里進(jìn)行判斷:
POSITION_UNCHANGED不重新加載item
POSITION_NONE要求重新加載item

而網(wǎng)上的一些解決方案是直接復(fù)寫FragmentPagerAdaptergetItemPosition返回POSITION_NONE,這樣做及違反了FragmentPagerAdapter的設(shè)計(jì)原則(保存在內(nèi)存,加載更快等)也沒有解決今天這個(gè)坑,一樣是界面沒有刷新的。

繼續(xù)說下去

假如返回POSITION_NONE要求從新加載Item,ViewPager會首先去FragmentManager里面去查找有沒有相關(guān)的fragment如果有就直接使用如果沒有才會觸發(fā)FragmentPageadAptergetItem方法獲取一個(gè)fragment。所以你更新的fragmentList集合是沒有作用的,還要清除FragmentManager里面緩存的fragment。

這樣今天的解決方案思路救出來了:

  1. 復(fù)寫notifyDataSetChanged
@Override
public void notifyDataSetChanged() {
    // 重寫這個(gè)方法,取到子Fragment的數(shù)量,用于下面的判斷,以執(zhí)行多少次刷新
    mChildCount = getCount();
    super.notifyDataSetChanged();
}
  1. 復(fù)寫getItemPosition,根據(jù)mChildCount判斷是返回POSITION_UNCHANGED還是itemPOSITION_NONE
@Override
public int getItemPosition(Object object) {
    if ( mChildCount > 0) {
        // 這里利用判斷執(zhí)行若干次不緩存,刷新
        mChildCount --;
        // 返回這個(gè)是itemPOSITION_NONE
        return POSITION_NONE;
    }
    // 這個(gè)則是POSITION_UNCHANGED
    return super.getItemPosition(object);}
  1. notifyDataSetChanged之前對FragmentManager進(jìn)行相應(yīng)的刪除操作。
if (viewPager.getAdapter() != null) {
    FragmentManager fm = getSupportFragmentManager();
    FragmentTransaction ft = fm.beginTransaction();
    List<Fragment> fragments = fm.getFragments();
    if(fragments != null && fragments.size() >0){
        for (int i = 0; i < fragments.size(); i++) {
            ft.remove(fragments.get(i));
        }
    }
    ft.commit();}
  1. 這樣就會在notifyDataSetChanged的時(shí)候刷新視圖,在平時(shí)滑動等情況使用緩存視圖,既保留了FragmentPagerAdapter的特點(diǎn),又解決了今天的坑。

到此,今天的坑又總算是跨過去了,如果有幫組到你,歡迎關(guān)注我的博客github
源碼下載
本文參考自:


2016年9月7日 00:35:49

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

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

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