我對懶加載的定義是:數據的加載要等到頁面對用戶可見時才加載,不然的話會浪費用戶流量。網上實現懶加載的方案很是多,但大多數都是解決了我下面說到的場景一的懶加載,本文還解決場景二的懶加載方式。java
若是不想看下面的分析,直接這個類導入你的項目中,須要懶加載的Fragment繼承這個類,並重寫相應的方法就行:傳送門。git
什麼?不會用Viewpager,能夠看一下這個入門系列:ViewPager 詳解(一)---基本入門。github
場景一應該是不少人都遇到過的狀況,界面總體使用Viewpager + Tablayout + Fragment組合,左右滑動界面以展現數據給用戶,當你滑動到下一頁的時候,Fragment已經有數據了,可是這個時候我但願開始加載數據而不是已經有了數據,特別是Viewpager 的適配器使用FragmentPagerAdapter的時候,由於這個適配器它會預加載好相鄰的Fragment頁面,這個預加載數量能夠經過以下設置:微信
viewPager.setOffscreenPageLimit(0);
複製代碼
那麼上面這句代碼不是把預加載數量設置爲0了嗎?這樣Fragment就不會預先加載了,這樣想你就太天真,經過看setOffscreenPageLimit的源碼得知,若是你傳入的數值小於1,那麼ViewPager就會把預加載數量設置成默認值,而默認值就是1,因此說就算你傳入了0,ViewPager仍是會預先加載好當前頁面的左右兩個Fragment頁面。app
那麼怎麼解決呢?這時要認識Fragment中的一個函數:setUserVisibleHint(boolean isVisibleToUser):ide
setUserVisibleHint方法是Fragment中的一個回調函數。當前Fragment可見對用戶可見時,setUserVisibleHint()回調,其中參數isVisibleToUser=true,當前Fragment由可見到不可見或實例化時,setUserVisibleHint()回調,其中參數isVisibleToUser=false。函數
下面看一下這個方法在Fragment生命週期中的調用時機:佈局
能夠看到此時setUserVisibleHint的調用時機老是在初始化時調用,可見時調用,由可見轉換成不可見時調用。this
下面講講場景一的懶加載實現思路:咱們通常在Fragment的onActivityCreated中加載數據,這個時候咱們能夠判斷此時的Fragment是否對用戶可見,調用fragment.getUserVisibleHint()能夠得到isVisibleToUser的值,若是爲true,表示可見,就加載數據,若是不可見,就不加載數據了,代碼以下:spa
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(isFragmentVisible(this) && this.isAdded()){
if (this.getParentFragment() == null || isFragmentVisible(this.getParentFragment())) {
onLazyLoadData();
isLoadData = true;
//...
}
}
}
複製代碼
判讀Fragment是否對用戶可見封裝在isFragmentVisible方法中, onLazyLoadData()是子類須要重寫的方法,用來加載數據,加載完數據後把isLoadData設置爲true,表示已經加載過數據。
上面就控制了當Fragment不可見時就不加載數據,並且此時Fragment的生命週期也走到onResume了,那麼當我滑到這個Fragment時,只會調用它的setUserVisibleHint方法,那麼就要在setUserVisibleHint方法中加載數據,代碼以下:
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isFragmentVisible(this) && !isLoadData && isViewCreated && this.isAdded()){
onLazyLoadData();
isLoadData = true;
}
}
複製代碼
isViewCreated字段表示佈局是否被初始化,它在onViewCreated方法中被賦值爲true,以下:
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
isViewCreated = true;
}
複製代碼
onViewCreated方法的回調在onCreateView方法後,當調用onViewCreated方法時,Fragment的View佈局必定建立好了。
咱們再回到setUserVisibleHint方法中,在if中它會依此判斷當前Fragment可見、尚未加載數據、佈局已經建立好等這些條件知足後才加載數據,並把isLoadData賦值爲true。
下面是我在項目中使用的狀況:
能夠看到,當我滑倒這個Fragment時才加載數據。
這個場景就是你把幾個Fragment經過FragmentTransaction的add方法add到FragmentManager 中,切換Fragment的時候經過FragmentTransaction的hide和show方法配合使用,相似於微信的主界面,底部有一個tab,而後點擊tab,切換頁面。
當Fragment被add進manager中時,Fragment生命週期已經執行到onResume了,因此在後續的hide和show方法切換Fragment時,Fragment已經有數據了,在個人項目中,我想要的效果是,當我點到這個tab時,該tab對於的Fragment才加載數據,因此我對這種狀況實現了懶加載。
那麼要怎麼實現呢?照搬場景2的實現方式?惋惜了,不行,由於這種狀況下setUserVisibleHint方法不會被調用。這個時候咱們又從新認識一個方法onHiddenChanged(boolean hidden):
onHiddenChanged方法是當Fragment的隱藏狀態變化示被調用,當Fragment沒有被隱藏時即調用show方法,當前onHiddenChanged回調,其中參數hidde=false,當Fragment被隱藏時即調用hide了方法,onHiddenChanged()回調,其中參數hidde=true。還有一點注意的是使用hide和show時,fragment的全部生命週期方法都不會調用,除了onHiddenChanged()。
下面看一下這個方法在Fragment生命週期中的調用時機:
能夠看到此時onHiddenChanged的調用時機老是在初始化時調用,hide時調用,show時調用。
場景二是在setUserVisibleHint方法中作文章,而此次是在onHiddenChanged方法中作文章,以下:
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
//一、onHiddenChanged調用在Resumed以前,因此此時可能fragment被add, 但還沒resumed
if(!hidden && !this.isResumed())
return;
//二、使用hide和show時,fragment的全部生命週期方法都不會調用,除了onHiddenChanged()
if(!hidden && isFirstVisible && this.isAdded()){
onLazyLoadData();
isFirstVisible = false;
}
}
複製代碼
首先看註釋1,由於當add的時候,onHiddenChanged調用在onResumed以前,此時尚未執行onResume方法,用戶還看不見這個Fragment,若是此時加載數據就沒有什麼用,等於用戶看到這個Fragmen時它就已經執行完數據了,因此這裏要加一個判斷,若是Fragment尚未Resume,就直接return,不作操做。
接下來看註釋2,執行到註釋2表示此時Fragment已經可見了,就能夠經過hidden字段控制懶加載,hidden爲false表示調用了show方法,經過isFirstVisible控制只加載一次,爲何要用isFirstVisible呢,由於在onActivityCreate方法中就有可能已經加載過數據,若是加載過就不用再加載了,在onActivityCreate中會把這個字段賦值爲true,以下:
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(isFragmentVisible(this) && this.isAdded()){
if (this.getParentFragment() == null || isFragmentVisible(this.getParentFragment())) {
onLazyLoadData();
isLoadData = true;
if(isFirstVisible)
isFirstVisible = false;
}
}
}
複製代碼
下面是我在項目中使用的狀況:
能夠看到,當我點擊到這個tab時,對應的Fragment才加載數據。
以上就是個人懶加載歷程,雖然如今也有一些Fragment庫能夠實現這個效果,可是它的原理也是這個,咱們要知其因此然,該懶加載類整合場景一和場景二,只有簡單的幾句代碼,只要繼承就能在兩種場景下使用。
參考文章: