ViewPager+Fragment 组合的预加载和懒加载

转载自http://www.crocutax.com

预加载介绍

ViewPager+Fragment的搭配在日常开发中也比较常见,可用于切换展示不同类别的页面,我们日常所见的咨询、购物、金融、社交等类型的APP都有机会用到这种控件组合.

例如:

今日头条APP

ViewPager控件有个特有的预加载机制,即默认情况下当前页面左右两侧的1个页面会被加载,以方便用户滑动切换到相邻的界面时,可以更加顺畅的显示出来.

通过ViewPager的setOffscreenPageLimit(int limit)可以设置预加载页面数量,当前页面相邻的limit个页面会被预加载进内存.

效果如下:注意看Log输出

viewpager预加载2页

懒加载介绍

所谓的懒加载,其实也就是延迟加载,就是等到该页面的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空指针异常.

基于以上原因,我们进行数据懒加载的时机需要满足两个条件

  1. onCreateView()方法执行完毕
  2. setUserVisibleHint(boolean isVisibleToUser)方法返回true

所以在BaseFragment中用两个布尔型标记来记录这两个条件的状态.只有同时满足了,才能加载数据

//Fragment的View加载完毕的标记
private boolean isViewCreated;//Fragment对用户可见的标记
private boolean isUIVisible;

第一步,改变isViewCreated标记

onViewCreated()方法执行时,表明View已经加载完毕,此时改变isViewCreated标记为true,并调用lazyLoad()方法

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);isViewCreated = true;lazyLoad();
}

第二步,改变isUIVisible标记

setUserVisibleHint(boolean isVisibleToUser)回调为true时,改变isUIVisible标记为true,并调用lazyLoad()方法

@Override
public 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 组合的预加载和懒加载相关推荐

  1. spring预加载与懒加载_通过Spring将继承树加载到List中

    spring预加载与懒加载 我注意到有趣的Spring功能. 我的一位同事使用它将Spring Bean的整个继承树加载到列表中. 在学习Spring文档时错过了这一点. 让我们来看看Spring b ...

  2. 前端项目分析:我是如何做图片优化的(预加载、懒加载和延迟加载)

    众所周知:前端页面上的图片是优化时最重要也是最令人头疼的部分,花费了几个月的时间才优化到令自己满意的一半程度,,,唉,一言难尽啊! 在此将几种方法总结一下,希望能帮到不少人吧- 图片的优化有两种方式: ...

  3. css 同步加载,同步加载,异步加载,懒加载,预加载

    同步加载 默认的就是同步加载 同步加载: 同步模式又称阻塞模式,会阻止浏览器的后续处理,停止了后续的文件的解析,执行,如图像的渲染.流览器之所以会采用同步模式,是因为加载的js文件中有对dom的操作, ...

  4. 图片预加载和懒加载的多种方法

    图片预加载和懒加载 图片懒加载 在渲染页面的时候,先将图片用一张默认图片代替,当图片到达浏览器可视区域时,才显示真实的图片. 这样的好处是,可以减缓服务器负担,加快初始界面的渲染速度. 实现方式: 使 ...

  5. img加载本地图片_图片加载技术-懒加载和预加载

    懒加载也就是延迟加载. 具体表现为: 当访问一个页面的时候,先把img元素或是其他元素的背景图片路径替换成一张占位图的路径,这样就只需请求一次,只有当图片出现在浏览器的可视区域内时,才设置图片正真的路 ...

  6. html页面预加载图片不出来,页面图片预加载与懒加载策略

    在图片的加载策略之前,我们先来了解下html网页中,图片的不同位置的图片分别是在什么时候发起图片资源请求的 img 标签 img标签会在html渲染解析到的时候,如果解析到img src值,则浏览器会 ...

  7. 超详细的图片预加载和懒加载教程

    最近接手一个项目 . 结果光安装依赖都出现了一堆 麻烦 . 好不容易处理完一个 , 又来一个 .头疼啊 看到之前有一些预加载的学习笔记.于是又查查找找 ,想想写写 把预加载和懒加载的笔记写完整 发现制 ...

  8. 【vue】预加载、懒加载

    项目场景: 提示:记录: 预加载:会在使用之前,提前加载js文件(等其他资源加载完毕,浏览器空闲了,在偷偷加载资源,适用于pc端)兼容性问题严重,caniuse查看是否使用该技术 懒加载:当文件需要使 ...

  9. 防抖、节流 和 预加载、懒加载

    目录 1. 防抖 节流 2. 预加载 3. 懒加载 (一)防抖 节流 1. 防抖 对于短时间内连续触发的事件(如滚动事件),防抖的含义就是让某个时间期限(如上面的1000毫秒)内,事件处理函数只执行一 ...

最新文章

  1. 团队-团队编程项目作业名称-模块开发过程
  2. 深入理解HTTP Session
  3. poj1015 Jury Compromise
  4. qq浏览器主页_安卓浏览器哪家强?这些小众好用的手机浏览器你知道吗
  5. Python中[index for index, value in enumerate(a) if value > 3]
  6. 利用函数重载编写函数max_彻底理清重载函数匹配
  7. ModBus TCP/IP协议
  8. 2、Ubuntu下安装Vivado下的下载器驱动 Digilent 版本
  9. mysql 视频教程下载_最全138节Mysql数据库+PHP零基础到精通视频教程【云盘下载】...
  10. 三级网络技术综合、应用题(2019、3)
  11. uni-app快速上手顺序
  12. enter不换行 wps_wps表格按enter键不能换行解决方法教程
  13. ABAP 新特性 - COND SWITCH 操作符
  14. OpengL消除锯齿
  15. EFM32片内外设--VCMP 基本操作
  16. Java逐行读取fasta文件
  17. 实用:最经济的美容偏方大汇集
  18. ncbi查找目的基因序列_如何查找目标基因序列?掌握这几招就够了!(NCBI篇)...
  19. 有道云笔记markdown教程
  20. [RL robotic 环境] - [Robosuite](1)

热门文章

  1. html里下拉标记,HTML: select 标签
  2. android监听通知消息 脚本,Bash玩转脚本4之搞一套完整的Android反编译与分包工具...
  3. httpd开启status模块_开启Apache Server Status
  4. 计算机文化基础课程总结,计算机文化基础课程总结.docx
  5. ebay php 商品添加多运输,eBay如何设置物流方式
  6. 计算机基础及wps office应用_自考本科计算机应用基础考试大纲
  7. 狭义上讲侠义计算机安全,狭义的会计电算化是什么
  8. 四种形态图解_涨停板战法的四种形态(图解)
  9. settimeout( )是全局函数吗_JS函数的执行
  10. 非专业计算机考试基础内容,全国高等教育自学考试指定教材:计算机应用基础·非计算机专业公共基础科·代码00018...