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