Fragment懒加载实现

作者: 沐小木沐 | 来源:发表于2019-03-08 17:14 被阅读34次

昨天写代码的时候发现项目里面的订单页面重新载入数据的时候会出现多次添加,检查了下代码才发现原来是懒加载出的问题。本来准备偷一下懒的,但是网上一直没找到满意的(p.s 主要还是我自个有点强迫症 (。・ω・。) ),所以还是自己写一个备忘吧。如果只是需要代码的话,可以滑动到最下面copy一下,忽略我的思路。


一. Fragment懒加载定义

说到Fragment懒加载,顾名思义就是让Fragment在不显示的时候不预加载数据。可能初学者会不理解,“Fragment不显示?难道是创建的时候?”,不是的,Fragment的显示隐藏状态是Fragment创建之后的一个状态,这个状态根据片段是否调用了setUserVisibleHint()方法而变化。通过查询源码,我们可以发现他是PagerAdapter对象调用的。这意味着只有使用PagerAdapter或者它的子类作为适配器的ViewPager才会触发这个方法。那么Fragment懒加载也只是针对ViewPager了,而不能用到add()hide()方法里。

image

二. Fragment懒加载的思路

通过懒加载的定义,大概可以了解到实现懒加载的关键在setUserVisibleHint()方法,那么是不是可以在这个方法里面直接判断是否显示然后调用数据加载呢?但是调用日志打印,却发现ViewPager加载的Fragment一开始就调用了setUserVisibleHint()方法,而且还是调用了多次。

E/打印: pagerNum = 0 setUserVisibleHint() + false
E/打印: pagerNum = 0 setUserVisibleHint() + false
E/打印: pagerNum = 0 setUserVisibleHint() + true
E/打印: pagerNum = 0 onCreate pagerNum = 0 onCreate()
E/打印: pagerNum = 0 onCreateView()
E/打印: pagerNum = 0 onActivityCreated()

那么,从打印出来的日志看,ViewPager + Fragment运行后,Fragment先运行了setUserVisibleHint(),然后再走了视图创建。那么上面说的在这个方法里面直接判断并懒加载是会出问题的。那么就做个变量判断,如果显示了并且视图创建了,那么就懒加载。


@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (getUserVisibleHint()) {
        isVisible = true;
        if (isPrepared && isVisible) {
            lazyLoad();
        }
    } else {
        isVisible = false;
        onInvisibleLoad();
    }
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    isPrepared = true;
    if (isPrepared && isVisible) {
        lazyLoad();
    }
}

public abstract void lazyLoad() ;

但是,这样真就可以了嘛?我试了一下多个Fragment作为ViewPager的嵌套。

image

E/打印: pagerNum = 0 setUserVisibleHint() isVisibleToUser = false
E/打印: pagerNum = 0 setUserVisibleHint() isVisibleToUser = false
E/打印: pagerNum = 0 setUserVisibleHint() isVisibleToUser = true
E/打印: pagerNum = 0 onActivityCreated() time = 1552032454259
E/打印: pagerNum = 0 lazyLoad()
E/打印: pagerNum = 1 onActivityCreated() time = 1552032454277
E/打印: pagerNum = 0 setUserVisibleHint() isVisibleToUser = false
E/打印: pagerNum = 0 setUserVisibleHint() isVisibleToUser = false
E/打印: pagerNum = 0 setUserVisibleHint() isVisibleToUser = true
E/打印: pagerNum = 4 onActivityCreated() time = 1552032460733
E/打印: pagerNum = 4 lazyLoad()
E/打印: pagerNum = 3 onActivityCreated() time = 1552032460742
E/打印: pagerNum = 5 onActivityCreated() time = 1552032460750
E/打印: pagerNum = 0 setUserVisibleHint() isVisibleToUser = false
E/打印: pagerNum = 1 setUserVisibleHint() isVisibleToUser = false
E/打印: pagerNum = 4 setUserVisibleHint() isVisibleToUser = false
E/打印: pagerNum = 0 setUserVisibleHint() isVisibleToUser = true
E/打印: pagerNum = 0 lazyLoad()
E/打印: pagerNum = 0 onActivityCreated() time = 1552032470357
E/打印: pagerNum = 0 lazyLoad()
E/打印: pagerNum = 1 onActivityCreated() time = 1552032470359

然后就发现了,点击了标题4或者5,再重新点回标题1的时候,lazyLoad()加载了两次。这就是为什么我昨天遇到的,明明清空了数据,结果,他跨格切页会重复添加数据。因为onCreateView()方法的多次请求,即使调用了清空数据,但是由于网络请求的延时,造成数据的重复添加。

(懒得画图了,这里贴上大神制作的流程图,上面是关于多ViewPager嵌套的。)

image image

然后,从算法逻辑考虑,这是懒加载它不够“懒”造成的。所以改进一下,并且按照MVP原则,可以写成一个基本懒加载片段类。


import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

/**
* ========================================
*
* @author ydw
* Create on 2019/3/7
* 描述: 懒加载基本页面 
* =========================================
*/

public abstract class BaseLazyFragment extends Fragment {

    protected boolean isVisible;
    private boolean isPrepared = false, isInitial = false ;
    private View mRootView;
    public FragmentActivity act;
    public BasicFragment fragment = this;
    private boolean isFirst = true;

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

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        act = getActivity();
        setHasOptionsMenu(true); //设置片段可以使用菜单栏
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (getUserVisibleHint()) {
            isVisible = true;
            lazyLoad();
        } else {
            isVisible = false;
            onInvisibleLoad();
        }
    }

    @Override
    public void onActivityCreated(@Nullable Bundle 
 savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (!isInitial) {
            initData();
            isInitial = true;
        }

        if (!isPrepared) {
            isPrepared = true;
            lazyLoad();
        }
    }

    private void lazyLoad() {
        if (!isPrepared || !isVisible) {
            return;
        }

        if (isFirst) {
            isFirst = false;
            onVisibleLoad(true);

        } else {
            onVisibleLoad(false);
        }
    }

    //页面显示的时候进行动作
    public void onVisibleLoad(boolean isFirst){
        //do something
    }

    //页面隐藏的时候进行动作
    public void onInvisibleLoad() {
        //do something
    }

    public boolean isFirst() {
        return isFirst;
    }

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        if (mRootView == null) {
            mRootView = initView(inflater, container, savedInstanceState);
        }

         return mRootView;
    }

    public View getRootView() {
        return mRootView;
    }

    public abstract View initView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState);

    public abstract void initData();

}

(p.s 本来这个页面没这么多,我是拆分成很多个基本类的,但是这里为了方便查看,就都放一起了。)

写到这里突然发现,今天居然是个节日哈,那顺便祝福各位女王节日快乐~


每多走一步,就与梦想更近一步。

相关文章

网友评论

    本文标题:Fragment懒加载实现

    本文链接:https://www.haomeiwen.com/subject/entdpqtx.html