存在的问题
- 默认情况下,ViewPager会自动预加载
- 切换过程中会销毁fragment视图
下面的图说明情况
如图,在Activity中使用ViewPager嵌套三个Fragment,当fragment1出现的时候,此时会预加载fragment2,滑动到fragment2会预加载fragment3,但是滑动到fragment3,此时会调用fragment1的destroyview方法,销毁视图。当重新滑动到fragment1才会重新调用fragment1的oncreateview方法。注意此时并不会销毁实例,不会调用ondestroy方法。
这样就存在两个问题
1、pagerview频繁切换,导致fragment1.fragment3在频繁的调用destroyview和oncreateview方法,重新创建视图。这样也浪费了大量的资源,用户体验不佳,虽然内存消耗比较低
2、因为切换到fragment1的时候,同时预加载了fragment2,如果此时fragment2也有大量的耗时网络请求要做,如果应用对启动反应速度比较敏感,所以此时做了多余的工作。或者说fragment2也会显示加载进度,显然不符合我们的预期,能否把这些耗时的工作延迟加载,也是个问题
防止频繁的销毁视图的解决方案
1、setOffscreenPageLimit(2)
2、或者重写PagerAdaper的destroyItem方法为空即可
笔者此处使用的是方案2
public class MyViewPagerAdapter extends FragmentPagerAdapter {
...
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
//如果注释这行,那么不管怎么切换,page都不会被销毁
//super.destroyItem(container, position, object);
}
}
取消预加载解决方案
介绍两个方法void setUserVisibleHint(boolean isVisibleToUser)、boolean getUserVisibleHint(),它们分别用作设置/获得Fragment可见状态,我们可以重写Fragment在其中做判断。
第一步:重写Fragment
public abstract class BaseFragment extends Fragment{
/** Fragment当前状态是否可见 */
protected boolean isVisible;
public void onCreate(android.os.Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(getUserVisibleHint()) {
isVisible = true;
onVisible();
} else {
isVisible = false;
onInvisible();
}
}
/**
* 可见
*/
protected void onVisible() {
lazyLoad();
}
/**
* 不可见
*/
protected void onInvisible() {
}
/**
* 延迟加载
* 子类必须重写此方法
*/
protected abstract void lazyLoad();
}
第二步:继承BaseFragment
1、声明二个重要的变量
private boolean mHasLoadedOnce = false;
private boolean isPrepared = false;
2、onCreateView中进行控件初始化,初始化完成后加载数据
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_layuot2, null);
Dbug.d("","Fragment2==onCreateView");
initData();
isPrepared = true;
lazyLoad();
return view;
}
此处为什么要用isPrepared呢,因为lazyLoad();方法调用的时候控件可能没初始化完成,会报空指针错误。
3、lazyLoad中判断是否是首次加载和初始化完成
@Override
protected void lazyLoad() {
if (mHasLoadedOnce || !isPrepared)
return;
mHasLoadedOnce = true;
}
4、onDestroyView中变量重置
@Override
public void onDestroyView() {
super.onDestroyView();
Dbug.d("","Fragment2==onDestroyView");
mHasLoadedOnce = false;
isPrepared = false;
}