ViewPager场景下对可见Fragment进行加载

464 阅读3分钟

说在前面:

在实际开发中,我们常常会有使用ViewPager+Fragment来实现滑动切换的需求,但是ViewPager在默认情况下是会对当前Fragment相邻单位为1 的其它Fragment进行预加载。有时候我们的需要可能不需要预加载另外两个界面,那我们就有了懒加载这个需求。(这里不对懒加载和预加载进行好坏的区分,因为在不同业务有不同的需求)

了解过ViewPager的同学们可能会知道 ViewPager有自己的设置缓存数量的方法setOffscreenPageLimit 。但是这个方法有个问题就是,他设置的缓存数量不能少于1个,当少于一个的时候会默认为1个。(这里避免篇幅太大就不贴代码了),既然ViewPager改变不了,那我们就改变Fragment!!

想要实现懒加载的需求,我们可能先准备一下Fragment在ViewPager中生命周期的调用情况,以及对setUserVisibleHint方法的了解。

Fragment在ViewPager中的生命周期 与 setUserVisibleHint

先说一下setUserVisibleHint方法吧

这个方法是在Fragment在onAttach之前就会被调用的,看名字也能猜到是用来设置对用户可见性的。

我们可以利用getUserVisibleHint()获得当前可见性,这个在后面我们实现懒加载中是比较重要的一个点。

然后就是Fragment在ViewPager中的生命周期的调用

假设有两个Fragment f0 和 f1,ViewPager默认预加载缓存为1;

那么这两个Fragment的生命周期将会是交错的,它们并不会说先加载完f0 再去缓存f1,而是交错回调,而且都会走到onResume,唯一的区别在于f0的getUserVisibleHint() 为true(f0为可见),而f1为false。并且当我们滑到f1时,也只会执行setUserVisibleHint方法

上面这个也是为什么我们能简单的通过判断生命周期来进行懒加载。

具体的生命周期日志可以自己去打印一下,加深印象。

开始我们的实现

先明确我们的需求:只有滑到该Fragment时才去加载数据

public class LazyloadFragment extends baseFragment {
    private boolean isViewPrepared; // 标识fragment视图已经初始化完毕
    private boolean hasFetchData; // 标识已经触发过懒加载数据

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        lazyFetchDataIfPrepared(); //经过了预加载页面, 然后展示 
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        isViewPrepared = true; //标识View加载完成
        lazyFetchDataIfPrepared(); //当首次进入到ViewPager指定的首页时,该首页并没有预加载。所以现在得再次调用
    }
    //需要注意的是 onViewcreated方法并不在生命周期中,只是在onCreateView执行后会被调用

    /**
     * 懒加载方法,获取数据什么的放到这边来使用,在切换到这个界面时才进行网络请求
     */
    private void lazyFetchDataIfPrepared() {
        // 用户可见fragment && 没有加载过数据 && 视图已经准备完毕
        if (getUserVisibleHint() && !hasFetchData && isViewPrepared) {
            hasFetchData = true; //已加载过数据
            lazyInit();
        }
    }

    /**
     * 执行需要懒加载的方法
     * 子类必须实现
     */
    abstract void lazyInit() {
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        hasFetchData = false;
        isViewPrepared = false;
    }
}

这里我们为什么要标识View是否加载呢? 是因为在onCreateView方法被调用之前setUserVisibleHint方法就被调用了,如果这里不进一步判断View是否加载完成,那就会报Null。

为什么要在onViewCreated再调用一次呢? 这是因为ViewPager首页首次进入并没有预加载,view加载完成标识为flase,在setUserVisibleHint方法中就不会加载数据了,所以要再次调用。

那不会造成重复获取数据吗?我们这里考虑到了,所以使用了数据加载完成标识符,只有加载符为false才会成功调用。

总得来说数据加载必须满足三点 :fragment可见、view加载完成、数据未加载。