viewpager2懒加载(viewpager的缓存加载机制)
本文目录
- viewpager的缓存加载机制
- 安卓中viewpager+tablayout+fragment懒加载怎么做
- android viewpager如何动态添加页面
- ViewPager详解
- Android-解决ViewPager2嵌套ViewPager2冲突
- ViewPage的使用
- Android开发 :Fragment懒加载的几种方式与性能对比
- Fragment 数据懒加载及原理
- viewpager中fragment数据更新不能及时显示问题
viewpager的缓存加载机制
viewpager有缓存预加载机制,主要使用setOffscreenPageLimit(int limit)
1.setOffscreenPageLimit(int limit) 解释
缓存:左右都会缓存limit个页面,比如limit缓存数量是2,在tab3,那会缓存tab1、tab2、tab4、tab5,如果其他界面已经缓存过的话会调用onDestroyView销毁
预加载:limit缓存数量是2,如果在tab1会预加载tab2、tab3,切换到tab2的话会预加载tab4,
2.viewpager源码分析
viewpager继承viewgroup当然也要走onmeasure,ondraw,onlayout方法,主要看onmeasure里面的populate()方法
这里的mAdapter是PagerAdapter
fragmentpageradapter就是你写的适配器,接下来看具体实现
所以我们可以用uservisiblehint来懒加载,需要注意的是uservisiblehint不是生命周期函数,初始化的时候他的执行在fragment的onattch之前
Fragment 生命周期按先后顺序:onAttach -》 onCreate -》 onCreatedView -》 onActivityCreated -》 onStart -》 onResume -》onPause -》 onStop -》 onDestroyView -》 onDestroy -》 onDetach
安卓中viewpager+tablayout+fragment懒加载怎么做
viewpager的预加载是无法取消的。 但我们可以换一种思路来实现。取消预加载无非就是你的页面没有准备齐全,数据上或其它的还不足以加载一个正确的视图。 你可以对这样的页面只写一个空视图,viewpager需要的List你可以组装好
android viewpager如何动态添加页面
动态创建Adapter即可。ViewPager的数据是通过PageAdapter来装载的,刷新数据的方法有以下:
1. 调用adapter.notifyDataSetChanged(); 刷新控件,但是要覆盖PagerAdapter的getItemPosition方法,并返回 return POSITION_NONE;
2. 利用PagerAdapter的工作机制,就是PagerAdapter的执行顺序, PagerAdapter作为ViewPager的适配器,无论ViewPager有多少页,PagerAdapter在初始化时也只初始化开始的2个View,即调用2次instantiateItem方法。而接下来每当ViewPager滑动时,PagerAdapter都会调用destroyItem方法将距离该页2个步幅以上的那个View销毁,以此保证PagerAdapter最多只管辖3个View,且当前View是3个中的中间一个,如果当前View缺少两边的View,那么就instantiateItem,如里有超过2个步幅的就destroyItem。
3. 每当Adapter调用instantiateItem时,运用View.setTag方法将该View标识。当需要更新这个View的数据时,通过调用ViewPager.findViewWithTag方法找到相应的View,然后更新View中的数据。
ViewPager详解
如果你不使用setoffscreenpagelimit(int limit)这个方法去设置默认加载数的话是会默认加载页面的左右两页的,也就是说当你进入viewpager第一页的时候第二页和第一页是会被一起加载的,这样同时加载就会造成一些问题,试想我们如果设置了setoffscreenpagelimit为3的话,那么进入viewpager以后就会同时加载4个fragment,像我们平时的项目中在这些fragment中一般都是会发送网络请求的,也就是说我们有4个fragment同时发送网络请求去获取数据,这样的结果显而易见给用户的体验是不好的(如:浪费用户流量,造成卡顿等等)。
ViewPager的预加载机制。默认缓存当前页面的前后各一个。
参考: ViewPager懒加载
PagerAdapter有两个子类:
FragmentPagerAdapter和FragmentPagerStateAdapter
参考: PagerAdapter深度解析和实践优化
ViewPager2实现是RecyclerView
ViewPager2的Adapter是FragmentStateAdapter
Android-解决ViewPager2嵌套ViewPager2冲突
业务场景:
LinearLayout+ViewPager2实现底部导航,然后Fragment当中MagicIndicator+ViewPager2,实现顶部导航栏。两个页面都是滑动切换的情况。这样两个ViewPager2会出现滑动冲突。
使用方式也很简单的,直接在子Fragment的嵌套使用:
完美的解决了 ViewPager2 嵌套 ViewPager2 滑动冲突的问题。参考的博客:
这可能是ViewPager2滑动冲突最全的处理方案
希望我的这篇博客对有 ViewPager2 嵌套 ViewPager2 滑动冲突问题的小伙伴有所帮助。
ViewPage的使用
ViewPager是Android扩展包v4包中的类
作用:
左右切换当前的view,实现滑动切换的效果
注意:
1.ViewPager类直接几层了ViewGroup类,和LinearLayout等布局一样,都是一个容器,需要在里面添加我们想要显示的内容。
2.ViewPager类需要PagerAdapter适配器类提供数据,与ListView、RecyclerView类似
使用:
建立ViewPage的步骤:
1.在XML布局中加入android.support.v4.view.ViewPager
2.在activity中加载要显示的页卡
当需要加载的页卡是View时:
当需要加载的页卡是Fragment时:
3.用相应的适配器Adapter关联上面的页卡(View/Fragment)和ViewPager
PagerAdapter 数据源:List《View》
FragmentPagerAdapter数据源:List《Fragment》
FragmentStatePagerAdapter数据源:ListView《Fragment》
当页卡是View时:用ViewPagerAdapter
当页卡是Fragment时:用FragmentAdapter
4.在activity里绑定adapter
ViewPagerAdapter:
5.设置切换、滑动动画
利用Viewpage自带的方法setPageTransformer()可用于设置切换动画
步骤:
1.先定义动画效果类
DepthPageTransformer.java
2.动画方法调用:
FragmentStatePagerAdapter 和 FragmentPagerAdapter 的异同:
• 同
PageAdapter 是 FragmentPagerAdapter 以及FragmentStatePagerAdapter 的基类,可将上面的FragmentPagerAdapter 替换成FragmentStatePagerAdapter
• 异
FragmentPagerAdapter使用时,每一个生成的 Fragment 都将保存在内存之中,而 FragmentStatePagerAdapter 只保留了当前显示的Fragment,其他划过的Fragment离开视线后,就会被销毁;而在页面需要显示时,再生成新的实例。
即当拥有大量的页面时,使用FragmentStatePagerAdapter不必在内存中占用大量的内存
Android开发 :Fragment懒加载的几种方式与性能对比
TabLayout+ViewPager+Fragment是我们开发常用的组合。ViewPager的默认机制就是把全部的Fragment都加载出来,而为了保障一些用户体验,我们使用懒加载的Fragment,就是让我们再用户可见这个Fragment之后才处理业务逻辑。
而我们在一些设备或版本中可能就出现懒加载失效的问题。其实谷歌早就把一些懒加载的方案都标记弃用了,我们一直都用的老的随时会失效的Api。万一哪天彻底失效了就会导致线上事故。
接下来我们就看看Fragment的懒加载是如何演变的。谷歌又是推荐我们如何使用的。
在AndroidX还没出来的时候,大家的懒加载应该都是这样。判断setUserVisibleHint的方法,当用户可见的时候才回调方法去加载逻辑。
例如的我封装:
使用的示例:
扩展方法:
Fragment:
到此就实现了onLazyInitData的回调,只有出现Fragment显示在前台的时候才会调用方法,执行逻辑。
每次判断 setUserVisibleHint 和 onHiddenChanged 也麻烦,并且他们并不稳定,我也遇到过不回调的时候。
Android出来之后,给 FragmentStatePagerAdapter 添加了一个 @Behavior int behavior 的参数。
其本质就是内部帮你处理和切换MaxLifecycle:
如何使用呢:
之前的扩展方法以及预留了 behavior 参数,当为1的时候就不会回调 setUserVisibleHint 方法了,我们直接** OnResume 即可。
注意这个页面继承的就不是我们自定义的懒加载Fragment了。普通的Fragment 回调 onResume 即可。
ViewPager2出来之后。我们的 FragmentStatePagerAdapter 退出历史舞台。
即便能用,即便效果还是和ViewPage2的效果一样,但是还是标记废弃了。具体原因我也不知道,据说是因为老版本会出现问题导致数据丢失,页面空白。
ViewPage2我们都知道内部是通过RV实现的。但是对于Fragment的处理有单独的Adapter实现。
扩展方法:
使用:
使用的方式和ViewPager差不多,这里的Fragment也是使用普通的Fragment即可。
内存占用分别取三组数据
ViewPager数据
ViewPager2数据
结论 ViewPager2基于RV实现的效果还是比老版ViewPager要骚好一点。
并且老版本标记废弃,大家如果是用ViewPager2的话,还是推荐使用ViewPager2实现。如果大家还是用的老版本的ViewPager也推荐使用behavor参数。使用 onResume 实现懒加载的实现。以后再换到ViewPager2的话,可以无缝切换过来。
说明一下,测试数据仅供参考,毕竟我也不是专业测试,测试数据源也不不多。如有不对的地方,也望大家指正。
Fragment 数据懒加载及原理
最近据后台同事反馈说,某些接口调用的频率有点高,而这块业务还没完全开放,照理说很少会用到,于是让我查查怎么回事。
我看了下日志,把网络请求日志过滤出来,发现的确有问题,每次打开首页后都有许多那块业务相关的网络请求。于是马上联想到可能是因为首页改版之后嵌套使用了 ViewPager,业务未完全开放的那个 fragment 里嵌套了一个 ViewPager,里面有多个 fragment,这样每次打开首页都会去加载该 page,然后是一连串的 fragment 初始化以及网络请求,所以为了解决该问题就不得不使用懒加载。
最终想要实现的效果是:1) 当 fragment 不可见的时候不加载数据;2) 当数据已经加载过之后,除非手动刷新否则不重新请求数据。
首先,默认情况下,由于 ViewPager 会预加载左右两边相邻的 至少 1 个 fragment,通过 setOffscreenPageLimit() 设置预加载 page 数为 0 并不会起作用,这点从 ViewPager 的源码中可以看到:
从以上源码可以看出相邻 fragment 的加载是必然的,但是我们如果可以得知 fragment 可见性,那么就可以在 fragment 可见时才去加载数据。这样虽然不是完全的懒加载,只是数据懒加载,但是同样也可以满足我们的需求了。
那么 fragment 中有没有可以获取当前 fragment 是否可见的方法呢,当然是有的,它就是 setUserVisibleHint(boolean isVisibleToUser) 。
无论你使用的是 FragmentPagerAdapter 还是 FragmentStatePagerAdapter,当它们初始化 fragment 的时候,该方法都会被调用两次。
一次是在实例化的时候,也就是在 instantioateItem() 方法中:
一次是在用户滑动到当前 fragment 的时候,在 setPrimaryItem() 方法中:
另外,当用户从当前 fragment 滑出的时候,setPrimaryItem() 方法也会被调用。
来看下 setUserVisibleHint() 的注释:
系统正是通过该方法来判断当前 fragment 的 UI 是否对用户可见,而该方法被暴露出来的主要目的也是让我们可以提醒系统当前 fragment 已经不可见了,是时候重新更新 fragment 的生命周期了。
不过如果只是实现数据懒加载,我们不需要直接去调用该方法,只要覆写它并实现控制数据加载的逻辑就可以了。
这里我参考了一种比较简便的做法,原文来自 尹star 的 ViewPager+Fragment LazyLoad 最优解 。
实现效果: lazy_load_fragment_demo
项目地址: aJIEw/DemoUI-LazyLoadFragment
可以看到只有第一次进入 fragment 的时候才会加载数据,而且也不会主动加载相邻的 fragment 或者已经加载过的数据了。
首先,由于 setUserVisibleHint() 会在 fragment 实例化时就先被调用 (在 onAttach() 之前),所以我们最好在 view 创建完毕之后加载数据,因此需要设置一个 view 是否初始化完毕的标志位。另外,当然也需要一个 view 是否可见的标志位,只有等到 view 可见才允许加载。然后还可以选择保存数据的初始化状态,这样可以控制在 fragment 生命周期中的合适时机重新加载数据。所以,我们需要以下 3 个标志位:
然后接下来分为两种情况,一种是 view 初始化完毕但是此时还不可见的情况。很显然,我们只要判断 setUserVisibleHint() 中参数的值就可以了:
还有一种情况是,如果当前 fragment 是整个 ViewPager 的第一个 fragment,那么 setUserVisibleHint(true) 会在 view 初始化之前就在 setPrimaryItem() 中被调用,此时 view 已经可见了,但是我们要等到 view 初始化才加载数据,所以我们要在某个地方判断 view 是否已经初始化并且去加载数据。
最好的地方是在 onActivityCreated() 中。根据 fragment 生命周期我们知道,onActivityCreated() 会在 onCreateView() 之后调用,此时 view 已经初始化完毕,我们可以在这里将 isViewInitiated 标记为 true,同时在这里为第一个显示的 fragment 加载数据:
最后,我们还需要判断下数据是否已经加载过,避免重复加载。
我们将以上所有判断逻辑写在 prepareFetchData() 中,判断条件为 view 已经初始化、可见且数据未加载:
最后再定义一个抽象方法 fetchData(),让子类去实现:
这样一个完整的数据懒加载就实现完毕了。
我们可以看下以上操作的日志来验证下我们的想法。
第一次打开,FirstFragment 作为第一个可见的 fragment 立马被初始化:
此时 isVisibleToUser 会在 isViewInitiated 之前设为 true,所以 FirstFragment 会在 onActivityCreated() 中真正开始获取数据。
另外,由于预加载的存在,SecondFragment 也会被创建,但是此时还不可见:
当滑动到 SecondFragment 的时候,SecondFragment 状态变为可见,setUserVisibleHint(true) 被调用,所以开始获取数据:
而此时 FirstFragment 由可见变为不可见:
ThirdFragment 则开始第一次被创建,同样此时并不可见:
当滑动到 ThirdFragment 的时候,状态变为可见,所以也就开始获取数据:
此时 SecondFragment 由可见变为不可见:
而 FirstFragment 由于超出了 ViewPager 可以保存的 Fragment 的数量,所以被销毁:
此时 SecondFragment 重新变得可见:
而 FirstFragment 也开始重新被创建:
此时 FirstFragment 重新变得可见,虽然 FirstFragment 之前被销毁了,但是由于之前获取的数据会被恢复,所以现在不会重新去获取数据:
当然我们也可以选择在 onDestroy() 中将 isDataInitiated 置为 false,这样每次 fragment 重新创建都会重新获取数据。当然前提是你使用的是 FragmentStatePagerAdapter ,因为如果使用 FragmentPagerAdapter ,不会每次都调用 onDestroy(),fragment 实例会被保存。而 SecondFragment 再次变得不可见,ThirdFragment 被销毁,过程与 3 中移动到 ThirdFragment 类似,这里就不截图了。
通过以上日志,验证了我们的想法是对的。
另外,如果是 ViewPager 嵌套 ViewPager 其实效果也是一样的,如果不做特殊处理,相邻的 fragment 的会被加载,导致该 fragment 中的 ViewPager 会去加载其中的 fragment。
viewpager中fragment数据更新不能及时显示问题
Mark一下吧,在手机上没心思回答。
————————————————————————
倦怠症发作,还是没有Coding的欲望,不过受楼下启发我突然想起一种会被人揍的省事方法,理论上可行,就是给内嵌的PagerView设置属性,setOffscreenPageLimit(0),也就是不预加载,这样每次切换页面都要onCreateView了(注意不是onActivityCreate,这里Activity依旧只Create一次)
其实这属于Fragment间的通信问题吧,正常实现方式挺多的,图省事的话就用EventBus,
笨一点的就弄个静态flag,数据变化时就把flag设为true,然后在有列表的Fragment的onResume周期判断,listView,adapter不为null(重要)且flag为true就notifydatasetchanged,然后再把flag改回false。这习惯不好,会被人鄙视不过能达到目的。
换平时要Fragment间及时交互,我通常会在Fragment的构造方法里传接口实例,通过父Activity分配处理,建议你也用这种方式,邪道方式尝试下可以,用多了养成坏习惯就害了自己了……
更多文章:

秦皇岛婚纱摄影工作时间(秦皇岛婚纱摄影哪里好秦皇岛LE婚纱摄影怎么样)
2025年4月18日 06:12

婚纱照加片一万二是不是傻(拍婚纱照花了一万多,值吗是不是很傻)
2025年4月15日 17:48

普通人像照片裙子(模特穿着蓝色系列的连衣裙,适合在什么场景下拍人像好看)
2025年4月18日 23:27

浪漫求婚现场(一场浪漫的求婚需要准备什么,现场准备物品罗列!)
2025年4月13日 17:33

最终幻想15结婚蛋糕(最终幻想15结婚蛋糕草莓在哪 制作方法介绍)
2025年4月15日 02:45

2022年长沙婚博会时间表(2022长沙民政局周末上班吗 长沙民政局周六可以领证吗)
2025年4月11日 14:27

旧照片翻新多少钱一张(修复老照片老照片修复要多少钱那里有修复老照片)
2025年4月5日 21:45

双反相机用什么胶卷(牡丹双反用的胶片多大120还是135 一般在哪儿买 什么胶片效果照起来好些)
2025年4月9日 15:06