本篇文章已授权微信公众号 dasu_Android(大苏)独家发布

使用前需知

2017-7-14更新: 目前有人使用后出现了诸如首次打开显示空白界面,但点击有反应;或来回切换又变空白界面的问题。这些问题我暂时还不知道该怎么解决,后期有时间时会具体去分析下问题该怎么解决。所以你如果要使用该代码,希望考虑一下,我自己的小应用目前是没碰到这些问题。

效果

老规矩,先来看看效果图

没错,我又入坑了,又重新做了个 Gank 客户端,因为之前那个代码写得太烂了,这次有好好的考虑了下架构之类的事,代码应该会更容易读懂了点了,吧。哈哈,再次欢迎来 star 交流哈。

上面的截图里有注释解析了,稍微认真点看看 log 的内容哈,看看是不是你需要的需求。

Fragment懒加载

如果想直接看代码,直接跳到最下面的代码部分和使用介绍即可,如果感兴趣,可以慢慢往下看看我的唠叨。

之前写过一篇 Fragment懒加载和ViewPager的坑,里面分析了 Fragment 结合 ViewPager 使用时会碰到的一些情况,以及为什么要用懒加载,如何用,感兴趣的也可以再回去看看。

后来发现,我在那篇博客里封装的 Fragment 基类不足以满足大家的懒加载需求,所以决定重新来封装一次,这次封装的支持以下的功能:

1.支持数据的懒加载并且只加载一次

2.提供 Fragment 可见与不可见时回调,支持你在这里进行一些 ui 操作,如显示/隐藏加载框

3.支持 view 的复用,防止与 ViewPager 使用时出现重复创建 view 的问题

第一点应该是比较需要且常用的一点,之前那篇博客里没有考虑到这点应用场景是我的疏忽。稍微讲解一下,有些时候,我们打开一个 Fragment 页面时,希望它是在可见时才去加载数据,也就是不要在后台就开始加载数据,而且,我们也希望加载数据的操作只是第一次打开该 Fragment 时才进行的操作,以后如果再重新打开该 Fragment 的话,就不要再重复的去加载数据了。

具体点说,Fragment 和 ViewPager 一起用时,由于 ViewPager 的缓存机制,在打开一个 Fragment 时,它旁边的几个 Fragment 其实也已经被创建了,如果我们是在 Fragment 的 onCreat() 或者 onCreateView() 里去跟服务器交互,下载界面数据,那么这时这些已经被创建的 Fragment,就都会出现在后台下载数据的情况了。所以我们通常需要在 setUserVisibleHint() 里去判断当前 Fragment 是否可见,可见时再去下载数据,但是这样还是会出现一个问题,就是每次可见时都会重复去下载数据,我们希望的是只有第一次可见时才需要去下载,那么就还需要再做一些判断。这就是要封装个基类来做这些事了,具体代码见后面。

即使我们在 setUserVisibleHint() 做了很多判断,实现了可见时加载并且只有第一次可见时才加载,可能还是会遇到其他问题。比如说,我下载完数据就直接需要对 ui 进行操作,将数据展示出来,但有时却报了 ui 控件 null 异常,这是因为 setUserVisibleHint() 有可能在 onCreateView() 创建 view 之前调用,而且数据加载时间很短,这就可能出现 null 异常了,那么我们还需要再去做些判断,保证在数据下载完后 ui 控件已经创建完成。

除了懒加载,只加载一次的需求外,可能我们还需要每次 Fragment 的打开或关闭时显示数据加载进度。对吧,我们打开一个 Fragment 时,如果数据还没下载完,那么应该给个下载进度或者加载框提示,如果这个时候打开了新的 Fragment 页面,然后又重新返回时,如果数据还没加载完,那么也还应该继续给提示,对吧。这就需要有个 Fragment 可见与不可见时触发的回调方法,并且该方法还得保证是在 view 创建完后才触发的,这样才能支持对 ui 进行操作。

以上,就是我们封装的 BaseFragment 基类要干的活了。下面上代码。

代码


/*** Created by dasu on 2016/9/27.** Fragment基类,封装了懒加载的实现** 1、Viewpager + Fragment情况下,fragment的生命周期因Viewpager的缓存机制而失去了具体意义* 该抽象类自定义新的回调方法,当fragment可见状态改变时会触发的回调方法,和 Fragment 第一次可见时会回调的方法** @see #onFragmentVisibleChange(boolean)* @see #onFragmentFirstVisible()*/
public abstract class BaseFragment extends Fragment {private static final String TAG = BaseFragment.class.getSimpleName();private boolean isFragmentVisible;private boolean isReuseView;private boolean isFirstVisible;private View rootView;//setUserVisibleHint()在Fragment创建时会先被调用一次,传入isVisibleToUser = false//如果当前Fragment可见,那么setUserVisibleHint()会再次被调用一次,传入isVisibleToUser = true//如果Fragment从可见->不可见,那么setUserVisibleHint()也会被调用,传入isVisibleToUser = false//总结:setUserVisibleHint()除了Fragment的可见状态发生变化时会被回调外,在new Fragment()时也会被回调//如果我们需要在 Fragment 可见与不可见时干点事,用这个的话就会有多余的回调了,那么就需要重新封装一个@Overridepublic void setUserVisibleHint(boolean isVisibleToUser) {super.setUserVisibleHint(isVisibleToUser);//setUserVisibleHint()有可能在fragment的生命周期外被调用if (rootView == null) {return;}if (isFirstVisible && isVisibleToUser) {onFragmentFirstVisible();isFirstVisible = false;}if (isVisibleToUser) {onFragmentVisibleChange(true);isFragmentVisible = true;return;}if (isFragmentVisible) {isFragmentVisible = false;onFragmentVisibleChange(false);}}@Overridepublic void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);initVariable();}@Overridepublic void onViewCreated(View view, @Nullable Bundle savedInstanceState) {//如果setUserVisibleHint()在rootView创建前调用时,那么//就等到rootView创建完后才回调onFragmentVisibleChange(true)//保证onFragmentVisibleChange()的回调发生在rootView创建完成之后,以便支持ui操作if (rootView == null) {rootView = view;if (getUserVisibleHint()) {if (isFirstVisible) {onFragmentFirstVisible();isFirstVisible = false;}onFragmentVisibleChange(true);isFragmentVisible = true;}}super.onViewCreated(isReuseView ? rootView : view, savedInstanceState);}@Overridepublic void onDestroyView() {super.onDestroyView();}@Overridepublic void onDestroy() {super.onDestroy();initVariable();}private void initVariable() {isFirstVisible = true;isFragmentVisible = false;rootView = null;isReuseView = true;}/*** 设置是否使用 view 的复用,默认开启* view 的复用是指,ViewPager 在销毁和重建 Fragment 时会不断调用 onCreateView() -> onDestroyView() * 之间的生命函数,这样可能会出现重复创建 view 的情况,导致界面上显示多个相同的 Fragment* view 的复用其实就是指保存第一次创建的 view,后面再 onCreateView() 时直接返回第一次创建的 view** @param isReuse*/protected void reuseView(boolean isReuse) {isReuseView = isReuse;}/*** 去除setUserVisibleHint()多余的回调场景,保证只有当fragment可见状态发生变化时才回调* 回调时机在view创建完后,所以支持ui操作,解决在setUserVisibleHint()里进行ui操作有可能报null异常的问题** 可在该回调方法里进行一些ui显示与隐藏,比如加载框的显示和隐藏** @param isVisible true  不可见 -> 可见*                  false 可见  -> 不可见*/protected void onFragmentVisibleChange(boolean isVisible) {}/*** 在fragment首次可见时回调,可在这里进行加载数据,保证只在第一次打开Fragment时才会加载数据,* 这样就可以防止每次进入都重复加载数据* 该方法会在 onFragmentVisibleChange() 之前调用,所以第一次打开时,可以用一个全局变量表示数据下载状态,* 然后在该方法内将状态设置为下载状态,接着去执行下载的任务* 最后在 onFragmentVisibleChange() 里根据数据下载状态来控制下载进度ui控件的显示与隐藏*/protected void onFragmentFirstVisible() {}protected boolean isFragmentVisible() {return isFragmentVisible;}
}

使用方法

使用很简单,新建你需要的 Fragment 类继承自该 BaseFragment,然后重写两个回调方法,根据你的需要在回调方法里进行相应的操作比如下载数据等即可。
例如:

public class CategoryFragment extends BaseFragment {private static final String TAG = CategoryFragment.class.getSimpleName();@Nullable@Overridepublic View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {View view = inflater.inflate(R.layout.fragment_category, container, false);initView(view);return view;}@Overrideprotected void onFragmentVisibleChange(boolean isVisible) {if (isVisible) {//更新界面数据,如果数据还在下载中,就显示加载框notifyDataSetChanged();if (mRefreshState == STATE_REFRESHING) {mRefreshListener.onRefreshing();}} else {//关闭加载框mRefreshListener.onRefreshFinish();}}@Overrideprotected void onFragmentFirstVisible() {//去服务器下载数据mRefreshState = STATE_REFRESHING;mCategoryController.loadBaseData();}
}

注意事项

  1. 如果想要让 fragment 的布局复用成功,需要重写 viewpager 的适配器里的 destroyItem() 方法,将 super 去掉,也就是不销毁 view。
  2. 如果出现切换回来或不相邻的Tab切换时导致空白界面的问题,解决方法:在 onCreateView中复用布局 + ViewPager 的适配器中复写 destroyItem() 方法去掉 super。

最后,继续不要脸的贴上我最近在做的 Gank 客户端的项目地址啦,项目没引入什么高级的库,都是用的最基本的代码实现的,项目也按模块来划分,也尽可能的实现ui和逻辑的划分,各模块也严格控制权限,尽量让模块之间,类之间的耦合减少些,之所以这样是为了后面更深入理解mvp做准备,总之,代码应该还是很容易可以看懂的吧,欢迎大家star交流。

GanHuo:https://github.com/woshidasusu/GanHuo
Meizi:https://github.com/woshidasusu/Meizi



最近刚开通了公众号,想激励自己坚持写作下去,初期主要分享原创的Android或Android-Tv方面的小知识,感兴趣的可以点一波关注,谢谢支持~~

转载于:https://www.cnblogs.com/dasusu/p/6745032.html

【Android】再来一篇Fragment懒加载(只加载一次哦)相关推荐

  1. Android androidx下的fragment懒加载

    androidx下的fragment中onHiddenChanged方法不会被调用,这就导致了懒加载实现方式要变更 前置条件: BottomNavigationView+ViewPager+fragm ...

  2. 【Android】Fragment懒加载和ViewPager的坑

    本篇文章已授权微信公众号 安卓巴士Android开发者门户 独家发布 效果 老规矩,先来看看效果 ANDROID和福利两个Fragment是设置的Fragment可见时加载数据,也就是懒加载.圆形的旋 ...

  3. Android优化方案之--Fragment的懒加载实现

    一.背景 在Android应用中,ViewPager是我们不可避免使用的一个控件,因为它可以使我们在占用较少空间的同时,增强内容的丰富性,同时以其内部流淌着Google的血液,所以它几乎成了每一个Ap ...

  4. Android多个fragment懒加载的坑(卡顿)

    在Android开发中,有很多种情况都是viewpage+fragment左右滑动进行布局的,例如下面的今日头条 在今日头条app上,我们可以看到,其顶部都是一栏的类型数据源,可滑动,可联动下面的vi ...

  5. Android——Fragment懒加载

    Fragment懒加载 简述 LazyFragment 简述 大部分APP框架由一个单例MainActivity和多个Fragment组成:在实际开发中常使用BottomNavigationView+ ...

  6. ViewPager+Fragment懒加载

    原文链接:http://blog.csdn.net/linglongxin24/article/details/53205878 Android中ViewPager+Fragment取消(禁止)预加载 ...

  7. 带你深入了解Fragment懒加载

    Fragment懒加载是一种优化技术,用于在Android应用中延迟加载和初始化Fragment的内容,以提高应用性能和用户体验.它的核心思想是只有在Fragment可见时才加载数据和执行相关操作,而 ...

  8. Fragment 懒加载

    为何懒加载 在很多 App 都会有ViewPage + Fragment 的基本组合,但是众所周知 viewPager 的 预加载机制,即使设置函数 setOffscreenPageLimit(0) ...

  9. Fragment懒加载预加载

    1. 预加载viewpager.setOffscreenPageLimit(2);,默认是预加载1,可以结合懒加载使用. 如果希望进入viewpager,Fragment只加载一次,再次滑动不需加载( ...

最新文章

  1. 一个关于wwf和linq的部署问题?
  2. who are you really?
  3. C++类的使用(三)—— 封装
  4. 结合源码看nginx-1.4.0之nginx内存管理详解
  5. vue-cli,webpack安装
  6. Spring aop优雅实现redis分布式锁 aop应用redis分布式锁
  7. 从零开始学android开发-创建第一个android项目
  8. Kotlin系列之枚举类
  9. Route66,GPS道路导航系统,我所用过的最大块头的手机软件
  10. Netty编解码框架分析
  11. 破产姐妹第六季/全集2 Broke Girls迅雷下载
  12. 2015061004 - slf4和mysql,jdbc下载地址
  13. Linux下本地yum源配置和局域网yum源配置
  14. 世界头号电脑黑客 - 凯文•米特尼克(Kevin Mitnick)
  15. 关于电脑是否需要安装杀毒软件的解答
  16. 微信小程序设置单个页面自定义头部加背景图
  17. 用java爬取斗鱼弹幕
  18. shiro身份认证(HelloWorld)
  19. 【xlwings api语言参考】Worksheet.Cells 属性
  20. 3dfier:GIS数据转3D城市模型

热门文章

  1. 利用反射判断初始化后的对象所有属性是否为空判断对象指定属性是否为空
  2. 索引书单(持续更新中)
  3. python pymysql模块 链接mysql 遍历查询结果的方法 详解
  4. Linux系统管理(5)——使用yum快速搭建LAMP环境【方便快捷版】
  5. python进阶22再识单例模式
  6. maven打包报错找不到符号,由于找不到类中方法的解决思路
  7. 用python找出所有三位数中的水仙花数_python使用循环打印所有三位数水仙花数的实例...
  8. php中获取随机数的方法,PHP实现获取随机数的方法
  9. python图片马赛克_Python实现PS滤镜中马赛克效果示例
  10. html字颜色代码,css 字体颜色(css color)