前言
我们在做安卓项目的时候,经常会有一个使用场景:ViewPage与多个Fragment组合使用。 然而,viewpager有着预加载机制:默认一次加载当前页面前后两个页面,即使设置setOffLimit(0)也没有效果。 虽然预加载优化了app的体验效果,但是这样把我们看不到的页面的数据也加载了,降低了性能,浪费初始化资源。这时我们就需要用到 “懒加载”。
Fragment的生命周期
为了方便了解首先看一下Fragment额生命周期。
实现思路
1.Fragment中有一个叫 setUserVisibleHint(boolean isVisibleToUser) 的方法,此方法会在onCreateView()之前执行,当viewPager中fragment改变可见状态时也会调用,当fragment 从可见到不见,或者从不可见切换到可见,都会调用此方法,使用getUserVisibleHint() 可以返回fragment是否可见状态。
2.但是直接根据 isVisibleToUser 判断就加载数据,可能onCreateView()方法并未执行完毕,此时就会出现NullPointerException空指针异常。所以就需要满足控件初始化完成,用户可见,才能加载数据。
3.添加 isPrepared 参数。在系统调用onActivityCreated时设置为true,这时onCreateView方法已调用完毕(一般我们在这方法里执行findviewbyid等方法),确保 lazyInitData()方法不会报空指针异常。
4.添加 isInitData 参数。确保ViewPager来回切换时BaseFragment的initData方法不会被重复调用,lazyInitData在该Fragment的整个生命周期只调用一次,第一次调用 lazyInitData() 方法后马上执行 isInitData= true。
5.getUserVisibleHint()会返回是否可见状态,这是fragment实现懒加载的关键,只有fragment 可见才会调用onLazyLoad() 加载数据。
代码示例
//定义一个懒加载的基类
public abstract class BaseFragment extends Fragment {
protected View rootView;
private boolean isInitData = false; /*标志位 判断数据是否初始化*/
private boolean isPrepareView = false; /*标志位 判断view已经加载完成 避免空指针操作*/
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Log.i("TAG","==onActivityCreated");
isPrepareView = true;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
rootView = inflater.inflate(getLayoutInt(), container, false);
// Log.i("TAG","==onCreateView");
return rootView;
}
//判断fragment是否可见
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
Log.i("TAG","==setUserVisibleHint="+getUserVisibleHint());
if (!isInitData && isVisibleToUser && isPrepareView) {
lazyInitData();
}
}
/*懒加载方法*/
private void lazyInitData() {
if (!isInitData && isVisibleToUser && isPrepareView) {
isInitData = true;
initData();
}
}
//初始化数据,数据加载等等。
public abstract void initData();
//加载布局
public abstract int getLayoutInt();
}
在fragment中的使用
public class MyFragment extends BaseFragment {
private ListView firstList;
private MFAdatper mfAdatper;
@Override
public void initData() {
//控件初始化
firstList = rootView.findViewById(R.id.firstList);
//自己要做的事情(我这里写了一个方法 用于 下载 图片)
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.qubaobei.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
FoodInterFace face = retrofit.create(FoodInterFace.class);
HashMap<String,String>map = new HashMap<>();
map.put("stage_id","1");
map.put("limit","20");
map.put("page","1");
Observable<Food> foodInfo = face.getFoodInfo(map);
foodInfo.
subscribeOn(Schedulers.io()).
observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Food>() {
@Override
public void accept(@NonNull Food food) throws Exception {
List<Food.DataBean> data = food.getData();
Log.i("TAG","==="+data.size());
mfAdatper = new MFAdatper(getContext(),data);
firstList.setAdapter(mfAdatper);
}
});
}
//===============================================================================================
//加载布局
@Override
public int getLayoutInt() {
return R.layout.list_layout;
}
}
总结
一个简单的fragment的懒加载 demo就完成了(博主的文笔比较烂,将就看吧)。最后盗张图。