懒加载,就是滑动Viewpager的过程中,当fragment显示的时候才去加载数据,但是由于ViewPager的预加载机制,会提前初始化左右两边的fragment,那么,要想实现懒加载,就需要一些骚操作了
骚一、既然Viewpager有个setOffscreenPageLimit(int limit)方法,那么是不是我们就可以直接将里面的参数设置为0,让他不预加载页面不就可以嘿嘿嘿了,我只想说,小伙子,你的思想很危险啊,看源码
public void setOffscreenPageLimit(int limit) { if (limit < DEFAULT_OFFSCREEN_PAGES) { Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " + DEFAULT_OFFSCREEN_PAGES); limit = DEFAULT_OFFSCREEN_PAGES; } if (limit != mOffscreenPageLimit) { mOffscreenPageLimit = limit; populate(); } } DEFAULT_OFFSCREEN_PAGES//这个的默认值是1也就是说最少预加载一个页面,次操作失败骚二、既然不能控制Viewpager的预加载,那么只能另辟蹊径,还有另外一个函数,setUserVisibleHint(boolean isVisibleToUser);,这个就是fragment是否可见的回调函数,但是,经过测试,这个函数也有玄机,这个函数的调用时机是两个:一个是fragment初始化之前会被调用,那个时候view还没被创建出来,一个是fragment从可见到不可见和由不可见到可见的时候会被调用,这个时候就需要做一些判断了,好了,先看代码吧
这次封装的支持以下的功能:
第一点应该是比较需要且常用的一点,之前那篇博客里没有考虑到这点应用场景是我的疏忽。稍微讲解一下,有些时候,我们打开一个 Fragment 页面时,希望它是在可见时才去加载数据,也就是不要在后台就开始加载数据,而且,我们也希望加载数据的操作只是第一次打开该 Fragment 时才进行的操作,以后如果再重新打开该 Fragment 的话,就不要再重复的去加载数据了。
具体点说,Fragment 和 ViewPager 一起用时,由于 ViewPager 的缓存机制,在打开一个 Fragment 时,它旁边的几个 Fragment 其实也已经被创建了,如果我们是在 Fragment 的 onCreat() 或者 onCreateView() 里去跟服务器交互,下载界面数据,那么这时这些已经被创建的 Fragment,就都会出现在后台下载数据的情况了。所以我们通常需要在 setUserVisibleHint() 里去判断当前 Fragment 是否可见,可见时再去下载数据,但是这样还是会出现一个问题,就是每次可见时都会重复去下载数据,我们希望的是只有第一次可见时才需要去下载,那么就还需要再做一些判断。这就是要封装个基类来做这些事了,具体代码见后面。
即使我们在 setUserVisibleHint() 做了很多判断,实现了可见时加载并且只有第一次可见时才加载,可能还是会遇到其他问题。比如说,我下载完数据就直接需要对 ui 进行操作,将数据展示出来,但有时却报了 ui 控件 null 异常,这是因为 setUserVisibleHint() 有可能在 onCreateView() 创建 view 之前调用,而且数据加载时间很短,这就可能出现 null 异常了,那么我们还需要再去做些判断,保证在数据下载完后 ui 控件已经创建完成。
除了懒加载,只加载一次的需求外,可能我们还需要每次 Fragment 的打开或关闭时显示数据加载进度。对吧,我们打开一个 Fragment 时,如果数据还没下载完,那么应该给个下载进度或者加载框提示,如果这个时候打开了新的 Fragment 页面,然后又重新返回时,如果数据还没加载完,那么也还应该继续给提示,对吧。这就需要有个 Fragment 可见与不可见时触发的回调方法,并且该方法还得保证是在 view 创建完后才触发的,这样才能支持对 ui 进行操作。
以上,就是我们封装的 BaseFragment 基类要干的活了。下面上代码。
使用很简单,新建你需要的 Fragment 类继承自该 BaseFragment,然后重写两个回调方法,根据你的需要在回调方法里进行相应的操作比如下载数据等即可。 例如:
public class CategoryFragment extends BaseFragment { private static final String TAG = CategoryFragment.class.getSimpleName(); @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_category, container, false); initView(view); return view; } @Override protected void onFragmentVisibleChange(boolean isVisible) { if (isVisible) { //更新界面数据,如果数据还在下载中,就显示加载框 notifyDataSetChanged(); if (mRefreshState == STATE_REFRESHING) { mRefreshListener.onRefreshing(); } } else { //关闭加载框 mRefreshListener.onRefreshFinish(); } } @Override protected void onFragmentFirstVisible() { //去服务器下载数据 mRefreshState = STATE_REFRESHING; mCategoryController.loadBaseData(); } }