新闻详情

新闻详情

首页 / 资讯中心 / 详情

Litho RecyclerCollectionComponent 实战:基于 Sections 构建高性能 RecyclerView 列表

发布时间:2026/9/29 8:50:55来源:尧图网络
Litho RecyclerCollectionComponent 实战:基于 Sections 构建高性能 RecyclerView 列表
移动开发UI组件【免费下载链接】lithoA declarative framework for building efficient UIs on Android.项目地址https://gitcode.com/gh_mirrors/li/litho点击查看免费下载本指南面向使用 Litho 的 Java codegen 版 Sections API 的开发者系统讲解RecyclerCollectionComponent的创建方式、内置能力与关键配置。读完你将掌握如何把 Section 树快速接入RecyclerView、如何配置横向列表与网格、如何启用列表吸附Snapping、如何控制横向列表高度、如何开启/关闭下拉刷新以及如何通过 loading/empty/error 三个插槽完成数据加载状态的 UI 呈现。文中所有结论均以当前仓库源码litho-sections-widget、litho-widget为事实依据。:::caution 适用范围 本页讲解的是较老的 Java codegen 版 Sections API。如果你在 Kotlin 中新建列表应优先参考 Lazy Collection 文档。 :::RecyclerView是任何包含可滚动列表的 Android 应用的基础构件之一。Litho 官方推荐使用RecyclerCollectionComponent配合 Sections 基础教程 来轻松构建滚动列表。这套 API 既能支撑简单、同质化的 List也能支撑由多个数据源驱动的复杂、异构 List同时天然享受 Litho 的后台布局background layout与增量挂载incremental mount等特性。下文将逐一讲解RecyclerCollectionComponent的常用能力横向列表、网格、吸附、固定高度以及加载/空/错误三种状态的界面定制。创建一个 RecyclerCollectionComponent和任何其他组件一样RecyclerCollectionComponent通过 Builder 构建并作为子组件加入布局OnCreateLayout static Component onCreateLayout( final ComponentContext c) { return RecyclerCollectionComponent.create(c) .section(createSection()) .build(); }上述代码最终会渲染为一个RecyclerView其行rows由传入的section内容驱动。从源码实现看section是RecyclerCollectionComponentSpec.onCreateLayout中唯一的必填 prop其余所有能力均为可选 prop见 RecyclerCollectionComponentSpec.java。组件内部会把section作为根节点设置到一棵SectionTree上sectionTree.setRoot(section)并通过SectionBinderTarget把 Section 树产生的 changeset 交给底层的RecyclerBinder渲染。Batteries Included开箱即用的内置能力RecyclerCollectionComponent为列表场景内置了许多实用特性。以下是其全部可选 prop 及其默认值均来自 RecyclerCollectionComponentSpec.javaProp默认值说明recyclerConfigurationListRecyclerConfiguration.create().build()决定 LayoutManager 与 Binder 的配置对象nestedScrollingEnabledtrue是否允许嵌套滚动scrollBarStyleView.SCROLLBARS_INSIDE_OVERLAY滚动条样式overScrollModeView.OVER_SCROLL_ALWAYS过度滚动行为itemAnimatorNoUpdateItemAnimator条目动画需移除变化动画时保留默认值即可clipToPadding/clipChildrentrue/true裁剪行为incrementalMounttrue增量挂载开关refreshProgressBarColor0XFF4267B2蓝色下拉刷新进度条颜色isLeft/Right/Top/BottomFadingEnabledtrue四个方向的淡出边缘开关asyncStateUpdates/asyncPropUpdates/setRootAsyncfalse异步状态/属性更新开关此外还支持itemDecoration可多值、left/right/top/bottomPadding、fadingEdgeLength、edgeEffectFactory、recyclerViewId、recyclerContentDescription、touchInterceptor、itemTouchListener、onScrollListener可多值、loadEventsHandler、recyclerTouchEventHandler、sectionsViewLogger等 prop。它们会被直接透传给内部的Recycler组件。横向列表Horizontal ListsRecyclerCollectionComponent通过RecyclerConfigurationprop 决定使用哪种 LayoutManager。若未指定该 prop默认使用ListRecyclerConfiguration——它会创建一个垂直方向的LinearLayoutManager。要实现横向布局传入一个水平方向的ListRecyclerConfiguration即可final RecyclerCollectionComponentSpec.RecyclerConfiguration recyclerConfiguration new ListRecyclerConfiguration( LinearLayoutManager.HORIZONTAL, false /* reverse layout */); final Component component RecyclerCollectionComponent.create(c) .section(FooSection.create(new SectionContext(c)).build()) .recyclerConfiguration(recyclerConfiguration) .build();从 ListRecyclerConfiguration.java 源码可知Builder 的字段默认值为orientation LinearLayoutManager.VERTICAL、reverseLayout false、stackFromEnd false、snapMode SNAP_NONE。也就是说不传 orientation 时默认为垂直列表reverseLayout(true)会反转布局方向stackFromEnd(true)让内容从末尾堆叠如聊天列表从底部开始还可以通过linearLayoutInfoFactory(...)注入自定义的LinearLayoutInfo工厂。网格列表Grid Lists网格列表使用GridRecyclerConfiguration创建它会为RecyclerView生成一个GridLayoutManagerGridRecyclerConfiguration.create() .orientation(LinearLayoutManager.VERTICAL) .numColumns(BOOKMARKS_GRID_NUM_COLUMNS) .recyclerBinderConfiguration(RecyclerBinderConfiguration.create().build()) .build();对照 GridRecyclerConfiguration.java其 Builder 默认值包括orientation VERTICAL、numColumns 2、reverseLayout false、stackFromEnd false、allowMeasureOverride false、snapMode SNAP_NONE。常用的配置项还有reverseLayout(boolean)/stackFromEnd(boolean)布局方向与堆叠方式allowMeasureOverride(boolean)是否允许 Grid 测量被覆盖透传给GridLayoutInfogridLayoutInfoFactory(...)自定义GridLayoutInfo工厂snapMode(...)/snapHelper(...)与吸附相关见下文。吸附Snapping对于可横向滚动的列表吸附模式同样通过ListRecyclerConfiguration配置final RecyclerCollectionComponentSpec.RecyclerConfiguration recyclerConfiguration new ListRecyclerConfiguration( LinearLayoutManager.HORIZONTAL, false /* reverse layout */, SNAP_TO_START); final Component component RecyclerCollectionComponent.create(c) .section(FooSection.create(new SectionContext(c)).build()) .recyclerConfiguration(recyclerConfiguration) .build();其他可用的吸附选项还有SNAP_NONE、SNAP_TO_END和SNAP_TO_CENTER。这些常量定义在 SnapUtil.kt常量取值对应的 SnapHelperSNAP_NONEInt.MIN_VALUE无nullSNAP_TO_STARTLinearSmoothScroller.SNAP_TO_STARTStartSnapHelper支持 fling offset 与起始偏移SNAP_TO_ENDLinearSmoothScroller.SNAP_TO_END无nullSNAP_TO_CENTERInt.MAX_VALUEPagerSnapHelper翻页式吸附从源码看SNAP_TO_END与SNAP_NONE都不会挂载 SnapHelper也就是说SNAP_TO_END在ListRecyclerConfiguration下实际等同于关闭吸附而SNAP_TO_CENTER会启用PagerSnapHelper表现接近一页一页的翻页效果。另外仓库还额外提供了SNAP_TO_CENTER_CHILDLinearSnapHelper吸附到子项中心与SNAP_TO_CENTER_CHILD_WITH_CUSTOM_SPEED自定义速度的 LinearSnapHelper并通过SnapUtil.getSnapHelper(snapMode, deltaJumpThreshold, snapToStartFlingOffset, snapToStartOffset, isStrictMode)统一构建。纵向列表的限制ListRecyclerConfiguration的校验逻辑validate 方法规定垂直方向只允许SNAP_NONE、SNAP_TO_START、SNAP_TO_CENTER否则抛出UnsupportedOperationException(Only snap to start is implemented for vertical lists)GridRecyclerConfiguration同样限制垂直网格只能使用SNAP_NONE或SNAP_TO_START。此外ListRecyclerConfiguration.Builder还暴露了若干精细调参项deltaJumpThreshold(int)硬滑动时的跳跃阈值默认Integer.MAX_VALUEsnapToStartFlingOffset(int)StartSnapHelper的 fling 偏移默认SNAP_TO_START_DEFAULT_FLING_OFFSET 1snapToStartOffset(int)吸附到起始位置的偏移默认0isStrictMode(boolean)控制轮播式 H-Scroll 行为——开启时硬滑动从卡片 N 跳到 Nxx 为deltaJumpThreshold关闭时跳到 Nx1默认falsesnapHelper(SnapHelper)直接注入自定义SnapHelper优先级高于snapMode。设置横向 RecyclerCollectionComponent 的高度横向滚动的RecyclerCollectionComponent有三种设置高度的方法完整讲解见 Horizontal Scrolling and Measurement固定高度法Fixed height在 H-Scroll 组件上直接设置固定高度。这是性能最优的方法建议尽可能采用。未知高度法Unknown height创建组件时高度未知让 H-Scroll 以第一个 item 的高度作为自身高度。动态高度法Dynamic height让 H-Scroll 动态改变高度以适配最高的 item。这是性能最差的方法。固定高度法——通过heightprop 设置固定高度RecyclerCollectionComponent.create(c) .recyclerConfiguration( ListRecyclerConfiguration.create() .orientation(OrientationHelper.HORIZONTAL) .build()) .section(DataDiffSection.createInt(SectionContext(c)) .data(colors) .renderEventHandler(FixedHeightHscrollComponent.onRender(c)) .build()) .heightDip(150f) .build()子项最多以 H-Scroll 的高度被测量更高的子项会被裁剪更矮的子项则对齐在起始位置未知高度法——不传heightprop而是通过RecyclerBinderConfig的crossAxisWrapMode设置为CrossAxisWrapMode.MatchFirstChild让 H-Scroll 以第一个子项的高度自我测量RecyclerCollectionComponent.create(c) .recyclerConfiguration( ListRecyclerConfiguration.create() .orientation(OrientationHelper.HORIZONTAL) .recyclerBinderConfiguration( RecyclerBinderConfiguration.create() .recyclerBinderConfig( RecyclerBinderConfig( crossAxisWrapMode CrossAxisWrapMode.MatchFirstChild)) .build()) .build()) .section(...) .build()该测量只在 H-Scroll 首次测量时发生之后高度不可改变其他子项高度以 H-Scroll 高度为上限并定位在起始位置⚠️ 如果既不在RecyclerCollectionComponent上设置非零高度、也不修改crossAxisWrapMode其默认值是CrossAxisWrapMode.NoWrapH-Scroll 的高度最终会是0。这一点在 hscrolls.mdx 中有明确警告。动态高度法——性能最差仅在绝对必要时使用尤其应避免用于无限滚动的列表。将crossAxisWrapMode设为CrossAxisWrapMode.Dynamic后H-Scroll 的初始高度由最高的子项决定当某个子项想变得比当前高度更高时H-Scroll 会按新高度重新测量而其他子项不会被重测当最高子项收缩时H-Scroll 会重新测量所有子项来确定新高度。由于每次插入新 item 都可能触发全量重测无限加载场景下成本很高。下拉刷新Pull to RefreshRecyclerCollectionComponent默认启用下拉刷新并向底层的Recycler发送一个事件处理器用于在SectionTree上触发刷新。要禁用该功能把disablePTRprop 设为truefinal Component component RecyclerCollectionComponent.create(c) .section(FooSection.create(new SectionContext(c)).build()) .recyclerConfiguration(recyclerConfiguration) .disablePTR(true) .build();从源码RecyclerCollectionComponentSpec.java看PTR 的实际可用性由两个条件共同决定final boolean canPTR recyclerConfiguration.getOrientation() ! OrientationHelper.HORIZONTAL !disablePTR;即横向列表上默认就没有下拉刷新只有纵向列表在disablePTR false时才启用。触发刷新时会回调refreshContent(c, sectionTree, ignoreLoadingUpdates)最终调用sectionTree.refresh()若设置了ignoreLoadingUpdates且存在外部PTRRefreshEvent处理器则会先尝试派发该事件、未被处理时才回落到sectionTree.refresh()。刷新进度条颜色可通过refreshProgressBarColor默认0XFF4267B2与refreshProgressBarBackgroundColor两个ResType.COLORprop 定制。加载中、空、错误界面Loading / Empty / Error screens在 Sections API 中数据获取可以通过 loading eventsLoadingState 和 services 集成。RecyclerCollectionComponent会监听这些加载事件并做出响应通过三个 prop 指定不同数据获取阶段展示的内容loadingComponent— 数据正在加载且列表中还没有任何内容时显示emptyComponent— 数据加载完成但没有任何内容可展示时显示errorComponent— 数据加载失败且列表中没有任何内容时显示。final Component component RecyclerCollectionComponent.create(c) .section(FooSection.create(new SectionContext(c)).build()) .recyclerConfiguration(recyclerConfiguration) .loadingComponent( Progress.create(c) .build()) .errorComponent( Text.create(c) .text(Data Fetch has failed).build()) .emptyComponent( Text.create(c) .text(No data to show).build()) .build();底层行为见 RecyclerCollectionComponentSpec.java组件内部维护一个LoadingState枚举LOADING/LOADED/EMPTY/ERROR初始状态为LOADINGRecyclerCollectionLoadEventsHandler监听 Section 树冒泡上来的加载事件onLoadStarted→ 更新为LOADING若数据为空或LOADEDonLoadSucceeded→EMPTY或LOADEDonLoadFailed→ERROR或LOADED并通过updateLoadingStateAsync触发状态更新布局时loading/empty/error 组件会被包装在Wrapper中以positionType(ABSOLUTE) 全边距 0 的方式覆盖在 Recycler 之上如果处于EMPTY或ERROR状态但对应的emptyComponent/errorComponent未传入组件会直接返回null什么都不渲染loadingComponent未传入时则仅显示空列表。注意ignoreLoadingUpdates是一个临时性 workaround开启后会直接跳过 loading/empty/error 组件逻辑状态被强制设为LOADED源码注释明确警告它“会破坏 loadingComponent/errorComponent/emptyComponent”不建议常规使用。深入底层组件是如何被组织起来的要理解上述能力可以看一下createInitialStateRecyclerCollectionComponentSpec.java中的组装流程从recyclerConfiguration.getLayoutInfo(c)得到LayoutInfoLinearLayoutInfo或GridLayoutInfo基于RecyclerBinderConfiguration构建RecyclerBinder可注入stickyHeaderControllerFactory、startupLogger、poolScope、PostDispatchDrawListener列表等用SectionBinderTarget把 Binder 包装为 Section 树的 Target并据此创建SectionTree其标签tag默认取section.getSimpleName()可通过sectionTreeTag覆盖创建RecyclerCollectionEventsController内部控制器与RecyclerCollectionLoadEventsHandler并将后者注册为 SectionTree 的LoadEventsHandler注册ViewportInfo.ViewportChanged监听把视口变化首/末可见索引、首/末完全可见索引、状态转发给sectionTree.viewPortChanged(...)为 Working Ranges预取与分页 提供基础。其中RecyclerBinderConfigurationRecyclerBinderConfiguration.java本身也是一个可配置对象支持recyclerBinderConfig(RecyclerBinderConfig)透传给底层 Binder 的完整配置包含wrapContent、crossAxisWrapMode、componentsConfiguration等见上文高度一节的使用方式useBackgroundChangeSets(boolean)是否在后台线程计算 changeset默认来自SectionsConfiguration.useBackgroundChangeSets实验性 APIchangeSetThreadHandler(RunnableHandler)自定义 changeset 计算线程postToFrontOfQueueForFirstChangeset(boolean)首个 changeset 是否插队到队列最前primitiveRecyclerBinderStrategy(...)实验性的 Primitive Recycler Binder 策略。在交互层面RecyclerCollectionComponentSpec还定义了三个 Trigger 事件ScrollEvent通过OnTrigger(ScrollEvent.class)处理内部调用sectionTree.requestFocusOnRoot(position)完成滚动RecyclerDynamicConfigEvent动态调整提交策略CommitPolicyClearRefreshingEvent清除刷新状态。这也印证了文档 communicating-with-the-ui.md 中描述的滚动方式从 Section 内部触发滚动应使用SectionLifecycle.requestFocus(SectionContext, int)系列方法含requestFocusWithOffset与按sectionKey聚焦的变体且目标数据必须先通过OnDataBound就绪requestFocus才能生效。小结与延伸阅读RecyclerCollectionComponent是 Litho Sections API 中连接“声明式 Section 树”与“原生RecyclerView”的桥梁它默认帮你处理 LayoutManager 选择、后台布局、增量挂载、视口变化上报、下拉刷新与加载状态呈现同时通过RecyclerConfiguration及其内部的RecyclerBinderConfiguration保留了高度的自定义空间。如果想继续深入仓库中相关的后续阅读路径包括Sections 基础教程Sections Basics理解DiffSection、SingleComponentSection、GroupSection如何组成列表结构Horizontal Scrolling and Measurement横向列表三种高度方案的完整对比与示例本文高度一节即提炼自该页Sections 与 UI 通信Communicating with the UILoadingState加载事件、requestFocus滚动与refresh刷新Best Practices and Performance数据 diff 的性能最佳实践Services 与 Working Ranges依赖注入、预取与分页等进阶主题。核心实现源码可进一步研读 RecyclerCollectionComponentSpec.java、ListRecyclerConfiguration.java、GridRecyclerConfiguration.java 与 SnapUtil.kt。赞分享移动开发UI组件【免费下载链接】lithoA declarative framework for building efficient UIs on Android.项目地址https://gitcode.com/gh_mirrors/li/litho点击查看免费下载相关推荐Litho Sections框架构建高性能列表的最佳实践Litho Sections框架构建高性能列表的最佳实践 本文深入探讨了Litho Sections框架的核心组件和优化机制包括RecyclerCollec移动开发UI组件MimeKit终极指南.NET平台最强大的MIME创建与解析库MimeKit终极指南.NET平台最强大的MIME创建与解析库 MimeKit是一款功能全面的.NET MIME创建与解析库支持S/MIME、PGP、DKI后端终极指南如何用Android Advanced RecyclerView打造高性能聊天列表终极指南如何用Android Advanced RecyclerView打造高性能聊天列表 Android Advanced RecyclerView是一个功移动开发UI组件上一篇暗黑破坏神2高清补丁D2DX深度实测25帧老游戏如何榨出144帧附完整调优指南下一篇跑通完整流程才算真正验证learn-harness-engineering 中的端到端测试与架构边界工程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

预算编制还在用Excel手工合并?大型企业正逐步告别这种“数据苦力”模式 2026/9/29 8:50:42

预算编制还在用Excel手工合并?大型企业正逐步告别这种“数据苦力”模式

“某集团CFO在季度经营会上拿到的利润数据,与各事业部自行上报的汇总数相差8%。财务团队耗时三天排查,最终发现问题源于三家子公司对“管理费用”的归集口径不一致——有的将IT运维支出计入其中,有的则未计入。实际在同规模企业中&#xff0c…

阅读更多 →
强Agent时代,还招Agent开发? 2026/9/29 8:50:42

强Agent时代,还招Agent开发?

摘要:通用 Agent 越强,企业越在狂挂 Agent Engineer 的 JD。反直觉:强的是 demo,买的是 Agent 外的生产系统——越缺把它钉进生产的人。 常见的一幕:公司买了通用 Agent 的席位,全员能用。HR 又挂出几个 Ag…

阅读更多 →
校园点餐系统测试实战:用例设计、核心链路与踩坑记录 2026/9/29 8:50:41

校园点餐系统测试实战:用例设计、核心链路与踩坑记录

校园点餐系统看着简单,真正做测试的时候才知道坑有多深。注册登录、菜品展示、加购结算、支付退款、商家接单、骑手配送,随便一环出问题,用户当场就骂娘。尤其是校园场景,下课高峰期几千人同时下单,库存扣重、支付回调…

阅读更多 →
基于STM32单片机共享单车充电宝GPS定位太阳能锂电池充电蓝牙无线APP/WiFi无线APP/摄像头视频监控/云平台设计S517 2026/9/29 8:50:35

基于STM32单片机共享单车充电宝GPS定位太阳能锂电池充电蓝牙无线APP/WiFi无线APP/摄像头视频监控/云平台设计S517

STM32-S517-太阳能锂电池电量充电GPS定位二维码语音播报开锁计时计费临时停用启用价OLED屏声光提醒按键(无线方式选择)产品功能描述:本系统由STM32F103C8T6单片机核心板、OLED屏、(无线蓝牙/无线WIFI/无线视频监控/联网云平台模块-可选)、锂电…

阅读更多 →
基于STM32单片机手机无线充电宝锂电池电量APP摄像头蓝牙无线APP/WiFi无线APP/摄像头视频监控/云平台设计S527 2026/9/29 8:50:35

基于STM32单片机手机无线充电宝锂电池电量APP摄像头蓝牙无线APP/WiFi无线APP/摄像头视频监控/云平台设计S527

STM32-S527-无线充电宝USB输出输出电压电流功率锂电池电压电量欠压OLED屏声光提醒按键(无线方式选择)产品功能描述:本系统由STM32F103C8T6单片机核心板、OLED屏、(无线蓝牙/无线WIFI/无线视频监控/联网云平台模块-可选)、锂电池充电管理电路、…

阅读更多 →
办公开发模拟数据生成工具实测:鲲穹万能生成器功能与使用边界客观记录 2026/9/29 8:50:35

办公开发模拟数据生成工具实测:鲲穹万能生成器功能与使用边界客观记录

办公素材填充、程序测试、自媒体内容制作过程中,经常需要生成各类模拟数据、编码字符、目录文本。不同数据生成工具支持的生成类型、自定义参数、批量输出能力存在明显差别。下文客观记录鲲穹万能生成器基础能力与使用边界,本文无任何商业合作&#xff0…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉