Android PagerAdapter notifyDatasetChanged

forget to call notifyDatasetChanged 錯誤

這幾天一直在完善之前自己的寫的Android應用。想用ViewPager讓之前界面能夠滑動閱讀。但是滑動過程中,經常會莫名其妙地遇到一個問題。莫名其妙地提示ViewPager expected 10 counts,but found 20. forget to call notifyDatasetChanged. 意思很明顯,就是ViewPager的數據集改變了,但是我卻忘了去通過notifyDatasetChanged去通知PagerAdapter數據已經更新了。但是我其實在那個頁面壓根沒有去更新數據。不過根據提示顯然是因爲哪個地方改變了數據的數量。還好我動態更新數據的地方不多。檢查一下代碼,根據自己的思路,立刻發現了問題。我會在已經載入當前列表的時候,去服務器取下一個列表。這樣在我取完下一個列表的時候,數據就變了。然後就出現那個問題了。

雖然沒怎麼浪費時間就解決了問題,但是在面對多個線程數據共享的時候確實得增加一些額外的考慮。PagerAdapter的數據如果改變,是需要調用notifyDatasetChanged方法的。多線程的共享需要考慮同步的問題,這個同步不僅僅是一個synchronize就能夠避免的,每個線程都需要去預防共享的數據被另外一個線程更改了。

ViewPager與Fragment

我在使用ViewPager的時候,使用了FragmentStatePagerAdapter作爲它的Adapter,因爲會有很多Fragment,如何從ViewPager控制它的Fragment就成了一個問題。

當然,直接保存ViewPager的所有Fragment似乎就能夠控制了,但是這實在是不科學,保存起來太耗空間。而實際上ViewPager每次只有幾個View保存着。當前顯示的View以及前後幾個View,在這之外的View都會被destory掉。這樣能夠很好地控制佔用的內存。

但是幸運地是FragmentActivity的FragmentManager提供了一個getFragments的方法,該方法能夠獲取當前所有的Fragment。這樣問題就解決了。獲取Activity中所有的Fragment,然後判斷是不是FragmentStatePagerAdapter的Fragment。這樣ViewPager就能夠直接控制它的Fragment Item了。

對於FragmentPagerAdapter與FragmentStatePagerAdapter的區別,FragmentPagerAdapter會對每一個Fragment進行緩存,而FragmentStatePagerAdapter不會。兩個的使用方法是一樣的。

notifyDatasetChanged做了什麼工作?

每個Adapter都會有private DataSetObservable mObservable = new DataSetObservable(); 一個DataSetObservable觀察者。這個觀察者可以註冊registerDataSetObserver。而notifyDatasetChanged函數做的就是通知註冊這,我的數據已經改變了。

這樣就給了ViewPager監聽Adapter數據集變化的機會。在ViewPager的setAdapter中,有下面這段代碼:

    431        if (mAdapter != null) {
    432            if (mObserver == null) {
    433                mObserver = new PagerObserver();
    434            }
    435            mAdapter.registerDataSetObserver(mObserver);
    436            mPopulatePending = false;
    437            final boolean wasFirstLayout = mFirstLayout;
    438            mFirstLayout = true;
    439            mExpectedAdapterCount = mAdapter.getCount();
    440            if (mRestoredCurItem >= 0) {
    441                mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader);
    442                setCurrentItemInternal(mRestoredCurItem, false, true);
    443                mRestoredCurItem = -1;
    444                mRestoredAdapterState = null;
    445                mRestoredClassLoader = null;
    446            } else if (!wasFirstLayout) {
    447                populate();
    448            } else {
    449                requestLayout();
    450            }
    451        }

在上面的代碼中,註冊了數據集變化的觀察者。而notifyDatasetChanged就是通知觀察者。


探究

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章