具有懒加载功能的 Fragment基类封装和一般的Fragment基类封装

知识点须知

1,FragmentPagerAdapter, FragmentStatePagerAdapter 的区别

FragmentPagerAdapter 中, 即使fragment不可见了, 他的视图可能会 destory(执行 onViewDestory, 是否执行与setOffscreenPageLimit 方法设置的值有关), 但是他的实例仍然会存在于内存中. 当较多的fragment时, 会占用较大的内存.
FragmentSatePagerAdapter 中, 当fragment不可见时, 可能会将fragment的实例也销毁(执行 onDestory, 是否执行与setOffscreenPageLimit 方法设置的值有关). 所以内存开销会小些, 适合多fragment的情形.

2.setOffscreenPageLimit 方法分析与 Fragment生命周期分析

setOffscreenPageLimit 方法设置的默认值是1.这个设置的值有两层含义: 一是 ViewPager 会预加载几页; 二是 ViewPager 会缓存 2*n+1 页(n为设置的值).
明白了setOffscreenPageLimit 方法的含义后就明白了在 ViewPager 中Fragment的生命周期了:在 FragmentPagerAdapter 中 setOffscreenPageLimit 的值影响的是 onViewDestory 方法.当缓存的 fragment 超过 setOffscreenPageLimit 设置的值后, 那些 fragment 的onViewDestory 方法会回调; 在 FragmentStatePagerAdapter 中, 当缓存的 fragment 超过 setOffscreenPageLimit 设置的值后, 那些 fragment 的onDestory 方法会回调 .
总结起来就一句话: ViewPager 中的 fragment 是否执行 onViewDestory 或者 onDestory 与 setOffscreenPageLimit 方法设置的值有关.

3,懒加载实现思路

由于ViewPager 的setScreenPageLimit 方法默认值是 1 ,所以在 ViewPager 中至少会预加载一页. 当然了预加载页面是有好处的, 但是对于页面的数据, 那就没有必要去预加载了, 如果当前加载的页面有多个请求, 又预加载下个页面的请求, 导致多个任务等待, 这会影响到当前页面的请求速度. 所以合理的做法是当页面对于用户可见时再去请求数据.
可能有的同学会想到, 那就在 Fragment 的 onResume 去做请求数据的操作不就行了吗! 如果真是这样 easy, 那就没必要写这篇文章了~~ 因为有预加载, 下一页的 fragment 的 onStart, onResume 方法都会执行. 怎么办?
Fragment 里面有个方法 setUserVisibleHint(boolean isVisibleToUser) 方法. 这个方法可以判断当前页面是否对用户可见. 但是这个方法有点坑, 如果你直接在这个回调方法里面做请求, 那就坑你了,哈哈~~
setUserVisibleHint(boolean isVisibleToUser) 这个方法的回调不是和fragment 的生命周期方法同步的, 也就是说该方法的调用有可能会在Fragemnt的onCreateView()方法被调用之前调用,这样一来就会出现NullPointerException空指针异常。所以就需要满足控件初始化完成,用户可见,之后才能加载数据。

有了上面的思路我们封装基类如下:

public abstract class BaseLazyLoadFragment extends Fragment {

    private boolean isInitView = false;  // 是否完成了初始化的标识

    protected View view;
    private Unbinder bind;
    protected Activity mActivity;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.mActivity = getActivity();
    }

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

        view = inflater.inflate(setLayoutResourceID(),container,false);//让子类实现初始化视图
        bind = ButterKnife.bind(this, view);
        // 注册
        if (!EventBus.getDefault().isRegistered(this)) {
            EventBus.getDefault().register(this);
        }
        
        initView();//初始化事件
        isInitView = true;//视图创建完成,将变量置为true

        if (getUserVisibleHint()) {//如果Fragment可见进行数据加载
            onLazyLoad();
            isInitView = false;
        }
        return view;
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        bind.unbind(); // 解绑butterknife
        isInitView = false;//视图销毁将变量置为false
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 取消注册
        EventBus.getDefault().unregister(this);
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isInitView && isVisibleToUser) {//视图变为可见并且是第一次加载
            onLazyLoad();
            isInitView = false;
        }

    }
    //数据加载接口,留给子类实现
    public abstract void onLazyLoad();

    //初始化视图接口,子类必须实现
    public abstract int setLayoutResourceID();

    //初始化事件接口,留给子类实现
    public void initView(){ }

    /**
     * 默认绑定一个事件,防止源码里面去找方法的时候找不到报错。
     * @param fragment
     */ @Subscribe
    public void onEvent(BaseLazyLoadFragment fragment){

    }

}

关于懒加载的Fragment这里有两点需要指出:
1,setUserVisibleHint()方法只有在ViewPager+FragmentPagerAdapter+Fragment结合使用的时候才会被调用,单用Fragment的时候可以考虑使用onHiddenChanged(boolean hidden)方法,可以这么理解:只有用了FragmentPagerAdapter才会显示调用setUserVisibleHint()方法。
2,当Fragment的数量大于2个,你会发现等你翻到第三个,再重新返回第一个的时候,第一个又重新加载了,并且重新走了创建周期,这是因为ViewPager默认只会预加载下一页的Fragment,其他的Fragment会被移除并销毁,因此下一次再添加的时候就需要重新创建Fragment,那么如何解决这个问题呢?
viewPager.setOffscreenPageLimit(2);
这个方法可以设置ViewPager当前页左右两边预加载的页面数量,默认为1,不可能小于1,如果页数比较小(例如3到4个),可以加载所有的页面,这样可以减少页数创建的时间,滑动更流畅,当然了预加载页面数量设置少了,必然流畅度会越高。

一般的Fragment封装如下:

public abstract class BaseFragment extends Fragment {

    private View mRootView;
    protected Activity mActivity;
    private Unbinder bind;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.mActivity = getActivity();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        mRootView = inflater.inflate(getContentViewId(),container,false);
        bind = ButterKnife.bind(this, mRootView);
        if (!EventBus.getDefault().isRegistered(this)) {
            EventBus.getDefault().register(this);
        }
        initView();
        return mRootView;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        initData();
        setListener();
        super.onActivityCreated(savedInstanceState);
    }

    protected void initView(){}
    protected void initData(){}
    protected void setListener(){}
    protected abstract int getContentViewId();

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        bind.unbind(); // 解绑butterknife
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 取消注册
        EventBus.getDefault().unregister(this);
    }

    /**
     * 默认绑定一个事件,防止源码里面去找方法的时候找不到报错。
     * @param fragment
     */ @Subscribe
    public void onEvent(BaseFragment fragment){

    }

}

你可能感兴趣的:(安卓项目实战系列,Android)