ViewPager+Fragment 组合的预加载和懒加载
ViewPager+Fragment 組合的預(yù)加載和懶加載
轉(zhuǎn)載自http://www.crocutax.com
預(yù)加載介紹
ViewPager+Fragment的搭配在日常開(kāi)發(fā)中也比較常見(jiàn),可用于切換展示不同類別的頁(yè)面,我們?nèi)粘K?jiàn)的咨詢、購(gòu)物、金融、社交等類型的APP都有機(jī)會(huì)用到這種控件組合.
例如:
今日頭條APP
ViewPager控件有個(gè)特有的預(yù)加載機(jī)制,即默認(rèn)情況下當(dāng)前頁(yè)面左右兩側(cè)的1個(gè)頁(yè)面會(huì)被加載,以方便用戶滑動(dòng)切換到相鄰的界面時(shí),可以更加順暢的顯示出來(lái).
通過(guò)ViewPager的setOffscreenPageLimit(int limit)可以設(shè)置預(yù)加載頁(yè)面數(shù)量,當(dāng)前頁(yè)面相鄰的limit個(gè)頁(yè)面會(huì)被預(yù)加載進(jìn)內(nèi)存.
效果如下:注意看Log輸出
viewpager預(yù)加載2頁(yè)
懶加載介紹
所謂的懶加載,其實(shí)也就是延遲加載,就是等到該頁(yè)面的UI展示給用戶時(shí),再加載該頁(yè)面的數(shù)據(jù)(從網(wǎng)絡(luò)、數(shù)據(jù)庫(kù)等),而不是依靠ViewPager預(yù)加載機(jī)制提前加載兩三個(gè),甚至更多頁(yè)面的數(shù)據(jù).這樣可以提高所屬Activity的初始化速度,也可以為用戶節(jié)省流量.而這種懶加載的方式也已經(jīng)/正在被諸多APP所采用.
但是通過(guò)ViewPager方法setOffscreenPageLimit(int limit)的源碼可以發(fā)現(xiàn),ViewPager通過(guò)一定的邏輯判斷來(lái)確保至少會(huì)預(yù)加載左右兩側(cè)相鄰的1個(gè)頁(yè)面,也就是說(shuō)無(wú)法通過(guò)簡(jiǎn)單的配置做到懶加載的效果.
ViewPager方法setOffscreenPageLimit(int limit) 相關(guān)源碼
//默認(rèn)的緩存頁(yè)面數(shù)量(常量) private static final int DEFAULT_OFFSCREEN_PAGES = 1;//緩存頁(yè)面數(shù)量(變量) private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;public void setOffscreenPageLimit(int limit) {//當(dāng)我們手動(dòng)設(shè)置的limit數(shù)小于默認(rèn)值1時(shí),limit值會(huì)自動(dòng)被賦值為默認(rèn)值1(即DEFAULT_OFFSCREEN_PAGES)if (limit < DEFAULT_OFFSCREEN_PAGES) {Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "+ DEFAULT_OFFSCREEN_PAGES);limit = DEFAULT_OFFSCREEN_PAGES;}if (limit != mOffscreenPageLimit) {//經(jīng)過(guò)前面的攔截判斷后,將limit的值設(shè)置給mOffscreenPageLimit,用于mOffscreenPageLimit = limit;populate();} }關(guān)于變量mOffscreenPageLimit到底是什么.可以從其get方法注釋中略見(jiàn)端倪
/*** 返回空閑狀態(tài)下的視圖層級(jí)中,當(dāng)前頁(yè)面任何一側(cè)保存的頁(yè)面數(shù)量,默認(rèn)是1* Returns the number of pages that will be retained to either side of the* current page in the view hierarchy in an idle state. Defaults to 1.** @return How many pages will be kept offscreen on either side* @see #setOffscreenPageLimit(int)*/ public int getOffscreenPageLimit() { return mOffscreenPageLimit; }至于mOffscreenPageLimit到底是怎么影響ViewPager控件預(yù)加載的,暫不追查,因?yàn)榇舜蔚哪康牟⒉皇荲iewPager運(yùn)行原理分析.
如何做到懶加載
既然通過(guò)ViewPager無(wú)法達(dá)到我們想要的懶加載效果,那么就得從Fragment自身入手了.
Fragment為我們提供了一個(gè)方法setUserVisibleHint(boolean isVisibleToUser),其中的參數(shù)isVisibleToUser就是表示該Fragment的UI對(duì)于用戶是否可見(jiàn)
Fragment的方法 setUserVisibleHint(boolean isVisibleToUser)
/*** Set a hint to the system about whether this fragment's UI is currently visible* to the user. This hint defaults to true and is persistent across fragment instance* state save and restore.** <p>An app may set this to false to indicate that the fragment's UI is* scrolled out of visibility or is otherwise not directly visible to the user.* This may be used by the system to prioritize operations such as fragment lifecycle updates* or loader ordering behavior.</p>** <p><strong>Note:</strong> This method may be called outside of the fragment lifecycle.* and thus has no ordering guarantees with regard to fragment lifecycle method calls.</p>** @param isVisibleToUser true if this fragment's UI is currently visible to the user (default),* false if it is not.*/ public void setUserVisibleHint(boolean isVisibleToUser) {if (!mUserVisibleHint && isVisibleToUser && mState < STARTED&& mFragmentManager != null && isAdded()) {mFragmentManager.performPendingDeferredStart(this);}mUserVisibleHint = isVisibleToUser;mDeferStart = mState < STARTED && !isVisibleToUser; }大意就是通過(guò)此方法來(lái)設(shè)置Fragment的UI對(duì)用戶是否可見(jiàn),當(dāng)該頁(yè)面對(duì)用戶可見(jiàn)/不可見(jiàn)時(shí),系統(tǒng)都會(huì)回調(diào)此方法.
我們可以重寫(xiě)此方法,然后根據(jù)回調(diào)的isVisibleToUser參數(shù)來(lái)進(jìn)行相關(guān)的邏輯判斷,以達(dá)到懶加載的效果,比如如果isVisibleToUser==true的話表示當(dāng)前Fragment對(duì)用戶可見(jiàn),此時(shí)再去加載頁(yè)面數(shù)據(jù).
由于ViewPager內(nèi)會(huì)裝載多個(gè)Fragment,而這種懶加載機(jī)制對(duì)于各個(gè)Fragment屬于共同操作,因此適合將其抽取到BaseFragment中.
注意
setUserVisibleHint(boolean isVisibleToUser)方法會(huì)多次回調(diào),而且可能會(huì)在onCreateView()方法執(zhí)行完畢之前回調(diào).如果isVisibleToUser==true,然后進(jìn)行數(shù)據(jù)加載和控件數(shù)據(jù)填充,但是onCreateView()方法并未執(zhí)行完畢,此時(shí)就會(huì)出現(xiàn)NullPointerException空指針異常.
基于以上原因,我們進(jìn)行數(shù)據(jù)懶加載的時(shí)機(jī)需要滿足兩個(gè)條件
所以在BaseFragment中用兩個(gè)布爾型標(biāo)記來(lái)記錄這兩個(gè)條件的狀態(tài).只有同時(shí)滿足了,才能加載數(shù)據(jù)
//Fragment的View加載完畢的標(biāo)記 private boolean isViewCreated;//Fragment對(duì)用戶可見(jiàn)的標(biāo)記 private boolean isUIVisible;第一步,改變isViewCreated標(biāo)記
當(dāng)onViewCreated()方法執(zhí)行時(shí),表明View已經(jīng)加載完畢,此時(shí)改變isViewCreated標(biāo)記為true,并調(diào)用lazyLoad()方法
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);isViewCreated = true;lazyLoad(); }第二步,改變isUIVisible標(biāo)記
當(dāng)setUserVisibleHint(boolean isVisibleToUser)回調(diào)為true時(shí),改變isUIVisible標(biāo)記為true,并調(diào)用lazyLoad()方法
public void setUserVisibleHint(boolean isVisibleToUser) {super.setUserVisibleHint(isVisibleToUser);//isVisibleToUser這個(gè)boolean值表示:該Fragment的UI 用戶是否可見(jiàn)if (isVisibleToUser) {isUIVisible = true;lazyLoad();} else {isUIVisible = false;} }第三步: 在lazyLoad()方法中進(jìn)行雙重標(biāo)記判斷,通過(guò)后即可進(jìn)行數(shù)據(jù)加載
private void lazyLoad() {//這里進(jìn)行雙重標(biāo)記判斷,是因?yàn)閟etUserVisibleHint會(huì)多次回調(diào),并且會(huì)在onCreateView執(zhí)行前回調(diào),必須確保onCreateView加載完畢且頁(yè)面可見(jiàn),才加載數(shù)據(jù)if (isViewCreated && isUIVisible) {loadData();//數(shù)據(jù)加載完畢,恢復(fù)標(biāo)記,防止重復(fù)加載isViewCreated = false;isUIVisible = false;printLog(mTextviewContent+"可見(jiàn),加載數(shù)據(jù)");} }第四步:定義抽象方法loadData(),具體加載數(shù)據(jù)的工作,交給子類去完成
protected abstract void loadData();注意: 數(shù)據(jù)加載完畢要恢復(fù)標(biāo)記,防止數(shù)據(jù)重復(fù)加載
效果如下:
Demo源碼
Github源碼
總結(jié)
以上是生活随笔為你收集整理的ViewPager+Fragment 组合的预加载和懒加载的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 重装机兵2战车位置图(小白一键重装系统官
- 下一篇: 惠普1020硒鼓型号是什么