RecyclerView实现无限数据循环效果:JFPicker的迭代思路

聊聊天

  很久没有写东西了,一方面是现在原生移动端开发的需求越来越少,再加上在小城市当程序猿,压力还是有一点的,所以最近一个多月都在看一些vue和css的教程,以前除了Android也做uniapp,所以还是学起来还是比较轻松的,这段时间的工作也开始做做网页前端,从app的开发思维转换为网页的开发思维确实有点不得劲,但真的还是挺有意思的。另一方面,也是最重要的,是我比较懒。
  JFPicker这个代码库,是我在上一家公司的时候做的,上一家公司是专门给企业的业务流程做开发的,所以会大量用到滚轮选择器,并且样式基本被美工固定了,所以封装被提上了日程。一开始我也用了github上的一些滚轮库,但很多地方都不满足客户的需求,比如滑动不够丝滑有卡顿、3D效果不明显等等,所以在AndroidPicker开源库的基础上,替换了核心的滚轮组件,使用了用RecyclerView实现的滚轮,以获得更丝滑的滚动体验和3D效果,另外也额外封装了ViewPager多次请求和普通的列表选择器。因为我只是个菜菜,所以我以为除了我和我的同事以外不会有人使用这个库,但偶尔会有一些小伙伴给我留言,有人使用还是蛮开心的,所以用摸鱼的时间迭代了一个版本,主要是增加了数据无限循环的功能。

实现过程

秉持着不要重复造轮子的原则,打开百度,搜索:RecyclerView实现无限数据循环,点开第一篇文章:Android无限循环RecyclerView的完美实现方案
在这篇文章里,提供了两种实现方式:

第一种实现方法:修改Adapter

  1. )在RecyclerView的Adapter中,重写getItemCount()方法返回Integer.MAX_VALUE。
  2. )重写onBindViewHolder()方法,对position参数取余运算,拿到position对应的真实数据索引
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    int index = position % dataList.size();
    holder.bind(dataList.get(index));
}

优点:实现比较简单
缺点:RecyclerView只能往下无限滑动,并不能往上无限滑动,参考文章也给出了解决方案,在初始化RecyclerView的时候,让其滑动到指定位置,如 Integer.MAX_VALUE/2,但仍有边界和滑动卡顿的问题

第二种实现方法:自定义LayoutManager

众所周知,RecyclerView.LayoutManager是RecyclerView中最重要的一个组件,负责以下任务:

  1. )定义子视图的位置和大小:LayoutManager决定RecyclerView中每个子视图的位置、大小和布局。通过调用LayoutManager中的方法,可以根据自己的需求自定义子视图的排列方式,如线性排列、网格排列、瀑布流排列等。
  2. )管理子视图的回收和复用:LayoutManager负责子视图的回收和复用,可以根据需要动态添加或移除子视图。这一特性可以大幅度减少内存的使用,提高应用程序的性能。
  3. )处理RecyclerView中的触摸和滚动事件:LayoutManager还可以处理RecyclerView中的触摸和滚动事件,使得RecyclerView可以响应用户的操作。

这些功能正是我们需求的,所以自定义LayoutManager是正道。

实现思路如下:
首先明确我们的需求:我们目的是做一个无限循环的滚轮控件,滚轮每一项高度固定,列表是n个滚轮项的高度,(也就是RecyclerView的列表项高度固定,RecyclerView的高度是n个列表项的高度)

  1. RecyclerView和其他的自定义ViewGroup一样,都会经过测量布局步骤,然后把列表项作为子View添加到RecyclerView中,滑动的话也是移动的子view的位置,我们完全可以通过Recycler.getChildAt()获取存活所有的列表项,然后根据每个列表项的getTop()和getBottom()方法判断这个列表项是否在屏幕内。
  2. 在onLayoutChildren方法中初始化布局,我们通过RecyclerView的高度和列表项的高度计算出RecyclerView可以填充几个列表项,然后填充列表项。
    onLayoutChildren是RecyclerView.LayoutManager类的一个方法,用于测量和布置RecyclerView中的项目。它通常在以下情况下被调用:
    1.) 当RecyclerView首次设置LayoutManager时:在设置LayoutManager时, RecyclerView会调用onLayoutChildren以获取初始布局。
    2.) 当RecyclerView的数据集发生更改时:当RecyclerView的数据集更改(例如添加、删除或移动项目)时,RecyclerView会调用onLayoutChildren来重新布局所有项目。
    3.) 当RecyclerView的尺寸发生更改时:当RecyclerView的尺寸更改时(例如,用户更改了屏幕方向或调整了RecyclerView的大小),RecyclerView会调用onLayoutChildren来重新布局所有项目。
    在这些情况下,onLayoutChildren方法都会被调用以确保RecyclerView中的项目能够正确地显示和布局。
  3. 当用户手动滑动或者 调用RecyclerView的scrollXXX()方法时,LayoutManager的scrollVerticallyBy方法会被调用,我们可以获取到需要滑动的距离,通过第一个列表项的getTop()和最后一个列表项的getBottom()判断是否滑动到头或者滑动到底,如果是就创建新的子view并添加到RecyclerView(如果向上滑动就按数据源倒叙添加列表项,如果向下滑动就按数据源正序添加列表项),这样就能实现无限滑动。当然,滑动出屏幕的子view肯定需要被回收和移除。
  4. 关于创建列表项:我们肯定要通过RecyclerView.getViewForPosition(i)方法创建,该方法最后是调用Adapter.onCreateViewHolder()方法创建,这样我们就可以创建多个adapterPosition 相同的列表项,并且也能通过RecyclerView.getPosition(@NonNull View view)方法获取正确的adapterPosition

原文章的几个问题:

  1. 原文每次只添加一个列表项,当用户滑动的非常快的时候(或者我们通过scrollxxx方法滑动时),会发生这种情况:一次的滑动距离比一个列表项的高度要大,这样就会出现无意义的空白,所以我们要判断一次的滑动距离是否比一个列表项的高度大,如果是,我们需要按照滑动方向正序或倒叙添加多个列表项
  2. 原文没有提供滑动到指定位置列表项的方法,因为我们做的是滚轮组件,所以这个是必须的,解决方法也很简单:先获取屏幕内的所有可见列表项,然后获取正中间的列表项,通过getPosition()方法获取该列表项的adapterPosition,对比要滑动到的位置,向上或向下滑动差值个数的列表项距离即可。

我们按照实现思路和解决问题的方法,修改原文章中的代码,就可以很轻松的实现我们想要的效果,因为比较简单,所以这里不再贴出代码,有需要的可以去github查看:VerticalLooperLayoutManager 无限数据循环的LayoutManager
最终看一下实现的效果:

数据无限循环的RecyclerView

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
C++ std::map迭代器是用来遍历map容器中的元素的工具。迭代器可以指向map容器中的每个键值对,允许访问键和值。在C++中,有三种常用的迭代器类型:基础迭代器、反向迭代器和常量迭代器。 基础迭代器是通过`begin()`和`end()`函数来获取的。`begin()`函数返回指向map容器中第一个元素的迭代器,而`end()`函数返回指向map容器中最后一个元素之后位置的迭代器。基础迭代器可以使用自增运算符`++`来遍历map容器中的键值对,并通过解引用(*)操作符来获得键和值。例如,使用`map<string, int>::iterator`类型定义迭代器,并通过循环遍历map容器中的键值对,可以实现对map容器的遍历。 反向迭代器是通过`rbegin()`和`rend()`函数来获取的。`rbegin()`函数返回指向map容器中最后一个元素的迭代器,而`rend()`函数返回指向map容器中第一个元素之前位置的迭代器。反向迭代器可以使用自减运算符`--`来遍历map容器中的键值对,并通过解引用(*)操作符来获得键和值。反向迭代器可以实现对map容器的反向遍历。 常量迭代器是通过`cbegin()`和`cend()`函数来获取的。常量迭代器与基础迭代器类似,但是不能修改map容器中的元素。常量迭代器可以用于在不改变map容器的情况下遍历元素。 综上所述,C++ std::map迭代器是用于遍历map容器中的键值对的工具。基础迭代器、反向迭代器和常量迭代器分别用于正向遍历、反向遍历和只读遍历map容器。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C++ std::map用迭代器遍历删除遇到的坑](https://blog.csdn.net/zidian666/article/details/125673395)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [C++map迭代器最全最容易理解](https://blog.csdn.net/qq_53547805/article/details/122146122)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值