尧图精选

NativeScript ListView 实战指南:items 绑定、itemTemplate 多模板与 itemTap / loadMoreItems 事件完整解析

🕒 发布时间:2026/10/1 9:33:36 📁 来源:尧图网络
【免费下载链接】NativeScript⚡ Write Native with TypeScript ✨ Best of all worlds (TypeScript, Swift, Objective C, Kotlin, Java, Dart). Use what you love ❤️ Angular, React, Solid, Svelte, Vue with: iOS (UIKit, SwiftUI), Android (View, Jetpack Compose), Flutter and you name it compatible.项目地址https://gitcode.com/gh_mirrors/na/NativeScript点击查看免费下载ListView 是 NativeScript 中最常用的数据展示组件之一它以虚拟化列表的形式渲染任意长度的数据集合并在底层直接映射到 Android 的android.widget.ListView与 iOS 的UITableView。本篇指南以仓库中的官方示例文档 list-view.md 为主线逐项讲解如何在 XML 与 TypeScript 中完成 items 数据绑定、itemTemplate 模板定义、itemTemplateSelector 多模板选择、itemTap 与 loadMoreItems 事件处理并结合 nativescript/core 的 ListView 源码 与 自动化测试用例 揭示其底层实现原理。读完本文你将能够独立完成一个具备模板切换、数据刷新与分页加载能力的原生列表页面。引入 ListView 模块使用 ListView 前需要先引入 ListView 模块。在当前版本的 NativeScript 中推荐直接从nativescript/core引入import { ListView } from nativescript/core;在文档对应的自动化测试用例 list-view-tests.ts 中正是通过该方式引入ListView以及ObservableArray、Label、ItemEventData等辅助类型。仓库内 packages/core/ui/list-view/Readme.md 也保留了一份早期的 CommonJS 风格用法供参考var lvm require(ui/list-view);var lv new lvm.ListView(); lv.items data; lv.on(lvm.ListView.itemLoadingEvent, function (args) { var label args.view; if (!label) { label new lm.Label(); args.view label; } label.text Item args.index; });说明require(ui/list-view)属于历史写法。在新版本中模块路径统一收敛为nativescript/core本文后续代码均采用新写法。将 ListView 的 items 属性绑定到视图模型中的集合在 XML 中声明一个 ListView并通过数据绑定双花括号语法将其items属性绑定到视图模型view-model中的集合属性Page ListView items{{ myItems }} / /Page对应的视图模型只需暴露一个数组或ObservableArray类型的myItems属性。自动化测试目录下的 list-view-view-model.ts 给出了一个典型的视图模型结构它继承自Observable在构造函数中生成 50 条{ id, text, shortText }结构的数据并放入items数组页面中的ListView items{{ items }} /即可直接消费。从源码看items属性的valueChanged回调见 list-view-common.ts做了两件关键事情当新值是一个Observable实例时通过addWeakEventListener监听ObservableArray.changeEvent并将_onItemsChanged内部调用refresh()注册为变更回调——这正是ObservableArray能自动刷新列表的机制来源无论新旧值是什么都会调用一次target.refresh()强制重建数据视图。在 XML 中绑定 itemTap 事件处理器itemTap事件在用户点击列表中的某一项时触发。在 XML 中通过事件名绑定函数即可Page ListView items{{ myItems }} itemTaplistViewItemTap / /Page对应的事件处理器定义在页面的 code-behind 文件中接收的参数对象带有index被点击项的索引等字段。测试文件中给出的原型如下见 list-view-tests.tsexport function listViewItemTap(args) { var itemIndex args.index; console.log(itemIndex); }args的实际类型是ItemEventData除了index外还包含view被点击项对应的视图等字段详见下文“响应其他事件”一节。在 XML 中绑定 loadMoreItems 事件处理器loadMoreItems事件在 ListView 滚动到最后一个条目可见时触发专用于实现“滚动到底部追加数据”的无限滚动 / 分页加载场景。XML 中绑定方式与 itemTap 一致Page ListView items{{ myItems }} loadMoreItemslistViewLoadMoreItems / /Page处理器示例见 list-view-tests.tsfunction listViewLoadMoreItems(args) { // 在此扩展绑定到 ListView 的集合追加更多数据 }定义 itemTemplate 属性itemTemplate定义列表中每一项的 UI 结构。它既可以写在ListView.itemTemplate子元素中也可以作为字符串属性传入编程方式。XML 写法Page ListView items{{ myItems }} ListView.itemTemplate Label text{{ title || Downloading... }} textWraptrue classtitle / /ListView.itemTemplate /ListView /Page模板内部是普通的 NativeScript 视图树每个数据项会成为该模板的bindingContext因此模板内可以直接用{{ title }}之类的绑定表达式访问数据项的字段。示例中的text{{ title || Downloading... }}展示了带默认值的绑定写法当数据项的title为空时显示“Downloading...”。源码层面itemTemplate属性见 list-view-common.ts的valueChanged同样触发refresh()这意味着运行时修改itemTemplate会立即重建所有行。而_defaultTemplate.createView会调用Builder.parse(this.itemTemplate, this)将模板字符串解析为视图见 list-view-common.ts。编程方式设置 itemTemplate除了 XML也可以在代码中通过字符串或工厂函数设置模板。自动化测试覆盖了三种方式见 list-view-tests.ts// 字符串模板每个数据项通过 $value 访问 listView.itemTemplate Label idtestLabel text{{ $value }} /; listView.items [1, 2, 3]; // 工厂函数模板 listView.itemTemplate () { var label new Label(); label.id testLabel; label.bind({ sourceProperty: $value, targetProperty: text, twoWay: false }); return label; };测试还验证了模板内可使用绑定表达式与转换器例如text{{ $value, $value some static text }}以及通过Application.getResources()注册的全局转换器text{{ date, date | dateConverter(DD.MM.YYYY) }}见 list-view-tests.ts。注意此时items数组中的每个元素会被直接注入模板元素本身即绑定上下文这与绑定到视图模型属性的场景一致。在 XML 中定义多模板与 itemTemplateSelector 表达式当列表中存在多种行样式时可以使用itemTemplates声明多个具名模板并用itemTemplateSelector为每个数据项选择模板。itemTemplateSelector可以直接在 XML 中写表达式表达式的上下文this就是每一行的数据项Page ListView items{{ myItems }} itemTemplateSelectorage 18 ? green : red ListView.itemTemplates template keygreen Label text{{ age }} style.backgroundColorgreen / /template template keyred Label text{{ age }} style.backgroundColorred / /template /ListView.itemTemplates /ListView /Page关键点template keyxxx中的key是模板的唯一标识itemTemplateSelector表达式返回值必须与某个key匹配表达式基于“当前行数据项”求值因此可以直接使用age、title等数据字段从源码看字符串形式的itemTemplateSelector会被包装成真正的选择函数内部创建一个临时的Label_itemTemplateSelectorBindable把表达式作为绑定表达式、把数据项作为bindingContext通过bind()求出templateKey并在求值前把行索引写入item[$index]见 list-view-common.ts。模板解析与兜底逻辑_getItemTemplate的实现见 list-view-common.ts展示了选择流程与兜底规则默认模板键为default若设置了itemTemplateSelector则取出对应行的数据项调用选择函数得到templateKey在_itemTemplatesInternal中查找key匹配的模板并返回若找不到选择器返回了不存在的 key回退到数组第一个模板即默认模板。自动化测试test_ItemTemplateSelector_WhenWrongTemplateKeyIsSpecified_TheDefaultTemplateIsUsed见 list-view-tests.ts专门验证了这一兜底行为当选择器返回wrong这个不存在的 key 时界面渲染的是默认模板的内容。在 code-behind 文件中指定模板选择函数当模板选择逻辑较复杂、无法用单个表达式表达时可以把它写成 code-behind 中的函数。该函数接收三个参数当前数据项item、行索引index、ListView 的整个 items 集合并根据这些信息返回要使用的模板key。XML 中通过函数名引用Page ListView items{{ myItems }} itemTemplateSelectorselectItemTemplate ListView.itemTemplates template keygreen Label text{{ age }} style.backgroundColorgreen / /template template keyred Label text{{ age }} style.backgroundColorred / /template /ListView.itemTemplates /ListView /Page测试文件中给出的完整函数签名见 list-view-tests.tsexport function selectItemTemplate(item: Item, index: number, items: ArrayItem) { return item.age % 2 0 ? red : green; }从源码看itemTemplateSelector的 setter 会区分输入类型见 list-view-common.ts字符串包装成上述基于绑定表达式的闭包函数函数直接赋值使用。因此两种写法最终都以(item, index, items) string的统一形式参与模板解析。测试test_ItemTemplateSelector_IsCorrectlyUsedAsAFunction见 list-view-tests.ts验证了函数形式的选择结果。使用 $index 实现交替行颜色斑马纹选择器表达式中可以使用特殊值$index表示行索引从而轻松实现基于行号的样式分支例如经典的交替行颜色Page ListView items{{ myItems }} itemTemplateSelector$index % 2 0 ? even : odd ListView.itemTemplates template keyeven Label text{{ age }} style.backgroundColorwhite / /template template keyodd Label text{{ age }} style.backgroundColorgray / /template /ListView.itemTemplates /ListView /Page$index的注入位置在 list-view-common.ts包装闭包在每次求值前执行item[$index] index把当前行索引挂到数据项上随后绑定表达式即可读取。测试test_ItemTemplateSelector_IsCorrectlyParsedFromString见 list-view-tests.ts通过age % 2 0 ? red : green验证了字符串选择器按数据项正确求值。以编程方式创建 ListViewListView 也可以在 TypeScript 中直接创建并添加到视图树var listView new ListView();测试test_default_TNS_values见 list-view-tests.ts验证了新建 ListView 的默认状态listView.items为undefined。创建后即可设置items、itemTemplate并通过on方法监听itemLoading事件来构建行视图见下文。结合 Array 使用 ListViewitemLoading 事件与 refresh()itemLoading 事件构建行视图当items为普通数组时ListView 通过itemLoading事件为每个即将显示的行创建 UI。事件参数args上的index表示行索引view表示当前行的视图若view为空则需要自行创建视图对象并赋回给args.view最后在该视图上填充数据var colors [red, green, blue]; listView.items colors; listView.on(ListView.itemLoadingEvent, function (args) { if (!args.view) { // 视图尚未创建则创建 Label args.view new Label(); } (Labelargs.view).text colors[args.index]; });这段原型来自 list-view-tests.ts。需要说明的是itemLoading是“按需调用”的列表滚动时只对进入可视区域的项触发配合原生控件自身的回收复用机制ListViewBase.prototype.recycleNativeView auto见 list-view-common.ts保证长列表的性能。测试test_set_items_to_array_loads_all_items通过断言index 0/1/2均被触发验证了数组前 3 项都能正确渲染。修改数组不会自动更新 UI注意ListView 显示后直接修改普通数组如push新元素界面不会自动更新。此时需要手动调用refresh()方法强制刷新colors.push(yellow); // 手动触发更新新颜色才会显示 listView.refresh();对应的测试test_refresh_after_adding_items_to_array_loads_new_items见 list-view-tests.ts验证了 push 之后调用refresh()原生视图数量会从 3 变为 4。test_refresh_reloads_all_items见 list-view-tests.ts进一步验证refresh()会为每个条目重新触发itemLoading即整表重载。另一个有用的行为是将items置为null或undefined会清空所有原生行见test_set_itmes_to_null_clears_native_items与test_set_itmes_to_undefiend_clears_native_items。结合 ObservableArray 使用 ListView自动更新ObservableArray是Array的可观察版本。当items指向ObservableArray时列表会在元素增删时自动刷新无需手动调用refresh()var colors new ObservableArray([red, green, blue]); listView.items colors; listView.on(ListView.itemLoadingEvent, function (args) { if (!args.view) { args.view new Label(); } (Labelargs.view).text colors.getItem(args.index); });随后直接操作数组即可colors.push(yellow); // ListView 会自动更新上述两段代码分别对应测试中的article-listview-observablearray与article-push-in-observablearray片段见 list-view-tests.ts。测试test_add_to_observable_array_refreshes_the_listview验证 push 后原生视图数自动变为 4test_remove_from_observable_array_refreshes_the_listview见 list-view-tests.ts验证pop()后自动变为 2test_splice_observable_array_refreshes_the_listview见 list-view-tests.ts验证splice(0, 2, d, e, f)这类复合变更同样被正确处理。自动更新的底层机制在 list-view-common.tsitems属性通过弱事件监听器订阅ObservableArray.changeEvent任何增删改都会回调_onItemsChanged并执行refresh()。使用弱事件监听addWeakEventListener则是为了避免 ListView 与数据源之间形成强引用导致的内存泄漏测试中的test_no_memory_leak_when_items_is_observable_array见 list-view-tests.ts专门覆盖了这一场景。响应其他事件编程监听 itemTap 与 loadMoreItems除了在 XML 中声明式绑定也可以在代码中通过on方法监听事件。ListView 支持的全部事件在 list-view-common.ts 中定义事件静态常量事件名字符串触发时机ListView.itemLoadingEventitemLoading某个条目需要构建 UI 时ListView.itemTapEventitemTap点击列表中的条目时ListView.loadMoreItemsEventloadMoreItems滚动到最后一个条目可见时ListView.searchChangeEventsearchChange搜索文本变化时编程监听 itemTaplistView.on(ListView.itemTapEvent, function (args: ItemEventData) { var tappedItemIndex args.index; var tappedItemView args.view; // 处理点击逻辑 });ItemEventData提供index被点击行索引与view被点击行的视图。测试test_nativeTap_is_raised见 list-view-tests.ts通过performNativeItemTap模拟原生点击并断言事件确实触发、args.index为 1。ItemEventData中还带有平台原生对象字段测试test_set_native_item_exposed见 list-view-tests.ts在 iOS 上断言args.ios instanceof UITableViewCell在 Android 上断言args.android instanceof android.view.ViewGroup。编程监听 loadMoreItemslistView.on(ListView.loadMoreItemsEvent, function (data: EventData) { // 滚动到底部时追加数据 });loadMoreItems的设计意图是当用户滚动到列表末尾时向数据源追加更多条目实现“无限滚动”式分页加载。相关测试确认了其触发条件test_loadMoreItems_raised_when_showing_few_items见 list-view-tests.ts条目不足以填满视口时加载完成后会触发一次loadMoreItemstest_loadMoreItems_not_raised_when_showing_many_items见 list-view-tests.ts条目足够多时不会误触发test_loadMoreItems_is_raised_when_scroll_to_last_item见 list-view-tests.ts调用listView.scrollToIndex(最后一项索引)滚动到底后事件必然触发。补充常用属性与进阶能力除文档重点覆盖的内容外从 ListView 类型定义 与公共实现 list-view-common.ts 中还可以确认以下常用属性均可在 XML 或代码中使用rowHeight行高默认auto见 list-view-common.ts。设置固定值可显著提升长列表的滚动性能。iosEstimatedRowHeightiOS 估算行高类型定义注释标明默认约 44px见 index.d.ts。separatorColor分割线颜色对应 CSS 属性separator-color见 list-view-common.ts。itemIdGenerator为条目生成稳定 ID 的函数默认以索引为 ID见 list-view-common.ts。sectioned与stickyHeader/stickyHeaderTemplate/stickyHeaderHeight分组分节列表与吸顶标题支持。开启sectioned后items应为形如{ title: Section A, items: [...] }的对象数组见 index.d.ts。测试文件后半部分list-view-tests.ts包含 iOS 吸顶标题、Android 吸顶标题内边距、以及分节列表下itemTemplateSelector逐节解析的完整验证。showSearch/searchAutoHide/iosSearchInsetBehavioriOS 原生UISearchController搜索集成相关选项见 list-view-common.ts。方法refresh()强制重载全部条目、scrollToIndex(index)/scrollToIndexAnimated(index)滚动到指定索引、isItemAtIndexVisible(index)判断条目是否可见。小结围绕官方示例文档本文完整还原了 NativeScript ListView 的核心用法从 XML 声明与 items 绑定、itemTap / loadMoreItems 事件绑定到 itemTemplate 单模板、itemTemplates itemTemplateSelector 多模板表达式与函数两种形式与$index交替行颜色再到代码方式创建 ListView、普通数组的refresh()手动刷新与ObservableArray的自动更新。通过对照 list-view-common.ts 的公共实现与 list-view-tests.ts 的自动化用例可以确认每个行为都有源码级依据items属性通过弱事件监听订阅ObservableArray变更、字符串选择器经绑定表达式求值、未知模板 key 回退默认模板等。以此为起点你可以进一步探索 Android 平台实现 与 iOS 平台实现深入理解两个原生控件android.widget.ListView/UITableView上的适配细节。赞分享【免费下载链接】NativeScript⚡ Write Native with TypeScript ✨ Best of all worlds (TypeScript, Swift, Objective C, Kotlin, Java, Dart). Use what you love ❤️ Angular, React, Solid, Svelte, Vue with: iOS (UIKit, SwiftUI), Android (View, Jetpack Compose), Flutter and you name it compatible.项目地址https://gitcode.com/gh_mirrors/na/NativeScript点击查看免费下载相关推荐TP6-Vue-Admin基于ThinkPHP 6与Vue 2的完整前后端分离后台方案TP6 Vue Admin基于ThinkPHP 6与Vue 2的完整前后端分离后台方案 TP6 Vue Admin 是一套完整的前后端分离后台管理方案基于NativeScript Xml 模块实战基于 SAX 事件回调解析 XMLnativescript/core/xmlNativeScript Xml 模块实战基于 SAX 事件回调解析 XMLnativescript/core/xml 本文围绕 NativeScripNgRx Platform 深入解析LetDirective*ngrxLet响应式模板绑定的完整实战指南NgRx Platform 深入解析LetDirective ngrxLet响应式模板绑定的完整实战指南 本文基于 NgRx Platform 仓库的官方前端状态管理上一篇Android WeakHandler 项目推荐下一篇Anonymous Github与Docker集成容器化部署最佳实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →