天天看點

Tablayout+Fragment+Viewpager 懶加載問題懶加載問題的完美解決

懶加載問題的完美解決

前言:

ViewPager會對其中的Fragment進行預加載。也就是說使用者第一次打開第一個界面的時候,不僅第一個界面會進行加載,其他的界面也會進行界面的預加載。這樣就會帶來界面啟動加載慢,浪費系統資源和使用者流量的不好的體驗。而Fragment的懶加載恰好可以解決這個問題.

定義:

什麼是懶加載?就是被動加載,當Fragment頁面可見時,才從網絡加載資料并顯示出來

懶加載條件:

1.View視圖加載完畢,即onCreateView()執行完成

2.目前Fragment可見,即setUserVisibleHint()的參數為true

3.初次加載,即防止多次滑動重複加載

是以要設定三個布爾類型參數,進行判斷

完美解決方案:

建立一個抽象父類BaseLazyLoadFragment,在父類中實作懶加載過程,把接口暴露給子類去實作

其中的問題:

問題一:setUserVisibleHint方法在切換界面時會多次調用,而我們隻希望他被調用一次,既第一次進入頁面時被調用。

解決:設定一個布爾類型參數mIsFirstLoad,保證是第一次加載

問題二:setUserVisibleHint是Fragment的可見性變化時回調的方法,而onCreateView是Fragment建立視圖時回調的方法。但是我們無法保證setUserVisibleHint的調用發生在onCreateView(也就是視圖建立)之後。那麼我們就是在視圖還沒有建立時進行資料加載,而往往資料的加載會對視圖控件進行操作,那麼就會造成空指針的異常發生(因為視圖控件還沒有初始化)。

解決:在設定一個布爾類型參數mIsPrepare,這樣就能保證視圖建立完成之前不會進行資料加載的操作。

問題三:setUserVisibleHint這個方法隻在Fragment的可見性改變的時候才會被調用,如果第一次進入頁面之後setUserVisibleHint先被調用,這時視圖還沒有完成建立,是以資料加載操作不會被調用。而之後沒有切換頁面,Fragment的可見性也就不會發生改變了,setUserVisibleHint也就不會被調用了,那麼資料加載也就不會被執行了。

解決:既然要保證在視圖建立完成後要進行一次資料加載,那麼就在onCreateView方法中手動調用一次資料加載就好了

當然在onCreateView中確定了View已經準備好時,将mPrepare置為true,在setUserVisibleHint中確定了目前可見時,mIsVisible置為true,第一次加載完畢後則将mIsFirstLoad置為false,避免重複加載。

代碼如下(可根據需求定義抽象方法)

public abstract class BaseLazyLoadFragment extends Fragment {

    boolean mIsPrepare = false;		//視圖還沒準備好
    boolean mIsVisible= false;		//不可見
    boolean mIsFirstLoad = true;	//第一次加載
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        View view = initView(inflater, container);

        mIsPrepare=true;
        startLoad();
        return view;
    }

    private void startLoad() {

        if (!mIsPrepare || !mIsVisible||!mIsFirstLoad) {
            return;
        }
        onLazyLoad();
        //資料加載完畢,恢複标記,防止重複加載
        mIsFirstLoad = false;
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        //isVisibleToUser這個boolean值表示:該Fragment的UI 使用者是否可見

        if (isVisibleToUser) {
            mIsVisible = true;
            startLoad();
        } else {
            mIsVisible = false;
        }
        super.setUserVisibleHint(isVisibleToUser);
    }

    @Override
    public void onDestroyView() {
        mIsFirstLoad=true;
        mIsPrepare=false;
        mIsVisible = false;
        super.onDestroyView();
    }

    //資料加載接口,留給子類實作
    public abstract void onLazyLoad();

    //初始化視圖接口,子類必須實作
    public abstract View initView(LayoutInflater inflater, @Nullable ViewGroup container);

}


           

view繪制導緻的卡頓優化

fragment的加載最為耗時的步驟主要有兩個,

一個是Fragment建立(尤其是建立View的過程),

另一個就是讀取資料填充到View上的過程。懶加載能夠解決後者所造成的卡頓,但是針對前者來說,并沒有效果

1.設定緩存頁面數

viewPager.setOffscreenPageLimit(int limit) 能夠有效地一次性緩存多個Fragment,這樣就能夠解決在之後每次切換時不會建立執行個體對象,看起來也會流暢。但是這樣的做法,最大的缺點就是容易造成第一次啟動時非常緩慢!如果第一次啟動時間滿足要求的話,就使用這種簡單地辦法吧。

2.避免Fragment的銷毀

@Override
        public void destroyItem(ViewGroup container, int position, Object object) {
           // super.destroyItem(container, position, object);
        }
           

把中間的代碼注釋掉就行了,這樣就可以避免Fragment的銷毀過程

源碼的這個過程之中包含了對FragmentInstanceState的儲存!這也是FragmentStatePagerAdapter的精髓之處,如果注釋掉,一旦Activity被回收進入異常銷毀狀态,Fragment就無法恢複之前的狀态,是以這種方法也是有纰漏和局限性的

3.避免重複建立View