简介
在开发的过程中,我们可能会遇到一个Activity
中ViewPager
与多个Fragment
组合使用的情况。ViewPager
有一个“预加载”机制,默认会把ViewPager
当前位置的左右相邻页面预先初始化(俗称预加载),它的默认值是1
,这样做的好处就是ViewPager
左右滑动会更加流畅。但是当有多个Fragment
且每个Fragment
都需要加载数据时可能就会导致页面卡顿,影响用户体验效果。此时我们希望在Fragment
可见时才去加载数据,这样不仅能够降低开销,同时也提高了用户的体验效果。Fragment
提供了setUserVisibleHint()
方法去判断当前Fragment
是否可见,实现在Fragment
可见时才进行数据加载操作,即Fragment
的懒加载。但是这样还会出现一个问题,就是每次可见时都会去重复加载数据,当然若需要的话可以每次都去加载,但是如果希望只是第一次可见时才去加载,那么还需要添加一些判断,下面我们封装一个基类来满足我们的需要。
基类Fragment
public abstract class BaseFragment extends Fragment {
protected View mRootView;
protected boolean isVisible = false; //是否可见
private boolean isPrepared = false;//是否初始化完成
private boolean isFirst = true;//是否第一次加载
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (mRootView == null) {
return;
}
if (getUserVisibleHint()) {
isVisible = true;
lazyLoad();
} else {
isVisible = false;
onInvisible();
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (mRootView == null) {
mRootView = initView(inflater, container, savedInstanceState);
}
return mRootView;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
isPrepared = true;
lazyLoad();
}
@Override
public void onResume() {
super.onResume();
if (getUserVisibleHint()) {
setUserVisibleHint(true);
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
((ViewGroup) mRootView.getParent()).removeView(mRootView);
}
@Override
public void onDestroy() {
super.onDestroy();
isVisible = false;
isPrepared = false;
isFirst = true;
mRootView = null;
}
/**
* 懒加载
*/
private void lazyLoad() {
if (!isPrepared || !isVisible || !isFirst) {
return;
}
initData();
isFirst = false;
}
/**
* 不可见时调用
*/
protected abstract void onInvisible();
/**
* 获取数据
*/
protected abstract void initData();
/**
* 初始化布局
*/
protected abstract View initView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState);
}
复用View
当和Fragment
关联的View Hierarchy
被移除时,不会执行onDestroy()
方法,而是会调用onDestroyView()
方法。比如在ViewPager
与多个Fragment
组合使用的时候,当ViewPager
滑动到第三页的时候,第一页的Fragment
就会执行onDestroyView()
,当再次滑动到第二页的时候,第一页的Fragment
中的onCreateView()
方法又会重新执行绘制页面。伴随而来的问题就是成员变量需要重新赋值,耗时加载出来的页面又要重新加载一次,这样不仅给内存增加了压力,而且用户体验也不太友好,尤其在有网络请求等开销时间比较长的情况下更是如此。
那对上面出现的问题有没有解决办法呢?答案是肯定的。onDestroyView()
的执行和Activity
的onDestroy()
不一样,它不会销毁当前的页面,所以Fragment
的所有成员变量的引用都还在。因此我们在onCreateView()
的时候,先判断获取到的数据是否为空,比如Fragment
的根视图mRootView
或者网络请求获取到的数据等,如果不为空就不用再次重新加载,这样一来也就避免了上述所说那些问题的存在了。
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (mRootView == null) {
mRootView = initView(inflater, container, savedInstanceState);
}
return mRootView;
}
但是需要注意的一点就是,如果重用mRootView
的话,一定要记得在onDestroyView()
里面将mRootView
先给移除掉,因为已经有过父布局的View
是不能再次添加到另一个新的父布局上面的,代码如下:
@Override
public void onDestroyView() {
super.onDestroyView();
((ViewGroup) mRootView.getParent()).removeView(mRootView);
}
项目地址 ☞ 传送门