ViewPager+Fragment 组合的预加载和懒加载
ViewPager+Fragment 組合的預(yù)加載和懶加載
轉(zhuǎn)載自http://www.crocutax.com
預(yù)加載介紹
ViewPager+Fragment的搭配在日常開發(fā)中也比較常見,可用于切換展示不同類別的頁面,我們?nèi)粘K姷淖稍儭①徫铩⒔鹑凇⑸缃坏阮愋偷腁PP都有機會用到這種控件組合.
例如:
今日頭條APP
ViewPager控件有個特有的預(yù)加載機制,即默認情況下當前頁面左右兩側(cè)的1個頁面會被加載,以方便用戶滑動切換到相鄰的界面時,可以更加順暢的顯示出來.
通過ViewPager的setOffscreenPageLimit(int limit)可以設(shè)置預(yù)加載頁面數(shù)量,當前頁面相鄰的limit個頁面會被預(yù)加載進內(nèi)存.
效果如下:注意看Log輸出
viewpager預(yù)加載2頁
懶加載介紹
所謂的懶加載,其實也就是延遲加載,就是等到該頁面的UI展示給用戶時,再加載該頁面的數(shù)據(jù)(從網(wǎng)絡(luò)、數(shù)據(jù)庫等),而不是依靠ViewPager預(yù)加載機制提前加載兩三個,甚至更多頁面的數(shù)據(jù).這樣可以提高所屬Activity的初始化速度,也可以為用戶節(jié)省流量.而這種懶加載的方式也已經(jīng)/正在被諸多APP所采用.
但是通過ViewPager方法setOffscreenPageLimit(int limit)的源碼可以發(fā)現(xiàn),ViewPager通過一定的邏輯判斷來確保至少會預(yù)加載左右兩側(cè)相鄰的1個頁面,也就是說無法通過簡單的配置做到懶加載的效果.
ViewPager方法setOffscreenPageLimit(int limit) 相關(guān)源碼
//默認的緩存頁面數(shù)量(常量) private static final int DEFAULT_OFFSCREEN_PAGES = 1;//緩存頁面數(shù)量(變量) private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;public void setOffscreenPageLimit(int limit) {//當我們手動設(shè)置的limit數(shù)小于默認值1時,limit值會自動被賦值為默認值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)過前面的攔截判斷后,將limit的值設(shè)置給mOffscreenPageLimit,用于mOffscreenPageLimit = limit;populate();} }關(guān)于變量mOffscreenPageLimit到底是什么.可以從其get方法注釋中略見端倪
/*** 返回空閑狀態(tài)下的視圖層級中,當前頁面任何一側(cè)保存的頁面數(shù)量,默認是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ù)加載的,暫不追查,因為此次的目的并不是ViewPager運行原理分析.
如何做到懶加載
既然通過ViewPager無法達到我們想要的懶加載效果,那么就得從Fragment自身入手了.
Fragment為我們提供了一個方法setUserVisibleHint(boolean isVisibleToUser),其中的參數(shù)isVisibleToUser就是表示該Fragment的UI對于用戶是否可見
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; }大意就是通過此方法來設(shè)置Fragment的UI對用戶是否可見,當該頁面對用戶可見/不可見時,系統(tǒng)都會回調(diào)此方法.
我們可以重寫此方法,然后根據(jù)回調(diào)的isVisibleToUser參數(shù)來進行相關(guān)的邏輯判斷,以達到懶加載的效果,比如如果isVisibleToUser==true的話表示當前Fragment對用戶可見,此時再去加載頁面數(shù)據(jù).
由于ViewPager內(nèi)會裝載多個Fragment,而這種懶加載機制對于各個Fragment屬于共同操作,因此適合將其抽取到BaseFragment中.
注意
setUserVisibleHint(boolean isVisibleToUser)方法會多次回調(diào),而且可能會在onCreateView()方法執(zhí)行完畢之前回調(diào).如果isVisibleToUser==true,然后進行數(shù)據(jù)加載和控件數(shù)據(jù)填充,但是onCreateView()方法并未執(zhí)行完畢,此時就會出現(xiàn)NullPointerException空指針異常.
基于以上原因,我們進行數(shù)據(jù)懶加載的時機需要滿足兩個條件
所以在BaseFragment中用兩個布爾型標記來記錄這兩個條件的狀態(tài).只有同時滿足了,才能加載數(shù)據(jù)
//Fragment的View加載完畢的標記 private boolean isViewCreated;//Fragment對用戶可見的標記 private boolean isUIVisible;第一步,改變isViewCreated標記
當onViewCreated()方法執(zhí)行時,表明View已經(jīng)加載完畢,此時改變isViewCreated標記為true,并調(diào)用lazyLoad()方法
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);isViewCreated = true;lazyLoad(); }第二步,改變isUIVisible標記
當setUserVisibleHint(boolean isVisibleToUser)回調(diào)為true時,改變isUIVisible標記為true,并調(diào)用lazyLoad()方法
public void setUserVisibleHint(boolean isVisibleToUser) {super.setUserVisibleHint(isVisibleToUser);//isVisibleToUser這個boolean值表示:該Fragment的UI 用戶是否可見if (isVisibleToUser) {isUIVisible = true;lazyLoad();} else {isUIVisible = false;} }第三步: 在lazyLoad()方法中進行雙重標記判斷,通過后即可進行數(shù)據(jù)加載
private void lazyLoad() {//這里進行雙重標記判斷,是因為setUserVisibleHint會多次回調(diào),并且會在onCreateView執(zhí)行前回調(diào),必須確保onCreateView加載完畢且頁面可見,才加載數(shù)據(jù)if (isViewCreated && isUIVisible) {loadData();//數(shù)據(jù)加載完畢,恢復(fù)標記,防止重復(fù)加載isViewCreated = false;isUIVisible = false;printLog(mTextviewContent+"可見,加載數(shù)據(jù)");} }第四步:定義抽象方法loadData(),具體加載數(shù)據(jù)的工作,交給子類去完成
protected abstract void loadData();注意: 數(shù)據(jù)加載完畢要恢復(fù)標記,防止數(shù)據(jù)重復(fù)加載
效果如下:
Demo源碼
Github源碼
總結(jié)
以上是生活随笔為你收集整理的ViewPager+Fragment 组合的预加载和懒加载的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 重装机兵2战车位置图(小白一键重装系统官
- 下一篇: 加载数据时先显示小圆圈不显示内容