我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android如何实现仿微信Viewpager-Fragment惰性加载

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android如何实现仿微信Viewpager-Fragment惰性加载

这篇文章将为大家详细讲解有关Android如何实现仿微信Viewpager-Fragment惰性加载,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

效果如图:

Android如何实现仿微信Viewpager-Fragment惰性加载

什么是lazy-loading呢?顾名思义就是在必要的时候才加载,否则不进行View的绘制和数据的加载。原因是Viewpager一次只会显示一个页卡,那么刚开始的时候,只需加载第一张Fragment页卡,其他的不加载,当用户向右滑动切换再进行加载。因为其他Fragment对于用户来说是不可见的,如果一开始就把全部Fragment一起加载,可能造成启动时卡顿的问题,更重要的是可能白白耗费用户的流量,因为用户可能并不需要其他Fragment的信息。

今天Google了有关Fragment惰性加载的资料,并没有找到介绍得清楚详细的博文+demo。所以我找到了Github上的一个开源项目demo里有关惰性加载的代码,学习了这个知识点,并把它整理出来分享给大家。

你应该知道

你应该知道viewPager.setOffscreenPageLimit();方法。该方法设置ViewPager允许有多少张pages存在于屏幕外(不包括正在显示的page),默认值是1。在范围之外的pages 的View会被销毁,即onDestroyView()会被执行。

Viewpager里面FragmentPagerAdapter、FragmentStatePagerAdapter的区别:

FragmentPagerAdapter会将每一个生成的Fragment都放到内存中,即无论怎么滑动切换ViewPager,都不会有一个Fragment的onDestroy方法被调用。但Fragment不在viewPager.setOffscreenPageLimit(3);保护的范围内会调用FragmentManager的detach()方法,相应的Fragment的onDestroyView会执行,但Fragment实例仍在!所以该类适用于需要展示的Fragment比较少的情况。

FragmentStateAdapter 有点类似于LIstview的RecyclerBin机制,当Fragment不在viewPager.setOffscreenPageLimit(3);保护的范围内,Fragment的就会被销毁,onDestroy()、onDetach()方法会被执行。适用于要展示Fragment数量比较多,Fragment的子View和数据量复杂的情况。
熟知Fragment的生命周期。

Android如何实现仿微信Viewpager-Fragment惰性加载

Fragment的生命周期

刚被new出来的Fragment并没有开始它的生命周期,当它被添加到FragmentManager时生命周期才开始。

我们通常是在onCreateView()中对Fragment完成视图的构建。若是要实现延迟加载,可以在调用onCreateView时获得一个空container的引用。当等待用户切换到屏幕的时候,开始加载数据和视图。

那么如何得知我们的Fragment何时被切换到屏幕呢?核心方法就是getUserVisibleHint()和在Fragment中重写setUserVisibleHint(boolean isVisibleToUser){…}方法。

在官方文档是这样描述该方法的:

public void setUserVisibleHint (boolean isVisibleToUser)
Added in API level 15
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.
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.
Parameters
isVisibleToUser true if this fragment's UI is currently visible to the user (default), false if it is not.

该方法的作用是设置一个提示或者标志,该标志代表的是Fragment在当前是否处于对用户的可见状态。注意这里的可见并不能与Activity或Fragment的onStart或者onResume混淆。因为Fragment处于onResume状态并不代表它对用户是可见的!仍觉得很困惑?那我们一起来Log一下吧。

我们把生命周期回调方法加了Log语句。

  @Override  public void setUserVisibleHint(boolean isVisibleToUser) {    super.setUserVisibleHint(isVisibleToUser);    Log.d("TAG", "setUserVisibleHint() called with: " + "isVisibleToUser = [" + isVisibleToUser + "]");  }

我们允许有4张页卡的缓存,因为微信是有4个tab的。这样ViewPager来回切换就不会有页卡被销毁了。

viewPager.setOffscreenPageLimit(3);

启动ViewPager后的Log:

D/TAG: setUserVisibleHint() called with: isVisibleToUser = [false]
D/TAG: setUserVisibleHint() called with: isVisibleToUser = [false]
D/TAG: setUserVisibleHint() called with: isVisibleToUser = [false]
D/TAG: setUserVisibleHint() called with: isVisibleToUser = [false]
D/TAG: setUserVisibleHint() called with: isVisibleToUser = [true]
D/TAG: onCreateView() : getUserVisibleHint():true
D/TAG: onStart() : getUserVisibleHint():true
D/TAG: onResume() : getUserVisibleHint():true
D/TAG: onCreateView() : getUserVisibleHint():false
D/TAG: onCreateView() : getUserVisibleHint():false
D/TAG: onCreateView() : getUserVisibleHint():false
D/TAG: onStart() : getUserVisibleHint():false
D/TAG: onResume() : getUserVisibleHint():false
D/TAG: onStart() : getUserVisibleHint():false
D/TAG: onResume() : getUserVisibleHint():false
D/TAG: onStart() : getUserVisibleHint():false
D/TAG: onResume() : getUserVisibleHint():false

从Log中,可得知,setUserVisibleHint()比onCreateView()先调用,并且只有一个方法的isVisbleToUser==true。由此我们可以断定,正在展示的fragment对应的isVisibleToUser才为true。我们现在有4个page,onCreateView()、onStart()、onResume()分别共调用了4次,由此可知,尽管Fragment没有被展示,ViewPager也会将它们构建起来,会回调onStart、onResume。那么ViewPager初始化时构建Fragment的个数与什么有关呢?这个主要跟使用的Adapter类型和setOffscreenPageLimit()有关。

接下来向右滑,切换到第二页,Log如下:

D/TAG: setUserVisibleHint() called with: isVisibleToUser = [false]
D/TAG: setUserVisibleHint() called with: isVisibleToUser = [true]

这次只会调用两次setUserVisibleHint(),将要刚刚显示的Fragment的isVisibleToUser 设置为false,并把将要显示的Fragment的isVisibleToUser 设置为true。

当我退出程序,Log如下:

D/TAG: onPause() : getUserVisibleHint():true
D/TAG: onPause() : getUserVisibleHint():false
D/TAG: onPause() : getUserVisibleHint():false
D/TAG: onPause() : getUserVisibleHint():false
D/TAG: onStop() called: getUserVisibleHint():true
D/TAG: onStop() called: getUserVisibleHint():false
D/TAG: onStop() called: getUserVisibleHint():false
D/TAG: onStop() called: getUserVisibleHint():false
D/TAG: onDestroyView() : getUserVisibleHint():true
D/TAG: onDestroy() :
D/TAG: onDetach() :
D/TAG: onDestroyView() : getUserVisibleHint():false
D/TAG: onDestroy() :
D/TAG: onDetach() :
D/TAG: onDestroyView() : getUserVisibleHint():false
D/TAG: onDestroy() :
D/TAG: onDetach() :
D/TAG: onDestroyView() : getUserVisibleHint():false
D/TAG: onDestroy() :
D/TAG: onDetach() :

从这“死亡日志”中,我们发现,getUserVisibleHint()贯穿着Fragment的凋亡生命线。
到此,对这个关键的方法,我们算是有了一个宏观的认识。

具体实现

那么具体应该怎么实现呢?我们可以在自定义一个抽象类LazyFragment,重写onCreateView()方法,只返回一个简单的,甚至是空的(不是null)的ViewGroup作为Container,比如return new FrameLayout();当然这个ViewGroup我们需要保存为成员变量。接下来重写setUserVisibleHint(boolean isVisibleToUser)方法,如果该Fragment处于用户可见状态,就会调用该方法,并传过来的isVisibleToUser==true。所以根据这个hint做一个判断,若等于true,立即加载原本要正常显示的视图和数据。当然这个方法可以作为一个抽象方法交给子类去实现。具体的实现就是这样!Talk is simple,show you the code!

LazyFragment:

省去了一些,回调方法,只给出了核心的几个方法,完整的可以看文章末尾的项目源码。注释已经写得相对完善,如果有不明白的地方欢迎评论留言。

public class LazyFragment extends BaseFragment { private boolean isInit = false;//真正要显示的View是否已经被初始化(正常加载) private Bundle savedInstanceState; public static final String INTENT_BOOLEAN_LAZYLOAD = "intent_boolean_lazyLoad"; private boolean isLazyLoad = true; private FrameLayout layout; private boolean isStart = false;//是否处于可见状态,in the screen @Deprecated protected final void onCreateView(Bundle savedInstanceState) {  Log.d("TAG", "onCreateView() : " + "getUserVisibleHint():" + getUserVisibleHint());  super.onCreateView(savedInstanceState);  Bundle bundle = getArguments();  if (bundle != null) {   isLazyLoad = bundle.getBoolean(INTENT_BOOLEAN_LAZYLOAD, isLazyLoad);  }  //判断是否懒加载  if (isLazyLoad) {   //处于完全可见、没被初始化的状态,调用onCreateViewLazy显示内容   if (getUserVisibleHint() && !isInit) {    this.savedInstanceState = savedInstanceState;    onCreateViewLazy(savedInstanceState);    isInit = true;   } else {    //进行懒加载    layout = new FrameLayout(getApplicationContext());    layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));    View view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.fragment_lazy_loading, null);    layout.addView(view);    super.setContentView(layout);   }  } else {   //不需要懒加载,开门江山,调用onCreateViewLazy正常加载显示内容即可   onCreateViewLazy(savedInstanceState);   isInit = true;  } } @Override public void setUserVisibleHint(boolean isVisibleToUser) {  super.setUserVisibleHint(isVisibleToUser);  Log.d("TAG", "setUserVisibleHint() called with: " + "isVisibleToUser = [" + isVisibleToUser + "]");  //一旦isVisibleToUser==true即可对真正需要的显示内容进行加载  //可见,但还没被初始化  if (isVisibleToUser && !isInit && getContentView() != null) {   onCreateViewLazy(savedInstanceState);   isInit = true;   onResumeLazy();  }  //已经被初始化(正常加载)过了  if (isInit && getContentView() != null) {   if (isVisibleToUser) {    isStart = true;    onFragmentStartLazy();   } else {    isStart = false;    onFragmentStopLazy();   }  } } @Override public void setContentView(int layoutResID) {  //判断若isLazyLoad==true,移除所有lazy view,加载真正要显示的view  if (isLazyLoad && getContentView() != null && getContentView().getParent() != null) {   layout.removeAllViews();   View view = inflater.inflate(layoutResID, layout, false);   layout.addView(view);  }  //否则,开门见山,直接加载  else {   super.setContentView(layoutResID);  } } @Override public void setContentView(View view) {  //判断若isLazyLoad==true,移除所有lazy view,加载真正要显示的view  if (isLazyLoad && getContentView() != null && getContentView().getParent() != null) {   layout.removeAllViews();   layout.addView(view);  }  //否则,开门见山,直接加载  else {   super.setContentView(view);  } }}

具体的实现类:

public class MoreFragment extends LazyFragment { private TextView tvLoading; private ImageView ivContent; private int tabIndex; public static final String INTENT_INT_INDEX="index"; public static MoreFragment newInstance(int tabIndex) {  Bundle args = new Bundle();  args.putInt(INTENT_INT_INDEX, tabIndex);  MoreFragment fragment = new MoreFragment();  fragment.setArguments(args);  return fragment; } @Override protected void onCreateViewLazy(Bundle savedInstanceState) {  super.onCreateViewLazy(savedInstanceState);  setContentView(R.layout.fragment_tabmain_item);  tabIndex = getArguments().getInt(INTENT_INT_INDEX);  ivContent = (ImageView) findViewById(R.id.iv_content);  tvLoading = (TextView) findViewById(R.id.tv_loading);  getData(); } private void getData() {  new Thread(new Runnable() {   @Override   public void run() {    //异步处理加载数据    //...    //完成后,通知主线程更新UI    handler.sendEmptyMessageDelayed(1, 2000);   }  }).start(); } @Override public void onDestroyViewLazy() {  super.onDestroyViewLazy();  handler.removeMessages(1); } private Handler handler = new Handler() {  public void handleMessage(android.os.Message msg) {   tvLoading.setVisibility(View.GONE);   int id=0;   switch (tabIndex){    case 1:     id=R.drawable.a;     break;    case 2:     id=R.drawable.b;     break;    case 3:     id=R.drawable.c;     break;    case 4:     id=R.drawable.d;     break;   }   ivContent.setImageResource(id);   ivContent.setVisibility(View.VISIBLE);  } };}

为了简化布局,demo中只用了微信上的几张截图,希望大家能专注重点。具体效果如图:

Android如何实现仿微信Viewpager-Fragment惰性加载

关于“Android如何实现仿微信Viewpager-Fragment惰性加载”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android如何实现仿微信Viewpager-Fragment惰性加载

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android如何实现仿微信Viewpager-Fragment惰性加载

这篇文章将为大家详细讲解有关Android如何实现仿微信Viewpager-Fragment惰性加载,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。效果如图:什么是lazy-loading呢?顾名思义就是在
2023-05-30

android fragment懒加载如何实现

Android Fragment的懒加载可以通过以下步骤实现:1. 在Fragment类中添加一个boolean类型的变量isLoaded,并在onCreateView()方法中将其初始化为false。2. 在Fragment的onCrea
2023-08-26

Android如何实现仿微信@好友功能

这篇文章主要介绍Android如何实现仿微信@好友功能,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!先上个效果图就是这么个功能1. 分析需求输入@跳转到联系人界面,选中一个或者多个好友返回到当前界面按退格键删除整块内
2023-05-30

Android如何实现仿微信数字键盘

这篇文章主要介绍了Android如何实现仿微信数字键盘,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一、图示效果二、需要考虑的问题布局的实现方式;demo中使用了popupw
2023-06-15

android如何实现仿微信通讯录搜索

这篇文章将为大家详细讲解有关android如何实现仿微信通讯录搜索,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一:先看效果图字母索引搜索匹配二:功能分析1:汉字转拼音通讯录汉字转拼音(首个字符当考虑姓氏
2023-05-30

Android如何实现仿微信右滑返回功能

这篇文章将为大家详细讲解有关Android如何实现仿微信右滑返回功能,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。先上效果图,如下:先分析一下功能的主要技术点,右滑即手势判断,当滑到一直距离时才执行返回,
2023-05-30

Android如何实现仿iOS菊花加载圈动画效果

这篇文章主要介绍了Android如何实现仿iOS菊花加载圈动画效果,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。常见的实现方式切图,做旋转动画自定义View,绘制效果gif图
2023-06-15

Android如何实现仿微信朋友圈全文、收起功能

小编给大家分享一下Android如何实现仿微信朋友圈全文、收起功能,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!效果图具体代码(详细解释在代码注释中都有,这里就省
2023-05-30

微信小程序图片懒加载如何实现

这篇文章主要介绍“微信小程序图片懒加载如何实现”,在日常操作中,相信很多人在微信小程序图片懒加载如何实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”微信小程序图片懒加载如何实现”的疑惑有所帮助!接下来,请跟
2023-06-26

Android仿微信列表滑动删除 如何实现滑动列表SwipeListView

接上一篇,本篇主要讲如何实现滑动列表SwipeListView。上篇完成了滑动控件SwipeItemView,这个控件是一个自定义的ViewGroup,作为列表的一个item,为列表提供一些方法让这个SwipeItemView能滑动其视图内
2022-06-06

Android如何实现仿微信语音消息的录制和播放功能

小编给大家分享一下Android如何实现仿微信语音消息的录制和播放功能,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!一、简述效果:实现功能:长按Button时改变Button显示文字,弹出Dialog(动态更新音量),动态
2023-05-30

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录