最近在做一個項目,有一個功能是答題翻頁。於是需要實現在這一頁的時候就緩存下一頁。
剛剛開始我是用
setOnPageChangeListener方法監聽,滑到這一頁的時候才刷新這一頁:
public void onPageSelected(int position) { ReadFragment fragment= (ReadFragment) fragmentArrayList.get(position); fragment.refresh(); }
不過這樣就只有滑動到這一頁的時候才能用fragmentArrayList.get(position)獲取當前頁,用這種方法獲取下一頁的fragment就會報空指針。也就是說無法先緩存刷新下一頁的內容。
到底怎么樣才能獲取得到下一頁的fragment呢?
百度了一下好像說要在
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; }
可以看出:
instantiateItem方法中並不是直接去List里面拿到Fragment,而是先從FragmentManager中通過Tag找對應的Fragment,如果可以找到就不會去List里面拿了,介於這種情況,我在adapter中加入了這個方法:
public ReadFragment getFragment(int position) { String tag = getFragmentTag(mContainer.getId(),position); ReadFragment fragment = (ReadFragment) fm.findFragmentByTag(tag); return fragment; } /** * 運用反射機制調用FragmentPagerAdapter的getFragmentTag的方法 * @param viewId * @param index * @return */ private String getFragmentTag(int viewId, int index) { try { Class<FragmentPagerAdapter> cls = FragmentPagerAdapter.class; Class<?>[] parameterTypes = { int.class, long.class }; Method method = cls.getDeclaredMethod("makeFragmentName", parameterTypes); method.setAccessible(true); String tag = (String) method.invoke(this, viewId, index); return tag; } catch (Exception e) { e.printStackTrace(); return ""; } }
在onPageSelected里面調用getFragment(int position)方法獲取下一頁的內容,達到當選中這一頁的時候就先緩存刷新下一頁。
getFragment(int position)方法其實就是拿到緩存的fragment(這里的緩存是指viewpager緩存的fragment),不過就得先保證該fragment已經先在viewpager中緩存了,雖然內容還沒有刷新(每個fragment里面顯示的內容都是一樣的),這樣就不會報空指針了。
出現了一個問題,onPageSelected在viewPager展示第一頁的時候是不會調用的,所以第一頁的內容還是得另外刷新,無法在onPageSelected里面刷新。
建立一個方法initData(),在里面單獨刷新第一頁的fragment。
由於viewPager展示第一頁的時候不會調用onPageSelected,那也就導致了第一頁和第二頁的內容都無法先得到緩存(因為第二頁是在第一頁選中的時候就事先開始緩存的),所以第一頁和第二頁的內容都得在initData()里面單獨刷新,其它的通過onPageSelected里面的方法來刷新。
到了這里總結一下思路:
剛剛進入界面的時候:刷新第一頁,緩存第二頁。
翻頁時候:從第一頁翻到第二頁,執行onPageSelected()
onPagerSelected里面調用方法getFragment(int position),獲取到下一頁即第三頁的fragment,然后刷新緩存內容。
從第二頁翻到第三頁:執行onPageSelected()
onPagerSelected里面調用方法getFragment(int position),獲取到下一頁即第四頁的fragment,然后刷新緩存內容。
結果又出現了又一個問題:從第一頁翻到第二頁的時候,閃退了,報空指針。
后來調試了一下發現getFragment方法得到的fragment為null,沒道理,為啥,想到最后才發現原來是因為第三頁的fragment在viewPager中沒有緩存,而我們的getFragment(int position)是通過tag標記向緩存中拿的。
怎么才能讓第三個fragment在viewPager中實現得到緩存呢?
默認的,viewpager在第一頁的時候會緩存第二頁,到了第二頁的時候會緩存第一與第二頁,實踐證明,只有當第二頁完全顯示的時候,第三頁才會得到緩存,而onPagerSelected在fragment滑到超過屏幕一半而且我們手指放開了才會調用,如果我們的手指沒有放開是不會被調用的,當我們的手指放開,onPagerSelected被調用的時候,第三頁還沒有得到緩存。
怎么辦?我又想到了
@Override public void onPageScrollStateChanged(int state) {}
本認為可以在里面判斷state==2,即滑動停止的時候,才緩存刷新這一頁,最后才發現還是有一樣的問題
原來滑動停止指的是手指的滑動,即手指離開屏幕,而不是指改fragment的滑動,因此還是會出現上面的情況。
怎么辦,不用怕:還有一個方法:readViewPager.setOffscreenPageLimit(2);
該方法可以給你答案。
這個方法可以設置viewPager當前頁兩邊的緩存數目,如readViewPager.setOffscreenPageLimit(2);當前頁左右各緩存2個。
viewPager默認的是readViewPager.setOffscreenPageLimit(1);
這樣就OK了?,高興太早了。滑到第五頁的時候出問題了,是空白的。改為readViewPager.setOffscreenPageLimit(3);也一樣。
(此時我總共只有4個fragment,我采用的是無限循環模式,實際的fragment有4個)而實際緩存的有5個fragment(當前頁加左右兩個),會不會是這里出問題。
關於怎么樣設置無限循環,可以參考我另外一篇博客:http://www.cnblogs.com/tangZH/p/6516566.html
於是我把fragment改為5個,結果沒問題了。
但是往回翻頁的時候出問題了,翻幾頁后又出現了空白頁,於是我改為6個fragment,才完全沒問題。
暈。
還有往回翻頁也需要刷新上一頁的內容。
