Android開發(fā)之Viewpager+Fragment實現(xiàn)懶加載

網(wǎng)上的懶加載分析文章已經(jīng)很多,這里也給出我自己的分析思路。

1 為什么要實現(xiàn)懶加載?原因是默認(rèn)情況下ViewPager會去預(yù)加載前后各一頁的內(nèi)容。預(yù)加載會依次調(diào)用Fragment的生命周期方法 onAttach(),onCreate(),onCreateView(),onViewCreated(),onActivityCreated(),onStart(),onResume(),此時已經(jīng)完成網(wǎng)絡(luò)的請求了(一般而言我們會把請求網(wǎng)絡(luò)的接口放在onViewCreated中),結(jié)果就是浪費用戶的流量。

2 何謂懶加載?懶加載即是當(dāng)Fragment可見時才去加載數(shù)據(jù)。對于普通的fragment,參考activity,當(dāng)onStart被調(diào)用時其界面應(yīng)該就可見了。但是當(dāng)Viewpager+Fragment配合使用時,根據(jù)上面的內(nèi)容,此時Viewpager相鄰的Fragment是不可見的,但是onStart已經(jīng)調(diào)用了。顯然,在Viewpager+Fragment配合使用時,無法通過onStart是否調(diào)用判斷Fragment是否可見。針對這種情況,谷歌提供了一個方法 public void setUserVisibleHint(boolean isVisibleToUser) 用于判斷Viewpager+Fragment配合使用時,fragment是否可見。參數(shù)isVisibleToUser為true時可見,為false時不可見。

3 懶加載的難點其實主要是各種標(biāo)志位的意義

 private void lazyLoad() {
        if(getUserVisibleHint() && isViewPrepared && !hasLoadData){
            hasLoadData = true;
            initdata();
        }
    }

標(biāo)志位有3個:
getUserVisibleHint : 表示界面是否可見
isViewPrepared ?。骸”硎窘缑媸欠褚呀?jīng)準(zhǔn)備好
hasLoadData?。骸”硎臼欠褚呀?jīng)加載過數(shù)據(jù)。
只有當(dāng)界面可見,界面ui已經(jīng)準(zhǔn)備好,且沒有加載過數(shù)據(jù)的情況下,才調(diào)用initData()加載數(shù)據(jù)。

3.1 isViewPrepared
首先我們會想到,既然setUserVisiableHint() 能夠判斷界面是否可見,那直接在setUserVisiableHint() 進行判斷,當(dāng)界面可見時,即isVisibleToUser為true去加載數(shù)據(jù)不就行了嗎?答案是不行。理由是setUserVisiableHint() 會在生命周期方法調(diào)用前調(diào)用。以下是打印的log:
05-24 10:42:12.819 15245-15245/com.lyd.lazyload I/FRAGMENT: Tab1 isVisibleToUser :false
05-24 10:42:12.819 15245-15245/com.lyd.lazyload I/FRAGMENT: Tab2 isVisibleToUser :false
05-24 10:42:12.820 15245-15245/com.lyd.lazyload I/FRAGMENT: Tab1 isVisibleToUser :true
05-24 10:42:12.821 15245-15245/com.lyd.lazyload I/FRAGMENT: Tab1 onAttach
05-24 10:42:12.821 15245-15245/com.lyd.lazyload I/FRAGMENT: Tab1 onCreate
05-24 10:42:12.821 15245-15245/com.lyd.lazyload I/FRAGMENT: Tab2 onAttach
05-24 10:42:12.821 15245-15245/com.lyd.lazyload I/FRAGMENT: Tab2 onCreate
05-24 10:42:12.822 15245-15245/com.lyd.lazyload I/FRAGMENT: Tab1 onCreateView
05-24 10:42:12.825 15245-15245/com.lyd.lazyload I/FRAGMENT: Tab1 onViewCreated
05-24 10:42:12.826 15245-15245/com.lyd.lazyload I/FRAGMENT: Tab1 onActivityCreated
05-24 10:42:12.826 15245-15245/com.lyd.lazyload I/FRAGMENT: Tab1 onStart
05-24 10:42:12.826 15245-15245/com.lyd.lazyload I/FRAGMENT: Tab1 onResume
05-24 10:42:12.826 15245-15245/com.lyd.lazyload I/FRAGMENT: Tab2 onCreateView
05-24 10:42:12.829 15245-15245/com.lyd.lazyload I/FRAGMENT: Tab2 onViewCreated
05-24 10:42:12.829 15245-15245/com.lyd.lazyload I/FRAGMENT: Tab2 onActivityCreated
05-24 10:42:12.829 15245-15245/com.lyd.lazyload I/FRAGMENT: Tab2 onStart
05-24 10:42:12.829 15245-15245/com.lyd.lazyload I/FRAGMENT: Tab2 onResume

那么為什么setUserVisiableHint() 在生命周期前調(diào)用就不能在里面去加載數(shù)據(jù)呢?聯(lián)系我們具體項目,一般而言拉取數(shù)據(jù)后會去更新ui控件,那ui控件的實例從哪里獲得?

 @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.i(TAG , mParam1 + "onCreateView");
        View view = inflater.inflate(R.layout.fragment_my, container, false);
        tv = (TextView) view.findViewById(R.id.tv);
        return view;
    }

如上所示,在onCreateView中通過View.findViewById獲取ui控件的實例,之后才能更新ui,所以在setUserVisiableHint()里面去拉取數(shù)據(jù)可以,但是不能更新ui,否則會報空指針異常。
那么這時候我們就可以在增加標(biāo)志位isViewPrepared,表示界面是否已經(jīng)加載完成,在onCreateView() 中View view = inflater.inflate(R.layout.fragment_my, container, false)后設(shè)置為true即可,這里我把它放在onViewCreated()這個方法中,由方法名可以知道,此時View已經(jīng)創(chuàng)建完成。

3.2 getUserVisibleHint
界面可見時返回true,不可見時返回false

3.3 hasLoadData
其實這里主要有3種情景需要考慮:
假設(shè)ViewPager共有3個頁面,分別對應(yīng)Fragment1,fragment2,fragment3.
1一開始進來,fragment1可見,Viewpager會去預(yù)加載fragment2,此時fragment2要不要去加載數(shù)據(jù)?當(dāng)然是不去加載,只有Fragment可見時才去加載數(shù)據(jù)。
2由fragment1滑到fragment2,然后再回到fragment1,此時fragment1需不需要再次去加載數(shù)據(jù)?需不需要加載數(shù)據(jù)主要是看滑動Fragment2時,fragment1有沒有被銷毀?通過log我們可以知道,當(dāng)滑到Fragment時,并沒有觸發(fā)Fragment1的任何生命周期方法,所以此時Fragment1頁面沒有銷毀,不需要去加載數(shù)據(jù)。此時hasLoadData為true,所以initdata()不執(zhí)行。
3由fragment滑到fragment2,在滑到Fragment3,然后再回到Fragment1.此時Fragment1需不需要去加載數(shù)據(jù)?
當(dāng)滑到Fragment3時,log如下:
05-24 15:15:32.139 12422-12422/com.lyd.lazyload I/FRAGMENT: Tab1 onPause
05-24 15:15:32.139 12422-12422/com.lyd.lazyload I/FRAGMENT: Tab1 onStop
05-24 15:15:32.140 12422-12422/com.lyd.lazyload I/FRAGMENT: Tab1 onDestroyView
此時Fragment1界面被銷毀,回到Fragment1時需要重新加載數(shù)據(jù)。需要特別注意的是,當(dāng)滑到Fragment3時,fragment1并沒有調(diào)用onDestory方法,也即是沒有銷毀Fragment1的實例,只是銷毀它的界面。所以需要onDestroyView做出判斷:

 @Override
    public void onDestroyView() {
        super.onDestroyView();
        Log.i(TAG , mParam1 + "onDestroyView");
        hasLoadData = false;
        isViewPrepared = false;

    }

4 完整代碼:

public class MyFragment extends Fragment {
    public static final String TAG = "FRAGMENT";

    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";
    private String mParam1;
    private String mParam2;
    private boolean hasLoadData = false;//是否已經(jīng)加載過數(shù)據(jù)
    private boolean isViewPrepared = false;//控件是否已經(jīng)準(zhǔn)備好
    private TextView tv;

    public MyFragment() {
        // Required empty public constructor
    }

    public static MyFragment newInstance(String param1, String param2) {
        MyFragment fragment = new MyFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(TAG , mParam1 + "onCreate");
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        Log.i(TAG , mParam1 + "onCreateView");
        View view = inflater.inflate(R.layout.fragment_my, container, false);
         tv = (TextView) view.findViewById(R.id.tv);

        return view;
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        Log.i(TAG , mParam1 + "isVisibleToUser :" + isVisibleToUser);
        if(isVisibleToUser){
            lazyLoad();
        }

    }

    private void lazyLoad() {
        if(getUserVisibleHint() && isViewPrepared && !hasLoadData){
            hasLoadData = true;
            initView();
        }
    }

    private void initView() {
        Log.i(TAG , mParam1 + "initdata");
        tv.setText(mParam1);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Log.i(TAG , mParam1 + "onViewCreated");
        isViewPrepared = true;
        lazyLoad();

    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        Log.i(TAG , mParam1 + "onDestroyView");
        hasLoadData = false;
        isViewPrepared = false;

    }

}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容