viewpager预加载(Android-ViewPager2)

本文目录
- Android-ViewPager2
- viewpager加fragment出现空白页面
- 如何让viewpager取消预加载且能有tabpageindicator
- android 卡片画廊效果及RecycleView、ViewPager、ScrollView之前的冲突解决
- fragment启动太复杂怎么优化
- 记录一个问题:Fragment的切换bug1
- Fragment 数据懒加载及原理
- android 如何去掉viewpager的预加载
Android-ViewPager2
ViewPager2 简单说就是将RecycleView再封装了一遍,然后协同FragmentStateAdapter将RecycleView的每个Item与Fragment绑定。 特性 支持从左到右,或者从上到下布局 由于适配基于的是RecyclerView.Adapter,所以内存优化也直接采用RecyclerView.Adapter的内存优化机制,相对于viewpager,内存优化更高效合理,且notifyDataSetChanged也更高效了。由于不用开发者自己实现内存和notifyDataSetChanged,也更简便了。 相比ViewPager,ViewPager2修复了不能关闭预加载和更新Adapter不生效的痛点。 目前ViewPager2对Fragment支持只能用FragmentStateAdapter,FragmentStateAdapter在遇到预加载时,只会创建Fragment对象,不会把Fragment真正的加入到布局中,所以自带懒加载效果。 FragmentStateAdapter不会一直保留Fragment实例,回收的ItemView也会移除Fragment,所以得做好Fragment重建后恢复数据的准备。 FragmentStateAdapter在遇到offscreenPageLimit》0时,处理离屏Fragment和可见Fragment没有什么区别,所以无法通过setUserVisibleHint判断显示与否。基本方法 部分核心方法使用参照RecycleView和ViewPager,如设置分割线addItemDecoration(),设置当前项setCurrentItem()等。 setAdapter() 设置适配器 setOrientation() 设置布局方向 setCurrentItem() 设置当前Item下标 beginFakeDrag() 开始模拟拖拽 fakeDragBy() 模拟拖拽中 endFakeDrag() 模拟拖拽结束 setUserInputEnabled() 设置是否允许用户输入/触摸 setOffscreenPageLimit()设置屏幕外加载页面数量 registerOnPageChangeCallback() 注册页面改变回调 setPageTransformer() 设置页面滑动时的变换效果 。。。还有好多。使用的时候大家可以具体看一下。offscreenPageLimit() 不设置它则不会预加载,一旦设置了,由于limit必须》0,所以会进行预加载limit个页面 viewpager2的预加载在加载时已经准备好了View布局,但是没有加载到parent视图上,所以自带懒加载效果。 而viewpager加载的时候View已经添加到parent上。所以会走生命周期的方法。从 初始化 方法可以看出,viewpager2支持的一些特性以及为什么。RecyclerViewImpl 基于RecyclerView的二次封装,对触摸事件,初始化等进行封装。 LinearLayoutManagerImpl 使用LinearLayoutManager,所以拥有LinearLayoutManager的特性,可以垂直或者水平。也就引申出为什么后面可以设置水平或者垂直滑动PageTransformerAdapter 用于监听pager的改变。基于 RecyclerView.Adapter实现 类似recycleView的使用。 基于FragmentStateAdapter实现
viewpager加fragment出现空白页面
切换fragment时出现空白页面。 问题原因:onCreateView每次都调用导致的,这样fragment每次都会设置新的view,而之前的view并没有被回收,这就导致了这个问题。 解决方案:1,预加载,viewpager.setOffscreenPageLimit(num);num为你的页面的个数 或者复制viewpager源码,修改字段private static final int DEFAULT_OFFSCREEN_PAGES =num;//默认是1 改成你预加载页面个数 2,基类移除view,再重新添加 @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // TODO 自动生成的方法存根if(contentView !=null) { ViewGroup parent = (ViewGroup) contentView.getParent(); if(parent !=null) { parent.removeView(contentView); } return contentView; } returncontentView = inflater.inflate(R.layout.fragment, container, false); } 3,重写public void destroyItem(ViewGroup container, int position, Object object),去掉super.destroyItem(container, position, object); 4,如果fragment里面嵌套了viewpager,里面的viewpager加载网络数据时也会出现空白页面,可以把FragmentStatePagerAdapter改成FragmentPagerAdapter即可解决。
如何让viewpager取消预加载且能有tabpageindicator
viewpager的预加载是无法取消的。但我们可以换一种思路来实现。取消预加载无非就是你的页面没有准备齐全,数据上或其它的还不足以加载一个正确的视图。你可以对这样的页面只写一个空视图,viewpager需要的List《View》你可以组装好,也就有了tabpageindicator正确的显示。空视图可以在你数据准备好之后,用新视图来替换。空视图你可以使用某种Layout也可以用ViewStub(轻量级的视图)。
android 卡片画廊效果及RecycleView、ViewPager、ScrollView之前的冲突解决
1、内容需要通过卡片的形式来展现,还有支持加载更多,所以最底部使用RecyclerView,最好是做成预加载形式,提前n页加载下一页,这样体验更好。 2、为了展示更多内容卡片内要支持垂直分页,这时候我使用了ViewPager,一是可以更好的管理分页内容,二是ViewPager的垂直分页容易实现,三是可以处理不同控件之前的滑动冲突 3、ViewPager第一页使用的可回弹的ScrollView,可以在下拉的时候做一些动画之类的操作,例如关注操作等。 4、ViewPager的第二页只是一个普通的ScrollView,具体使用可以根据实际情况来处理 1、RecycleView的分页效果基于PagerSnapHelper,RecyclerView在25.1.0版本中添加了一盒基于SnapHelper的子类PagerSnapHelper,可以使RecyclerView像ViewPager一样的效果,一次只能滑一页,而且居中显示。 2、卡片的效果是在滑动的时候根据RecycleView的偏移量计算缩放因子进行缩放 3、RecycleView的item内有一个垂直分页的VerticalViewPager,VerticalViewPager是在ViewPager上转换X,Y即可 4、解决ViewPager与RecycleView滑动的冲突,在ViewPager中屏蔽父视图的上下滑动事件 5、解决ViewPager子视图ScrollView的冲突,在ViewPager中拦截事件 6、解决ViewPager与BounceScrollView的冲突,在下拉过程中有时会被ViewPager拦截 以上就是此项目中的所有关键点。 ScrollViewDemo 欢迎Star RecycleViewCardGallary
fragment启动太复杂怎么优化
关于Fragment启动复杂的问题,有一些优化方法可以采用。首先,尽可能地使用单个Activity多个Fragment的架构,避免创建和销毁Activity的开销。其次,使用ViewPager预加载邻近的Fragment,可以提升用户体验。还可以使用Fragment的嵌套来合并UI元素,避免重复的布局代码。最后,对于较为复杂的Fragment建议进行优化,如将繁琐的UI和业务逻辑拆分成不同的子Fragment。这些方法可以在一定程度上优化Fragment的启动速度,提升应用的性能。
记录一个问题:Fragment的切换bug1
记录一个在项目中遇到的问题,电影详情部分的预告片,在切换Fragment之后预告片依然在继续播放,目前已经解决,记录一下问题。 这种问题发生的原因有两种: 1.Fragment的预加载机制,我项目采用的是ViewPager+Fragment组合进行展示,Fragment默认加载机制是预加载机制即加载前一个Fragment、当前Fragment以及后一个Fragment。假设现在又1、2、3、4总计4个Fragmnet,我的预告片在Fragment2,打开详情以后因为预加载机制Fragment2也开始进行加载,预告片开始播放,同理切换到Fragment3时预告片依然会接着播放。解决这个问题有两个方法。 一:通过ViewPager的setOffscreenPageLimit(int limit)可以设置预加载页面数量。 二:通过Fragmnet的setUserVisibleHint(boolean isVisibleToUser)方法,把Fragment设置为懒加载即可解决。 2.如果项目是通过add hide show进行fragment切换的时候,使用add hide() show()方法切换fragment 不会走任何的生命周期,无法通过生命周期进行刷新这个时候另一个方法就派上用处了,onHiddenChanged(),在方法中进行暂停即可。
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。
android 如何去掉viewpager的预加载
两种方式实现viewPager不加载数据。只需要设置viewPager.setOffscreenPageLimit(3);表示三个界面之间来回切换都不会重新加载。使用Adapter,每次改变数据后重新设置Adapter。MyAdapter adapter = new MyAdapter();viewPager.setAdapter(adapter); 即可。

更多文章:
k95火车时刻表查询(北京到锦州的火车兴城到北京火车时刻表)
2025年9月15日 20:15
sf90 stradale(法拉利SF90 XX Stradale/Spider全球首发 零百加速2.3秒)
2025年3月4日 19:20





















