ViewPager+Fragment 组合的预加载和懒加载
来源:互联网 发布:非主流签名 知乎 编辑:程序博客网 时间:2024/05/21 06:39
ViewPager+Fragment 组合的预加载和懒加载
转载自http://www.crocutax.com
预加载介绍
ViewPager+Fragment的搭配在日常开发中也比较常见,可用于切换展示不同类别的页面,我们日常所见的咨询、购物、金融、社交等类型的APP都有机会用到这种控件组合.
例如:
ViewPager控件有个特有的预加载机制,即默认情况下当前页面左右两侧的1个页面会被加载,以方便用户滑动切换到相邻的界面时,可以更加顺畅的显示出来.
通过ViewPager的setOffscreenPageLimit(int limit)
可以设置预加载页面数量,当前页面相邻的limit个页面会被预加载进内存.
效果如下:注意看Log输出
懒加载介绍
所谓的懒加载,其实也就是延迟加载,就是等到该页面的UI展示给用户时,再加载该页面的数据(从网络、数据库等),而不是依靠ViewPager预加载机制提前加载两三个,甚至更多页面的数据.这样可以提高所属Activity的初始化速度,也可以为用户节省流量.而这种懒加载的方式也已经/正在被诸多APP所采用.
但是通过ViewPager方法setOffscreenPageLimit(int limit)
的源码可以发现,ViewPager通过一定的逻辑判断来确保至少会预加载左右两侧相邻的1个页面,也就是说无法通过简单的配置做到懒加载的效果.
ViewPager方法setOffscreenPageLimit(int limit) 相关源码
//默认的缓存页面数量(常量)private static final int DEFAULT_OFFSCREEN_PAGES = 1;//缓存页面数量(变量)private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;public void setOffscreenPageLimit(int limit) { //当我们手动设置的limit数小于默认值1时,limit值会自动被赋值为默认值1(即DEFAULT_OFFSCREEN_PAGES) 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) { //经过前面的拦截判断后,将limit的值设置给mOffscreenPageLimit,用于 mOffscreenPageLimit = limit; populate(); }}
关于变量mOffscreenPageLimit到底是什么.可以从其get方法注释中略见端倪
/** * 返回空闲状态下的视图层级中,当前页面任何一侧保存的页面数量,默认是1 * Returns the number of pages that will be retained to either side of the * current page in the view hierarchy in an idle state. Defaults to 1. * * @return How many pages will be kept offscreen on either side * @see #setOffscreenPageLimit(int) */public int getOffscreenPageLimit() { return mOffscreenPageLimit;}
至于mOffscreenPageLimit到底是怎么影响ViewPager控件预加载的,暂不追查,因为此次的目的并不是ViewPager运行原理分析.
如何做到懒加载
既然通过ViewPager无法达到我们想要的懒加载效果,那么就得从Fragment自身入手了.
Fragment为我们提供了一个方法setUserVisibleHint(boolean isVisibleToUser)
,其中的参数isVisibleToUser
就是表示该Fragment的UI对于用户是否可见
Fragment的方法 setUserVisibleHint(boolean isVisibleToUser)
/** * Set a hint to the system about whether this fragment's UI is currently visible * to the user. This hint defaults to true and is persistent across fragment instance * state save and restore. * * <p>An app may set this to false to indicate that the fragment's UI is * scrolled out of visibility or is otherwise not directly visible to the user. * This may be used by the system to prioritize operations such as fragment lifecycle updates * or loader ordering behavior.</p> * * <p><strong>Note:</strong> This method may be called outside of the fragment lifecycle. * and thus has no ordering guarantees with regard to fragment lifecycle method calls.</p> * * @param isVisibleToUser true if this fragment's UI is currently visible to the user (default), * false if it is not. */public void setUserVisibleHint(boolean isVisibleToUser) { if (!mUserVisibleHint && isVisibleToUser && mState < STARTED && mFragmentManager != null && isAdded()) { mFragmentManager.performPendingDeferredStart(this); } mUserVisibleHint = isVisibleToUser; mDeferStart = mState < STARTED && !isVisibleToUser;}
大意就是通过此方法来设置Fragment的UI对用户是否可见,当该页面对用户可见/不可见时,系统都会回调此方法.
我们可以重写此方法,然后根据回调的isVisibleToUser
参数来进行相关的逻辑判断,以达到懒加载的效果,比如如果isVisibleToUser==true
的话表示当前Fragment对用户可见,此时再去加载页面数据.
由于ViewPager内会装载多个Fragment,而这种懒加载机制对于各个Fragment属于共同操作,因此适合将其抽取到BaseFragment中.
注意
setUserVisibleHint(boolean isVisibleToUser)
方法会多次回调,而且可能会在onCreateView()
方法执行完毕之前回调.如果isVisibleToUser==true,然后进行数据加载和控件数据填充,但是onCreateView()
方法并未执行完毕,此时就会出现NullPointerException空指针异常.
基于以上原因,我们进行数据懒加载的时机需要满足两个条件
onCreateView()
方法执行完毕setUserVisibleHint(boolean isVisibleToUser)
方法返回true
所以在BaseFragment中用两个布尔型标记来记录这两个条件的状态.只有同时满足了,才能加载数据
//Fragment的View加载完毕的标记private boolean isViewCreated;//Fragment对用户可见的标记private boolean isUIVisible;
第一步,改变
isViewCreated
标记
当onViewCreated()
方法执行时,表明View已经加载完毕,此时改变isViewCreated
标记为true,并调用lazyLoad()
方法
@Overridepublic void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); isViewCreated = true; lazyLoad();}
第二步,改变
isUIVisible
标记
当setUserVisibleHint(boolean isVisibleToUser)
回调为true时,改变isUIVisible
标记为true,并调用lazyLoad()
方法
@Overridepublic void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); //isVisibleToUser这个boolean值表示:该Fragment的UI 用户是否可见 if (isVisibleToUser) { isUIVisible = true; lazyLoad(); } else { isUIVisible = false; }}
第三步: 在lazyLoad()方法中进行双重标记判断,通过后即可进行数据加载
private void lazyLoad() { //这里进行双重标记判断,是因为setUserVisibleHint会多次回调,并且会在onCreateView执行前回调,必须确保onCreateView加载完毕且页面可见,才加载数据 if (isViewCreated && isUIVisible) { loadData(); //数据加载完毕,恢复标记,防止重复加载 isViewCreated = false; isUIVisible = false; printLog(mTextviewContent+"可见,加载数据"); }}
第四步:定义抽象方法
loadData()
,具体加载数据的工作,交给子类去完成
protected abstract void loadData();
注意: 数据加载完毕要恢复标记,防止数据重复加载
效果如下:
Demo源码
Github源码
- ViewPager+Fragment组合的预加载和懒加载
- ViewPager+Fragment组合的预加载和懒加载
- ViewPager+Fragment 组合的预加载和懒加载
- ViewPager+Fragment组合的预加载和懒加载
- viewpager+fragment的懒加载
- fragment 懒加载 viewpager 取消预加载
- Fragment+Viewpager 取消懒加载/预加载
- 关于Fragment+viewpager的取消预加载(懒加载)
- fragment 与viewpager预加载的解决 懒加载
- Fragment+Viewpager的预加载与懒加载
- ViewPager加载Fragment懒加载
- viewpager fragment 预加载
- ViewPager+Fragment懒加载
- Viewpager Fragment懒加载
- ViewPager+Fragment懒加载
- ViewPager+Fragment懒加载
- viewPager fragment 懒加载
- fragment viewpager 懒加载
- .net中excel程序导入数据库
- ARM多核引导过程
- Vue instance lifecycle
- 【Spark的那些事儿】—你眼中的RDD
- 增强学习的初步理解- 周志华《机器学习》
- ViewPager+Fragment 组合的预加载和懒加载
- the road of IELTS
- 根据天数 ,返回孕妈多少周 格式 14周+3天
- Hello_World
- COCOS2dX
- Sync服务发布
- ESXI6.5-coentOS6.5-共享磁盘创建
- 模型保存(持久化)
- 学习网站收录随时更新