【免费下载链接】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.
Label 是 NativeScript 中最基础也最常用的 UI 组件,用于在页面上展示静态或动态文本内容。本文以 apps/automated/src/ui/label/label.md 为核心脉络,结合 packages/core/ui/label 的跨平台实现与 label-tests.ts 中的自动化测试用例,系统讲解 Label 的模块引入、文本绑定、自动换行、CSS 样式作用域以及基于 Observable 的数据驱动用法,帮助你掌握在真实项目中正确使用 Label 的完整方案。
引入 Label 模块
Label 与 NativeScript 的其他 UI 组件一样,统一从@nativescript/core模块导出。无论你使用的是 TypeScript、JavaScript 还是 Angular/Vue/React 等框架,只需要在逻辑代码中引入一次即可:
import { Label } from '@nativescript/core';引入之后,既可以通过代码动态创建 Label 实例,也可以直接在 XML 页面中声明式地使用<Label>标签。从源码看,Label 类继承自TextBase(文本基类),并分别针对两个平台做了原生封装:
- Android 端(packages/core/ui/label/index.android.ts):底层对应
org.nativescript.widgets.StyleableTextView,初始化时调用setSingleLine(true)并默认设置TruncateAt.END的省略号截断方式; - iOS 端(packages/core/ui/label/index.ios.ts):底层对应
TNSLabel(UILabel的子类),默认不开启换行、单行展示。
两个平台均通过@CSSType('Label')装饰器注册,这也是后面“按类型选择器(label)进行 CSS 样式匹配”能够生效的底层基础。
将 Label 的 text 属性绑定到 View-Model 属性
在 NativeScript 的 XML 中,使用双花括号语法即可将 Label 的text属性与 View-Model(或任意可观察对象)中的属性建立单向数据绑定:
<Page> <Label text="{{ title }}" /> </Page>当title的值发生变化时,Label 会自动刷新显示的文本。这种绑定是 NativeScript 数据绑定引擎的核心能力,绑定方向由绑定表达式决定,{{ title }}表示以title为数据源、text为目标属性。
通过代码设置 Label 的文本内容
在代码中创建 Label 并设置文本非常简单,label-tests.ts 中snippet_Set_Text_TNS测试给出了标准写法:
const label = new Label(); const expectedValue = 'Expected Value'; label.text = expectedValue;对应的测试还验证了设置后的效果:label.text能读回相同的值(test_Set_Text_TNS);同时会同步到原生视图——Android 端通过label.android.getText()、iOS 端通过label.ios.text均可读回相同文本(test_Set_Text_Native)。
需要特别注意 null/undefined 的处理:test_Set_Text_Native_Null 与 test_Set_Text_Native_Undefined 证明,将text设为null或undefined时,原生视图会统一显示为空字符串(''),而不是崩溃或显示 "null" 字样。这为处理动态数据缺失场景提供了安全保证。
开启 Label 的文本自动换行
Label 默认是单行显示、超出部分截断的。要支持多行自动换行,需要设置textWrap属性:
const label = new Label(); label.textWrap = true;从源码实现(packages/core/ui/label/index.ios.ts)可以看到,textWrap本质上是whiteSpace样式的语法糖:
textWrap = true时,style.whiteSpace = 'normal'(允许换行);textWrap = false时,style.whiteSpace = 'nowrap'(禁止换行)。
它同样支持字符串值,内部通过booleanConverter自动转换(例如 XML 中的textWrap="true")。两个平台都实现了textWrap的 getter/setter,Android 与 iOS 行为保持一致。
关于换行行为的底层验证,参考测试 test_Set_TextWrap_Native:
- Android:换行开启时
getEllipsize()为 null(不截断)、getLineCount() > 1(多行)、无水平滚动、无TransformationMethod; - iOS:
lineBreakMode为NSLineBreakMode.ByWordWrapping(按单词换行)、numberOfLines = 0(不限行数)。
而 test_Set_TextWrapFirstTrueThenFalse_Native 验证了关闭换行后的回退行为:Android 恢复TruncateAt.END省略号、单行;iOS 恢复ByTruncatingTail尾部截断、numberOfLines = 1。这解释了为什么动态切换textWrap时界面会呈现"多行 ↔ 单行省略"两种形态。
通过 CSS 类选择器(.class)为 Label 设置样式
在 NativeScript 中,CSS 是 Label 样式体系的核心入口。给 Label 指定一个 class 名称,再在页面 CSS 中定义同名规则即可:
label.text = 'The quick brown fox jumps over the lazy dog.'; label.className = 'title'; // 之后只需在父页面 CSS 中添加对应的样式条目即可生效,例如: // label.parentPage.css = ".title {background-color: #C6C6C6; color: #10C2B0; font-size: 14;}";对应的 CSS 规则为:
.title { background-color: #C6C6C6; color: #10C2B0; font-size: 14; }测试 test_SetStyleProperties_via_css_class_Native 验证了该类选择器确实会落到底层原生视图:Android 端getTextSize()与按COMPLEX_UNIT_SP换算的期望字号一致,iOS 端font.pointSize、textColor、layer.backgroundColor均与 CSS 值吻合。这说明 CSS 样式不是"伪渲染",而是真实驱动原生控件属性。
CSS 也可以通过代码动态注入,测试中使用的注入方式为this.testPage.css = testCss,正式项目里更常见的是在页面 XML 中通过<Page css="...">或全局样式表(如app.css)来维护。
通过 CSS 类型选择器(label)为 Label 设置样式
如果希望页面内所有 Label 统一应用某种样式,不需要给每个 Label 加 class,直接使用类型选择器label即可:
label.text = 'The quick brown fox jumps over the lazy dog.'; // 以"类型样式作用域"设置样式,只需添加如下 CSS 条目: // testLabel.parentPage.css = "label {background-color: #C6C6C6; color: #10C2B0; font-size: 14;}"; // 父页面中的所有 Label 都会按该 CSS 值渲染对应 CSS:
label { background-color: #C6C6C6; color: #10C2B0; font-size: 14; }test_SetStyleProperties_via_css_type_TNS 验证了label.style.backgroundColor、label.style.color、label.style.fontSize与 CSS 值完全一致。类型选择器依赖前面提到的@CSSType('Label')注册——NativeScript 解析器据此把Label类与 CSS 选择器label关联起来。
通过 CSS ID 选择器(#id)为单个 Label 设置样式
当页面里存在多个 Label、只想精确命中其中一个时,使用 id 选择器:
label.text = 'The quick brown fox jumps over the lazy dog.'; label.id = 'testLabel'; // 之后只需在父页面 CSS 中添加对应条目,例如: // label.parentPage.css = "#testLabel {background-color: #C6C6C6; color: #10C2B0; font-size: 14;}";对应 CSS:
#testLabel { background-color: #C6C6C6; color: #10C2B0; font-size: 14; }test_SetStyleProperties_via_css_id 验证了 id 选择器生效后背景色、文字颜色、字号与预期一致。
关于三种选择器的优先级,测试 testLocalTextAlignmentFromCssWhenAddingCssAllSelectorsAreApplied 给出了明确结论:当同时存在#testLabel与label两条规则时,实际生效的是 id 选择器的值,因为 id 的优先级高于类型选择器。这与 Web CSS 的特异性(specificity)规则一致,可用于推断更复杂选择器组合的最终效果。
另外,样式既可以整页注入(page.css = '...'),也可以追加式注入(page.addCss(...)/page.addCssFile(...)),后者不会覆盖已有样式,适合运行时动态扩展主题。测试 testLocalTextAlignmentFromCssWhenAddingCss 与 testLocalTextAlignmentFromCssWhenAddingCssFileAllSelectorsAreApplied 均验证了这两种注入方式的可用性。
将 Label 的 text 绑定到 Observable 数据模型
除了 XML 的{{ title }}语法,还可以在代码中通过bind()方法把 Label 的text属性绑定到任意Observable对象,实现真正的数据驱动更新。label-tests.ts 中test_BindingToText给出了标准写法:
const label = new Label(); const expValue = 'Expected Value'; const sourceModel = new Observable(); const bindingOptions: BindingOptions = { sourceProperty: 'sourceProperty', targetProperty: 'text', }; label.bind(bindingOptions, sourceModel); sourceModel.set('sourceProperty', expValue); // console.log(label.text); --> 打印: "Expected Value"关键点在于:绑定建立后,调用sourceModel.set('sourceProperty', ...)修改数据源,Label 的 text 会自动同步更新,无需手动赋值。测试 test_BindingToText_Native 进一步验证了绑定结果会穿透到原生视图(label.android.getText()/label.ios.text)。
更灵活的一种方式是不显式传入 sourceModel,而是复用页面的 bindingContext:
label.bind(bindingOptions); // 不传第二个参数,绑定到页面的 bindingContext this.testPage.bindingContext = firstSourceObject; // label.text === 'Expected Value' this.testPage.bindingContext = secondSourceObject; // label.text === 'Second value'test_BindingToText_WithBindingContext 验证了切换bindingContext时 Label 文本会随之更新。这也是 MVC/MVVM 架构下最常见的用法:页面切换数据上下文,所有绑定到它的 Label 自动刷新。
更多进阶行为:布局尺寸与格式化文本
围绕 Label 的text与formattedText,测试集中还覆盖了若干值得在生产中注意的布局行为:
- 文本变长/变短时 Label 的尺寸自适应:
test_label_grows_on_text_change、test_label_shrinks_on_text_change等用例验证了水平对齐为 left 时,Label 的宽度会随文本实际渲染宽度增大/缩小(label-tests.ts); - maxWidth / maxHeight 约束:
test_maxWidth_caps_label_measured_width(label-tests.ts)验证了maxWidth会封顶 Label 的测量宽度;test_maxWidth_percent_resolves_against_parent(label-tests.ts)验证了百分比形式的maxWidth(如{ value: 0.5, unit: '%' })会按父容器宽度解析; - 单行/多行与 requestLayout 的性能优化:iOS 端实现(packages/core/ui/label/index.ios.ts)会根据固定尺寸状态决定文本变化时是否触发重新布局,测试 test_ChangingTextWhenInFixedSizeGridShouldNotRequestLayout 验证了固定尺寸容器中修改文本不会触发无谓的 requestLayout,而尺寸随内容变化的场景则会触发,这一细节有助于理解动态文本界面的性能特性;
- 格式化文本(FormattedString + Span):测试展示了通过多个
Span(可各自设置fontWeight等属性)拼装FormattedString赋给label.formattedText,实现一段文字内不同样式混排的能力(label-tests.ts)。
小结
Label 虽然简单,但其正确用法涉及模块引入、文本绑定(XML 声明式 / 代码bind()/ bindingContext 三种形态)、换行开关、CSS 三种选择器作用域(class、type、id)以及底层原生视图的同步行为。本文所有示例均可在 label-tests.ts 中找到对应测试用例,两个平台的实现细节可继续阅读 packages/core/ui/label/index.android.ts 与 packages/core/ui/label/index.ios.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.
相关推荐
Angular 模板数据绑定指南:文本插值、属性/attribute 绑定与 CSS 类与样式绑定
Angular 模板数据绑定指南:文本插值、属性/attribute 绑定与 CSS 类与样式绑定 本篇基于 Angular 官方开发者文档( adev/src
前端Web框架Relay 查询基础实战:从占位数据到 GraphQL 数据驱动的 React 组件
Relay 查询基础实战:从占位数据到 GraphQL 数据驱动的 React 组件 本篇技术指南以 Relay 官方教程“Query Basics”为核心,讲
前端开发工具Primer CSS按钮组件终极指南:从基础到高级的完整样式解决方案
Primer CSS按钮组件终极指南:从基础到高级的完整样式解决方案 Primer CSS是GitHub官方设计系统的CSS实现,为开发者提供了一套完整的、经过
前端设计系统UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考