尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Mugen源码深读②:RecyclerViewPositionHelper内部机制,为什么要自己写可见位置查找器

Mugen源码深读②:RecyclerViewPositionHelper内部机制,为什么要自己写可见位置查找器 Mugen源码深读②RecyclerViewPositionHelper内部机制为什么要自己写可见位置查找器【免费下载链接】mugenMicrolibrary for implementing infinite scroll on Android项目地址: https://gitcode.com/gh_mirrors/mu/mugenMugen 是一款用于 Android 的无限滚动infinite scroll / 上拉加载更多微库。本篇源码深读聚焦其中的RecyclerViewPositionHelper拆解它如何查找 RecyclerView 的首末可见项位置以及作者为什么放弃官方 API、自己实现一个可见位置查找器。1. Mugen 的无限滚动触发链在哪里需要可见位置在上一篇中我们看过Mugen 通过 Mugen.java 的工厂方法把功能挂到列表上Mugen.with(recyclerView, callbacks).start();start()会启动一个 Attacher它内部挂了一个滚动监听器在每次滚动时回答三个问题用户是往上滑看后面还是往下滑看前面当前能看到列表的第几个到第几项是否已经接近底部该触发onLoadMore()了判断接近底部必须知道首末可见项的位置。AbsListView的滚动回调会直接把firstVisibleItem、visibleItemCount递给你见 AbsListViewAttacher.java而 RecyclerView 不会——这正是RecyclerViewPositionHelper存在的意义。2. 为什么官方 API 不够用三个痛点 新手常见疑问RecyclerView 不是有现成的可见位置方法吗确实有但有不等于好用2.1 方法散落在各种 LayoutManager 上findFirstVisibleItemPosition()定义在LinearLayoutManager里findFirstCompletelyVisibleItemPosition()又只存在于StaggeredGridLayoutManager等子类中。不同布局管理器能力参差代码没法统一写。2.2 必须 instanceof 判断管理器类型想在任意布局下取可见位置只能写if (manager instanceof LinearLayoutManager)这样的类型判断每加一种布局就加一个分支扩展性差。2.3 RecyclerView 本体没有这类 APIRecyclerViewAttacher.java 面向的是通用的RecyclerView实例它不应该假设用户用了哪种LayoutManager。结论与其到处做类型判断不如基于子 View 的几何位置自己算一遍——这就是RecyclerViewPositionHelper的设计动机。3. 深读 RecyclerViewPositionHelper4 个公开方法共用 1 个核心循环源码位于 RecyclerViewPositionHelper.java不到 120 行结构非常清晰。3.1 工厂入口不允许空指针public static RecyclerViewPositionHelper createHelper(RecyclerView recyclerView) { if (recyclerView null) { throw new NullPointerException(Recycler View is null); } ... }构造函数是包级私有外部只能走 createHelper 工厂方法在入口处就把空指针挡掉避免滚动监听里再判一次。3.2 可见与完全可见4 个方法其实是 2 类该 Helper 对外提供 4 个查询方法方法语义findFirstVisibleItemPosition第一个沾边可见的项哪怕只露一条缝findFirstCompletelyVisibleItemPosition第一个完整露出的项findLastVisibleItemPosition最后一个沾边可见的项findLastCompletelyVisibleItemPosition最后一个完整露出的项它们只是给同一个私有方法传入不同方向与不同参数没有任何重复逻辑。3.3 findOneVisibleChild 核心算法四个方法最终都汇聚到 findOneVisibleChild算法分三步确定轴向用layoutManager.canScrollVertically()判断可竖向滚动就建垂直方向的OrientationHelper否则建水平的——竖屏、横屏、横向 ViewPager 式列表都能覆盖。确定可见范围取getStartAfterPadding()到getEndAfterPadding()之间的区间作为屏幕有效区域。遍历子 View 做区间求交if (childStart end childEnd start) { if (completelyVisible) { if (childStart start childEnd end) { return child; } else if (acceptPartiallyVisible) { partiallyVisible child; } } else { return child; } }两个细节值得品味用getDecoratedStart/End而不是普通坐标decoration 边距也被算进位置结果和屏幕上的真实观感一致。部分可见兜底要求完全可见却一个都找不到时会退而返回部分可见的项而不是直接返回NO_POSITION让调用方尽量拿到可用结果。循环方向由toIndex fromIndex推导出next ±1同一个循环既能从前往后找第一个也能从后往前找最后一个这就是4 个方法共用 1 个循环的由来。4. 从查位置到触发 onLoadMoreRecyclerViewAttacher 的完整链路回到 RecyclerViewAttacher.javaHelper 的产出在这里被消费判方向每次onScrolled都调一次findFirstVisibleItemPosition()和上一次值 比较得出 UP / DOWN / SAME枚举定义见 ScrollDirection.java。判触发只有方向为UP用户往列表尾部翻时才计算lastVisiblePosition lastAdapterPosition - mLoadMoreOffset命中则回调onLoadMore()见 触发判断代码。防抖与防重回调前还会询问MugenCallbacks的isLoading()与hasLoadedAllItems()正在加载或已全部加载完就直接跳过避免一次滚动触发多次请求。默认的提前触发距离由 BaseAttacher 的 DEFAULT_LOAD_OFFSET 2 给出并可通过setLoadMoreOffset调整。 一个容易忽略的点Helper 在 构造函数里一次性缓存了 LayoutManager而滚动监听会被高频调用——把不变的引用缓存下来是滚动路径上很划算的微优化。5. 工程智慧一个 Helper 抹平了两种列表体系对比两个 Attacher 你会发现一条清晰的对称设计AbsListView 路线RecyclerView 路线位置信息来源系统回调直接给出RecyclerViewPositionHelper自己查方向判定逻辑完全一致完全一致触发条件逻辑完全一致完全一致也就是说方向判定 触发判定这套与平台无关的策略全部沉淀在 BaseAttacher.java 里而平台差异被压缩到怎么拿到可见位置这一个点上由 Helper 吸收掉。一个类让同一个触发策略同时服务 ListView/GridView 与任意 LayoutManager 的 RecyclerView这正是 Mugen一个库通吃多种列表的底层支撑。官方 Demo 中 RecyclerView 的完整用法可参考 ReposFragment.java。6. 小结新手能抄走的 3 个设计点用几何位置代替 API 假设不绑定具体 LayoutManager 类型用子 View 的装饰区间与屏幕区间求交天然适配所有布局。单一核心循环 参数化方向一次遍历实现第一/最后 × 可见/完全可见四种查询代码量小且不易出错。防御性兜底完全可见查不到就退回部分可见、入口处空指针快速失败让高频滚动路径始终有稳定输出。提示仓库 README 中说明该项目已停止开发正式项目可优先考虑官方的 Paging 方案但 Mugen 这套可见位置查找的思路对理解 RecyclerView 内部机制依然是一份极佳的小样本。想获取源码可执行git clone https://gitcode.com/gh_mirrors/mu/mugen【免费下载链接】mugenMicrolibrary for implementing infinite scroll on Android项目地址: https://gitcode.com/gh_mirrors/mu/mugen创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表