☰
NativeScript ListPicker 完整指南:模块引入、数据绑定与编程式选中
2026/10/1 2:11:40 网站建设 项目流程

【免费下载链接】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
点击查看免费下载

本文是 NativeScript 核心包中ui/list-picker模块的实战指南,围绕官方文档(apps/automated/src/ui/list-picker/list-picker.md)梳理 ListPicker 的引入方式、控件创建、items数据绑定以及selectedIndex编程式选择四大核心操作,并结合@nativescript/core源码(跨平台公共实现、Android NumberPicker、iOS UIPickerView 封装)与自动化测试用例,深入解释其底层原理与边界行为。读完本文,你将能够熟练地在 NativeScript 应用中创建滚轮式选择器、绑定数组或自定义数据源,并通过属性或事件精确控制选中项。

引入 ListPicker 模块

在 NativeScript 中使用 ListPicker,首先需要引入ui/list-picker模块。官方文档给出的引用方式与测试代码(list-picker-tests.ts)完全一致:

import * as listPickerModule from '@nativescript/core/ui/list-picker';

该模块导出了ListPicker类(继承自ListPickerBase)以及selectedIndexProperty、itemsProperty等属性描述符,因此既可以按命名空间方式访问(如new listPickerModule.ListPicker()),也可以直接使用具名导入:

import { ListPicker } from '@nativescript/core/ui/list-picker';

从源码结构看,@nativescript/core在打包时通过平台别名(.android/.ios)将平台实现暴露为同一个模块入口:Android 端基于android.widget.NumberPicker(见 index.android.ts),iOS 端基于UIPickerView(见 index.ios.ts),开发者无需关心平台差异。

创建 ListPicker 控件

使用new关键字即可创建 ListPicker 实例:

var listPicker = new listPickerModule.ListPicker();

创建完成后,可以像其他 View 一样将其加入页面内容或布局容器中(参考测试代码 list-picker-tests.ts):

listPicker.id = 'ListPicker'; page.content = listPicker;

在 XML 声明式语法中,ListPicker 同样是注册过的原生视图(源码中标有@nsView ListPicker注解),可以直接写为:

<ListPicker id="listPicker" items="{{ items }}" selectedIndex="{{ selectedIndex }}" />

控件创建时有两个值得注意的默认状态(均由自动化测试验证):

  • items默认为undefined(测试testWhenlistPickerIsCreatedItemsAreUndefined);
  • selectedIndex默认为-1(测试testWhenlistPickerIsCreatedSelectedIndexIsUndefined)。

这一默认设计避免了在未设置数据时出现“悬空选中态”,具体边界行为详见下文。

绑定 items 数据源

ListPicker 的items属性既可以绑定普通数组,也可以绑定自定义数据源对象。先看最直接的数组绑定方式(官方文档片段,测试 list-picker-tests.ts 中原样复现):

listPicker.items = [1, 2, 3];

绑定一个包含 10 个元素的数组同样简单:

listPicker.items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

items 的底层行为

items在 list-picker-common.ts 中注册为普通Property,其valueChanged回调会根据新值是否具有getItem方法自动判定是否为ItemsSource:

export const itemsProperty = new Property<ListPickerBase, any[] | ItemsSource>({ name: 'items', valueChanged: (target, oldValue, newValue) => { const getItem = newValue && (<ItemsSource>newValue).getItem; target.isItemsSource = typeof getItem === 'function'; }, });

因此items支持两种形态:

  1. 普通数组(any[]):按索引取值,listPicker.items[index];
  2. 自定义数据源:满足ItemsSource接口的对象,只需实现length属性和getItem(index): any方法:
interface ItemsSource { length: number; getItem(index: number): any; }

绑定数据源后,列表展示的文本由_getItemAsString(index)决定(list-picker-common.ts):当textField属性非空时,取item[textField]作为展示文本;否则直接把item转为字符串。这为“对象数组 + 指定展示字段”的场景提供了支持:

listPicker.textField = 'name'; // 展示 item.name listPicker.items = [ { id: 1, name: 'Angular' }, { id: 2, name: 'React' }, { id: 3, name: 'Vue' }, ];

对应的valueField属性则用于定义selectedValue的取值字段(见下文“selectedValue 联动”)。

数据绑定的原生联动

  • Android 端:itemsProperty.setNative会同步到原生NumberPicker的maxValue与minValue。测试 list-picker-tests.ts 验证了 Android 原生控件的maxValue恒等于items.length - 1;而createNativeView初始即设置setMinValue(0)、setMaxValue(0)、setWrapSelectorWheel(false)(index.android.ts),防止空数据时出现无限滚动。
  • iOS 端:itemsProperty.setNative调用reloadAllComponents()刷新UIPickerView,随后对selectedIndex执行一次强制收敛(index.ios.ts)。

此外,items在视图加载前设置也能被正确解析(测试testItemsIsResolvedCorrectlyIfSetBeforeViewIsLoaded),属性机制保证了延迟到原生视图创建后再同步。

编程式选择选中项

通过设置selectedIndex属性即可在代码中选中指定行(官方文档片段,测试 list-picker-tests.ts 中原样复现):

listPicker.selectedIndex = 9;

例如先绑定 10 个元素,再选中索引为 9 的最后一项:

listPicker.items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; listPicker.selectedIndex = 9; // 选中最后一项

selectedIndex 的收敛规则

selectedIndex在 list-picker-common.ts 中被定义为CoercibleProperty,默认值为-1,赋值时自动执行范围收敛(coerce),核心逻辑如下:

  • 当items存在时,最大值取items.length - 1;
  • 值小于 0 时被收敛为0;
  • 值大于items.length - 1时被收敛为最大值;
  • 当items为空(或未设置)时,selectedIndex一律收敛为-1。

这意味着给selectedIndex赋值一个越界索引不会抛异常,而是被安全钳制到有效区间内。valueConverter会将字符串形式的索引(如 XML 属性传来的"2")自动parseInt为数字。

不同 items 状态下的选中索引行为

自动化测试(list-picker-tests.ts)系统地验证了 items 变化时选中索引的重置规则:

items 状态selectedIndex 结果依据测试
绑定非空数组变为0(自动选中首项)testSelectedIndexBecomesZeroWhenItemsBoundToNonEmptyArray
绑定空数组[]重置为-1testSelectedIndexBecomesUndefinedWhenItemsBoundToEmptyArray
设为undefined重置为-1testSelectedIndexBecomesUndefinedWhenItemsBoundToUndefined
设为null重置为-1testSelectedIndexBecomesUndefinedWhenItemsBoundToNull

另外,selectedIndex在视图加载前设置也能正确生效(测试testSelectedIndexIsResolvedCorrectlyIfSetBeforeViewIsLoaded)。

平台原生同步

  • iOS:selectedIndexProperty.setNative通过selectRowInComponentAnimated(value, 0, false)定位到指定行(index.ios.ts),且仅当value >= 0时才执行,避免对未就绪的控件误操作。
  • Android:用户滚动NumberPicker时,ValueChangeListenerImpl.onValueChange会回调selectedIndexProperty.nativeValueChange(owner, newValue)并联动updateSelectedValue(index.android.ts),从而保证 UI 操作与属性状态双向一致。

监听选中变化与 selectedValue 联动

当用户通过滚轮改变选中项时,ListPicker 会触发selectedIndexChange事件(事件名常量定义于 list-picker-common.ts,d.ts 中标注其载荷类型为PropertyChangeData)。监听方式如下:

listPicker.on('selectedIndexChange', (args) => { console.log('新的选中索引:', args.value); });

与此同时,selectedIndex变化还会联动更新只读的selectedValue属性。updateSelectedValue(list-picker-common.ts)的逻辑为:

  • 当valueField非空时,取item[valueField]作为selectedValue;
  • 否则直接取item本身;
  • 索引小于 0 时selectedValue置为null。

典型用法:配合textField展示名称、valueField取 ID,从而在选中变化时直接拿到业务主键,无需再手动查表:

listPicker.textField = 'name'; listPicker.valueField = 'id'; listPicker.items = [ { id: 101, name: 'Angular' }, { id: 202, name: 'React' }, ]; listPicker.on('selectedIndexChange', (args) => { console.log('选中 ID:', listPicker.selectedValue); // 101 或 202 });

平台实现速览

ListPicker 的双端原生实现都封装在@nativescript/core的ui/list-picker目录中:

  • 公共基类list-picker-common.ts:定义ListPickerBase、ItemsSource接口及selectedIndex、items、textField、valueField、selectedValue五个属性,并设置了recycleNativeView = 'auto'以支持原生视图回收复用(自动化测试test_recycling即验证了这一点)。
  • Android 实现index.android.ts:原生控件为android.widget.NumberPicker,通过Formatter(调用_getItemAsString渲染行文本)与OnValueChangeListener(回写selectedIndex)桥接 JS 与原生;同时支持通过color属性定制选中文字颜色,API 28 及以下使用反射获取mSelectorWheelPaint,API 29+ 使用原生公开方法。
  • iOS 实现index.ios.ts:原生控件为UIPickerView,通过UIPickerViewDataSource(返回组件数与行数)和UIPickerViewDelegate(渲染行文本、回写选中行)实现桥接,并支持tintColor定制。
  • 类型声明index.d.ts:对外暴露selectedIndex与items属性,并标注 Android 端原生视图为android.widget.NumberPicker、iOS 端为UIPickerView,便于在代码中直接访问原生对象做进一步定制。

进一步阅读

  • 官方 HOW-TO 文档主体:apps/automated/src/ui/list-picker/list-picker.md
  • 完整自动化测试(含全部边界行为用例):apps/automated/src/ui/list-picker/list-picker-tests.ts
  • 平台原生测试辅助:list-picker-tests-native.ios.ts、list-picker-tests-native.android.ts
  • 双端原生封装源码:apps/ui/src/list-picker(UI 示例工程,含 XML 页面与 TS 用例)

【免费下载链接】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
点击查看免费下载
上一篇:如何快速找回遗忘的压缩包密码:开源工具的终极解决方案
下一篇:如何跑通 Bangumi 上架 App 的发布流程:一次双端发版的 5 步清单

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询