单页网站排名没有,网站制作高手,建设平台类网站需要多少钱,不锈钢网架公司鸿蒙开发-ArkTS 语言
1. 初识 ArkTS 语言 ArkTS 是 HarmonyOS 优选主力开发语言。ArkTS 是基于 TS(TypeScript)扩展的一门语言#xff0c;继承了 TS 的所以特性#xff0c;是TS的超集。 主要是扩展了以下几个方面#xff1a; 声明式UI描述和自定义组件#xff1a; ArkTS允…鸿蒙开发-ArkTS 语言
1. 初识 ArkTS 语言 ArkTS 是 HarmonyOS 优选主力开发语言。ArkTS 是基于 TS(TypeScript)扩展的一门语言继承了 TS 的所以特性是TS的超集。 主要是扩展了以下几个方面 声明式UI描述和自定义组件 ArkTS允许开发者使用声明式的方式描述用户界面UI使UI的结构和布局更直观易懂。提供了自定义组件的能力开发者可以定义自己的组件以便在应用中重复使用。 动态扩展UI元素的能力 ArkTS支持动态扩展UI元素使开发者能够灵活地向应用中添加新的UI元素。 多维度的状态管理机制 ArkTS提供了多种方式来管理应用的状态。支持在组件内使用与UI相关联的数据实现局部状态管理。支持在不同组件层级间传递数据包括父子组件和爷孙组件之间的传递。全局状态管理允许在应用的整个范围内传递数据甚至可以跨设备传递。提供只读的单向数据传递和可变更的双向数据传递使开发者能够根据需要选择合适的数据传递形式。 渲染控制的能力 ArkTS提供了灵活的渲染控制功能使开发者能够根据应用的状态动态渲染UI内容。条件渲染允许根据不同的应用状态选择性地渲染UI内容。循环渲染支持从数据源中迭代获取数据并在每次迭代中创建相应的组件。数据懒加载允许按需迭代数据并在每次迭代中创建相应的组件提高应用性能。
2. 基本语法
2.1 ArkTS的基本组成
概念描述装饰器用于装饰类、结构、方法以及变量赋予其特殊含义。例如Component 表示自定义组件Entry 表示入口组件State 表示状态变量。状态变量的变化会触发UI刷新。UI描述使用声明式的方式描述UI的结构通常体现在 build() 方法中的代码块。自定义组件可复用的UI单元可组合其他组件。被 Component 装饰的 struct xxx 就是一个自定义组件。系统组件ArkUI框架中默认内置的基础和容器组件可以直接调用。 常见系统组件Column、Text、Divider、Button 。属性方法组件可以通过链式调用配置多项属性的方法。例如fontSize()、width()、height()、backgroundColor() 可以用于设置组件的样式和尺寸等属性。事件方法组件可以通过链式调用设置多个事件的响应逻辑。例如在 Button 后面的 onClick() 就是一个事件方法用于处理按钮被点击时的逻辑。
代码示例
// 1. 按引用传递参数时传递的参数可为状态变量
// 且状态变量的改变会引起Builder方法内的UI刷新
Builder function MessageBuilder($$:{message:string}) { // 自定义装饰器Row(){Text(Message is ${$$.message})}
}// 2. 按值传递
Builder function ValueBuilder(message : string) {Row() {Text(message is ${message})}
}Entry // 装饰器
Component // 装饰器
struct Index { // 使用 struct 关键字定义组件State message: string hello // State表示组件中的状态变量状态变量变化会触发UI刷新State count: number 1build() {Row() { // 行Column() { // 列Text(this.message this.count).fontSize(50) // 属性方法.fontWeight(FontWeight.Bold)Button(点我试试).onClick(() {this.count this.message 你好}).fontColor(#000)MessageBuilder({message: this.message}) // 传递参数的引用ValueBuilder(this.message) // 按值传递无响应式}.width(100%)}.height(100%)}
}效果如下 2.2 声明式 UI
ArkTS采用声明方式组合和扩展组件来描述应用程序的UI同时提供了基本的属性、事件和子组件配置方法
1. 创建组件 无参数创建对于没有必选构造参数的组件可以直接在组件后面使用空括号例如 Divider()。 有参数创建如果组件包含构造参数可以在括号内配置相应参数例如 Image(https://xyz/test.jpg)。4 Column() {Text(item 1)Divider()Text(item 2)
}2. 配置属性 使用属性方法通过“.”链式调用配置系统组件的样式和其他属性建议每个属性方法单独写一行如设置字体大小、宽度、高度等。 Text(test).fontSize(12)3. 配置事件 通过事件方法以“.”链式调用配置系统组件支持的事件可使用lambda表达式、匿名函数表达式或组件的成员函数来定义 Button(Click me).onClick(() {this.myText ArkUI;})2.3 自定义组件
在ArkUI中UI显示的内容由组件构成其中框架直接提供的组件称为系统组件而开发者定义的组件则被称为自定义组件。在进行UI界面开发时通常需要考虑代码的可复用性、业务逻辑与UI的分离以及后续版本演进等因素。因此将UI和部分业务逻辑封装成自定义组件是不可或缺的能力。
自定义组件具有以下特点 可组合 允许开发者组合使用系统组件及其属性和方法。 可重用 自定义组件可以被其他组件重用作为不同实例在不同的父组件或容器中使用。 数据驱动UI更新 通过状态变量的改变来驱动UI的刷新。
以下示例展示了自定义组件的基本用法
Component
struct GreetingComponent {State greeting: string Hello, World!;build() {// GreetingComponent自定义组件组合系统组件Row和TextRow() {Text(this.greeting).onClick(() {// 状态变量greeting的改变驱动UI刷新this.greeting Hello, ArkUI!;});}}
}GreetingComponent 可以在其他自定义组件的 build() 函数中多次创建实现自定义组件的重用。
Entry
Component
struct ParentComponent {build() {Column() {Text(ArkUI Greetings);GreetingComponent({ greeting: Hello, World! });Divider();GreetingComponent({ greeting: 你好! });}}
}自定义组件的结构
概念描述struct基于 struct 实现自定义组件结构为 struct 自定义组件名 {...}。实例化时可以省略 new。Component装饰器仅能装饰使用 struct 关键字声明的数据结构。被装饰后的结构具备组件化能力需要实现 build 方法描述UI。一个 struct 只能被一个 Component 装饰。build() 函数用于定义自定义组件的声明式 UI 描述。自定义组件必须实现 build() 函数。Entry装饰的自定义组件将作为UI页面的入口。在单个UI页面中最多可以使用一个 Entry 装饰的自定义组件。可以接受一个可选的 LocalStorage 参数。
示例代码
// 自定义组件基本结构
Component
struct MyComponent {build() {// 在这里描述组件的UI结构}
}// Entry装饰的自定义组件作为UI页面入口
Entry
Component
struct MainComponent {build() {// UI页面的主要组件结构}
}成员函数/变量
自定义组件除了必须要实现build()函数外还可以实现其他成员函数成员函数具有以下约束
不支持静态函数。成员函数的访问始终是私有的。
自定义组件可以包含成员变量成员变量具有以下约束
不支持静态成员变量。所有成员变量都是私有的变量的访问规则与成员函数的访问规则相同。自定义组件的成员变量本地初始化有些是可选的有些是必选的。具体是否需要本地初始化是否需要从父组件通过参数传递初始化子组件的成员变量请参考官方文档状态管理。
build()函数 Entry装饰的自定义组件必须要有且仅有一个 build() 函数且必须为容器组件其中ForEach禁止作为根节点他会产生多节点。 Component装饰的自定义组件其build()函数下的根节点唯一且必要可以为非容器组件其中ForEach禁止作为根节点。 代码示例
Entry
Component
struct MyComponent {build() {// 根节点唯一且必要必须为容器组件Row() {ChildComponent() }}
}Component
struct ChildComponent {build() {// 根节点唯一且必要可为非容器组件Image(test.jpg)}
}在 build() 函数内不允许以下几点:
build() { // 反例不允许声明本地变量 let a: number 1;// 反例不允许console.infoconsole.info(print debug log);// 反例不允许本地作用域{...}// 不允许switch语法如果需要使用条件判断请使用if。反例如下。switch (expression) {case 1:Text(...)break;case 2:Image(...)break;default:Text(...)break;}// 不允许使用表达式反例如下。(this.aVar 10) ? Text(...) : Image(...)}另外
不允许调用除了被Builder装饰以外的方法允许系统组件的参数是TS方法的返回值
Component
struct ParentComponent {doSomeCalculations() {}calcTextValue(): string {return Hello World;}Builder doSomeRender() {Text(Hello World)}build() {Column() {// 反例不能调用没有用Builder装饰的方法this.doSomeCalculations();// 正例可以调用this.doSomeRender();// 正例参数可以为调用TS方法的返回值Text(this.calcTextValue())}}
}2.4 自定义组件通用样式
自定义组件通过“.”链式调用的形式设置通用样式。
Component
struct MyComponent2 {build() {Button(Hello World)}
}Entry
Component
struct MyComponent {build() {Row() {MyComponent2() // 为自定义组件添加通用样式.width(200).height(300).backgroundColor(Color.Red)}}
}2.5. 页面和自定义组件生命周期
可以由一个或者多个自定义组件组成Entry装饰的自定义组件为页面的入口组件即页面的根节点一个页面有且仅能有一个Entry。只有被Entry装饰的组件才可以调用页面的生命周期。
图示
图片来源https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/arkts-page-custom-components-lifecycle-0000001524296665-V2 生命周期接口描述onPageShow每次页面显示时触发。onPageHide每次页面隐藏时触发一次。onBackPress当用户点击返回按钮时触发。aboutToAppear组件即将出现时回调具体时机为在创建新实例后在执行 build() 函数之前执行。aboutToDisappear在自定义组件即将析构销毁时执行。
示例代码
// 包含两个自定义组件一个是被Entry装饰的MyComponent也是页面的入口组件即页面的根节点一个是Child是MyComponent的子组件。
// 只有Entry装饰的节点才可以生效页面的生命周期方法所以MyComponent中声明了当前Index页面的页面生命周期函数。MyComponent和其子组件Child也同时也声明了组件的生命周期函数。
import router from ohos.router;Entry
Component
struct MyComponent {State showChild: boolean true;// 只有被Entry装饰的组件才可以调用页面的生命周期onPageShow() {console.info(Index onPageShow);}// 只有被Entry装饰的组件才可以调用页面的生命周期onPageHide() {console.info(Index onPageHide);}// 只有被Entry装饰的组件才可以调用页面的生命周期onBackPress() {console.info(Index onBackPress);}// 组件生命周期aboutToAppear() {console.info(MyComponent aboutToAppear);}// 组件生命周期aboutToDisappear() {console.info(MyComponent aboutToDisappear);}build() {Column() {// this.showChild为true创建Child子组件执行Child aboutToAppearif (this.showChild) {Child()}// this.showChild为false删除Child子组件执行Child aboutToDisappearButton(create or delete Child).onClick(() {this.showChild false;})// push到Page2页面执行onPageHideButton(push to next page).onClick(() {router.pushUrl({ url: pages/Page2 });})}}
}
Component
struct Child {State title: string Hello World;// 组件生命周期-在自定义组件即将析构销毁时执行aboutToDisappear() {console.info([lifeCycle] Child aboutToDisappear)}// 组件生命周期-组件即将出现时回调aboutToAppear() {console.info([lifeCycle] Child aboutToAppear)}build() {Text(this.title).fontSize(50).onClick(() {this.title Hello ArkUI;})}
}2.6. Builder装饰器自定义构建函数 Builder装饰的函数也称为“自定义构建函数”。 自定义组件内自定义构建函数
定义的语法
Builder MyBuilderFunction({ ... })使用方法
this.MyBuilderFunction({ ... })允许在自定义组件内定义一个或多个自定义构建函数该函数被认为是该组件的私有、特殊类型的成员函数。自定义构建函数可以在所属组件的build方法和其他自定义构建函数中调用但不允许在组件外调用。在自定义函数体中this指代当前所属组件组件的状态变量可以在自定义构建函数内访问。建议通过this访问自定义组件的状态变量而不是参数传递。
示例如下
Builder function GlobalBuilder() {Text(我是全局装饰器)
}Component
struct Children1 { // 子组件Builder DoNothing(){}BuilderParam aBuilder: () void this.DoNothing; // 定义局部的装饰器BuilderParam bBuilder: () void GlobalBuilder; // 定义全局的装饰器build(){Column() {GlobalBuilder()}}
}Component
struct Children2 {BuilderParam paramsBuilder: () void; // 声明装饰器build(){Column() {this.paramsBuilder()}}
}Component
Entry
struct Index {Builder componentBuilder() {Text(我是父组件的 builder)}build() {Column() {Children1()Children2({ paramsBuilder: this.componentBuilder}) // 传入一个 builder 给子组件}.width(100%)}
}尾随闭包初始化组件 在初始化自定义组件时紧跟一个大括号“{}”形成尾随闭包场景将尾随闭包内的内容看做Builder装饰的函数传给BuilderParam // 尾随闭包初始化组件
Component
struct CustomContainer {Prop header: string;BuilderParam closer: () voidbuild() {Column() {Text(this.header).fontSize(30)this.closer()}}
}Builder function specificParam(label1: string, label2: string) {Column() {Text(label1).fontSize(30)Text(label2).fontSize(30)}
}Entry
Component
struct Index {State text: string header;build() {Column() {// 在创建CustomContainer时通过其后紧跟一个大括号“{}”形成尾随闭包// 用内部的 this.text 作为参数CustomContainer({ header: this.text }) {Column() {specificParam(testA, testB)}.backgroundColor(Color.Yellow).onClick(() {this.text changeHeader;})}}}
}2.7 Styles装饰器定义组件重用样式 如果每个组件的样式都需要单独设置在开发过程中会出现大量代码在进行重复样式设置虽然可以复制粘贴但为了代码简洁性和后续方便维护我们推出了可以提炼公共样式进行复用的装饰器Styles。 Styles装饰器可以将多条样式设置提炼成一个方法直接在组件声明的位置调用。通过Styles装饰器可以快速定义并复用自定义样式。用于快速定义并复用自定义样式。 使用方法
// 反例 Styles不支持参数
// Styles function globalFancy (value: number) {
// .width(value)
// }// Styles可以定义在组件内或全局在全局定义时需在方法名前面添加function关键字
// 组件内定义时则不需要添加function关键字。// 定义在组件内的Styles可以通过this访问组件的常量和状态变量
// 并可以在Styles里通过事件来改变状态变量的值// 组件内Styles的优先级高于全局Styles。Component
Entry
struct Index {State heightValue: number 100Styles fancy() {.height(this.heightValue).backgroundColor(Color.Yellow).onClick(() {this.heightValue 200})}build() {Column(){Row(){Text(自定义样式点我也能改变样式).fancy()}Divider()Row(){Text(点我也一样也会跟着改变样式).fancy()}}}
}2.8 Extend装饰器定义扩展组件样式
使用规则
和Styles不同Extend仅支持定义在全局不支持在组件内部定义。和Styles不同Extend支持封装指定的组件的私有属性和私有事件和预定义相同组件的Extend的方法
// 和Styles不同Extend仅支持定义在全局不支持在组件内部定义。
// 和Styles不同Extend支持封装指定的组件的私有属性和私有事件和预定义相同组件的Extend的方法。
// 和Styles不同Extend装饰的方法支持参数开发者可以在调用时传递参数调用遵循TS方法传值调用。
// Extend的参数可以为状态变量当状态变量改变时UI可以正常的被刷新渲染
Extend(Text) function fancy (fontSize: number) { // 只给 Text 继承了 fancy.fontColor(Color.Red).fontSize(fontSize)
}// Extend装饰的方法的参数可以为function作为Event事件的句柄。
Extend(Text) function makeMeClick(onClick: () void) {.backgroundColor(Color.Blue).onClick(onClick)
}Entry
Component
struct Index {State label: string Hello World;State fontSizeValue: number 58;onClickHandler() {this.label Hello ArkUI;this.fontSizeValue 108}build(){Column(){Row({space: 10}) {Text(测试).fancy(this.fontSizeValue) // 传入可响应数据后续函数执行字号也会发生变化// Span(span无效)// .fancy() // Property fancy does not exist on type SpanAttribute. tsCheck}Row({ space: 10 }) {Text(${this.label}).makeMeClick(this.onClickHandler.bind(this)) // bind 绑定当前作用域.fancy(109)}}}
}2.9 stateStyles多态样式
// 多态样式
// stateStyles是属性方法可以根据UI内部状态来设置样式类似于css伪类但语法不同。ArkUI提供以下四种状态
//
// 1. focused获焦态。
// 2. normal正常态。
// 3. pressed按压态。
// 4. disabled不可用态。// 基础场景
// 下面的示例展示了stateStyles最基本的使用场景。Button处于第一个组件默认获焦
// 生效focused指定的粉色样式。按压时显示为pressed态指定的黑色。
// 如果在Button前再放一个组件使其不处于获焦态就会生效normal态的黄色。// Styles也可和stateStyles联合使用
Entry
Component
struct Index {Styles normalStyle() {.backgroundColor(Color.Gray)}Styles pressedStyle() {.backgroundColor(Color.Red)}build() {Column() {Button(Click me).stateStyles({focused: {.backgroundColor(Color.Pink)},pressed: {.backgroundColor(Color.Black)},normal: {.backgroundColor(Color.Yellow)}})Column() {Text(Text1).fontSize(50).fontColor(Color.White).stateStyles({normal: this.normalStyle,pressed: this.pressedStyle,})}}.margin(30%)}
}3. 状态管理
变量必须被装饰器装饰才能成为状态变量状态变量的改变才能导致 UI 界面重新渲染
概念描述状态变量被状态装饰器装饰的变量改变会引起UI的渲染更新。常规变量没有状态的变量通常应用于辅助计算。它的改变永远不会引起UI的刷新。数据源/同步源状态变量的原始来源可以同步给不同的状态数据。通常意义为父组件传给子组件的数据。命名参数机制父组件通过指定参数传递给子组件的状态变量为父子传递同步参数的主要手段。示例CompA: ({ aProp: this.aProp }。从父组件初始化父组件使用命名参数机制将指定参数传递给子组件。本地初始化的默认值在有父组件传值的情况下会被覆盖。
Components部分的装饰器为组件级别的状态管理Application部分为应用的状态管理。可以通StorageLink/LocalStorageLink和StorageProp/LocalStorageProp实现应用和组件状态的双向和单向同步。图中箭头方向为数据同步方向单箭头为单向同步双箭头为双向同步。
图片来源https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/arkts-state-management-overview-0000001524537145-V2 状态管理分三种 组件级别即components级别 StateState装饰的变量拥有其所属组件的状态可以作为其子组件单向和双向同步的数据源。当其数值改变时会引起相关组件的渲染刷新。PropProp装饰的变量可以和父组件建立单向同步关系Prop装饰的变量是可变的但修改不会同步回父组件。LinkLink装饰的变量和父组件构建双向同步关系的状态变量父组件会接受来自Link装饰的变量的修改的同步父组件的更新也会同步给Link装饰的变量。Provide/ConsumeProvide/Consume装饰的变量用于跨组件层级多层组件同步状态变量可以不需要通过参数命名机制传递通过alias别名或者属性名绑定。ObservedObserved装饰class需要观察多层嵌套场景的class需要被Observed装饰。单独使用Observed没有任何作用需要和ObjectLink、Prop连用。ObjectLinkObjectLink装饰的变量接收Observed装饰的class的实例应用于观察多层嵌套场景和父组件的数据源构建双向同步。 应用级别即 Application 级别 AppStorage是应用程序中的一个特殊的单例LocalStorage对象是应用级的数据库和进程绑定通过StorageProp和StorageLink装饰器可以和组件联动。AppStorage是应用状态的“中枢”需要和组件UI交互的数据存入AppStorage比如持久化数据PersistentStorage和环境变量Environment。UI再通过AppStorage提供的装饰器或者API接口访问这些数据框架还提供了LocalStorageAppStorage是LocalStorage特殊的单例。LocalStorage是应用程序声明的应用状态的内存“数据库”通常用于页面级的状态共享通过LocalStorageProp和LocalStorageLink装饰器可以和UI联动。 其他状态管理功能 Watch用于监听状态变量的变化。 $$ 运算符给内置组件提供TS变量的引用使得TS变量和内置组件的内部状态保持同步。
3.1 组件级别的状态管理
3.1.1 State 组件内状态
State装饰的变量是私有的只能从组件内部访问在声明时必须指定其类型和本地初始化。初始化也可选择使用命名参数机制从父组件完成初始化。
示例如下
Entry
Component
struct Index {build() {Column() {Parent()}}
}Component
struct MyComponent {State count: number 0;private increaseBy: number 1;build() {Column(){Row() {Text(${this.count}).fontSize(108)}Row() {Button(click me hh).onClick(() {this.count this.increaseBy})}}}
}Component
struct Parent {build() {Column() {// 从父组件初始化覆盖本地定义的默认值MyComponent({ count: 2, increaseBy: 2 })}}
}效果如下 3.1.2 能观察的数据类型
观察简单类型的变化 当状态变量是boolean、string、number类型时修改这些变量的数值可以被观察到从而引起UI的刷新。
State count: number 0;
this.count 1; // 这种修改可以被观察到观察类和对象类型的变化 对于class或Object类型可以观察到自身的赋值变化以及其属性的赋值变化。但是嵌套属性的赋值观察不到。
class ClassA {public value: string;constructor(value: string) {this.value value;}
}class Model {public value: string;public name: ClassA;constructor(value: string, a: ClassA) {this.value value;this.name a;}
}State title: Model new Model(Hello, new ClassA(World));// class类型赋值可以观察到
this.title new Model(Hi, new ClassA(ArkUI));// class属性的赋值可以观察到
this.title.value Hi;// 嵌套属性的赋值观察不到
this.title.name.value ArkUI;观察数组类型的变化 当状态变量是数组时可以观察到数组本身的赋值以及对数组的添加、删除、更新操作。
class Model {public value: number;constructor(value: number) {this.value value;}
}State title: Model[] [new Model(11), new Model(1)];// 数组自身的赋值可以观察到
this.title [new Model(2)];// 数组项的赋值可以观察到
this.title[0] new Model(2);// 删除数组项可以观察到
this.title.pop();// 新增数组项可以观察到
this.title.push(new Model(12));3.1.2 Prop装饰器父子单向通信
可以使用 Prop 定义要从父级接受的变量。注意以下两点 Prop变量可以在子组件内修改但修改后的变化不会同步回父组件中。 万但当父组件中的数据源更改时与之相关的Prop装饰的变量都会自动更新。如果子组件已经在本地修改了Prop装饰的相关变量值而在父组件中对应的State装饰的变量被修改后子组件本地修改的Prop装饰的相关变量值将被覆盖。 允许装饰的变量的类型string、number、boolean、enum类型。 不允许的类型anyundefined和null。
Prop 初始化规则
图示
图片来源https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/arkts-prop-0000001473537702-V2 代码示例
// 框架行为
// 要理解Prop变量值初始化和更新机制有必要了解父组件和拥有Prop变量的子组件初始渲染和更新流程。
//
// 初始渲染
// 执行父组件的build()函数将创建子组件的新实例将数据源传递给子组件
// 初始化子组件Prop装饰的变量。
// 更新
// 子组件Prop更新时更新仅停留在当前子组件不会同步回父组件
// 当父组件的数据源更新时子组件的Prop装饰的变量将被来自父组件的数据源重置所有Prop装饰的本地的修改将被父组件的更新覆盖。// 父组件State到子组件Prop简单数据类型同步
// 以下示例是State到子组件Prop简单数据同步
// 父组件ParentComponent的状态变量countDownStartValue初始化子组件CountDownComponent中Prop装饰的count点击“Try again”
// count的修改仅保留在CountDownComponent不会同步给父组件ParentComponent。
Component
struct CountDownComponent {Prop count: number;costOfOneAttempt: number 1;build() {Column() {if (this.count 0) {Text(儿子还有 ${this.count} 个萝卜头)} else {Text(吃完了要挨打了)}// Prop装饰的变量不会同步给父组件Button(儿子偷吃了${this.costOfOneAttempt}个萝卜头).onClick(() {this.count - this.costOfOneAttempt;})}}
}Entry
Component
struct ParentComponent {State countDownStartValue: number 10;build() {Column() {Text(老爸还有 ${this.countDownStartValue} 个萝卜头)// 父组件的数据源的修改会同步给子组件Button(一起买了1个萝卜头).onClick(() {this.countDownStartValue 1;})// 父组件的修改会同步给子组件Button(一起吃了1个萝卜头).onClick(() {this.countDownStartValue - 1;})CountDownComponent({ count: this.countDownStartValue, costOfOneAttempt: 2 })}}
}
效果如下 3.1.3 Link: 父子双向通信
可以使用 Link 装饰器进行数据双向同步
Link 是双向同步的装饰器父组件中State, StorageLink和Link 和子组件Link可以建立双向数据同步反过来也是可以的。允许装饰的类型有Object、class、string、number、boolean、enum类型同时类型必须被指定且和双向绑定状态变量的类型相同。不支持any不支持简单类型和复杂类型的联合类型不允许使用undefined和null。当装饰的数据类型为boolean、string、number时可以同步观察到数值的变化为class或Object时可以观察到赋值和属性赋值的变化为array时可以观察到数组添加、删除、更新数组单元的变化。
Link初识化规则
图示
图片来源https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/arkts-link-0000001524297305-V2 代码示例
class GreenButtonState {width: number 0;constructor(width: number) {this.width width;}
}// class类型的子组件GreenButton通过Link装饰变量greenButtonState同步到父组件Index中的State变量greenButtonStateComponent
struct GreenButton {Link greenButtonState: GreenButtonState;build() {Button(绿色按钮).width(this.greenButtonState.width).height(150.0).fontWeight(FontWeight.Bold).backgroundColor(#ff81c123).onClick(() {if (this.greenButtonState.width 700) {// 更新class的属性变化可以被观察到同步回父组件this.greenButtonState.width 125;} else {// 更新class变化可以被观察到同步回父组件this.greenButtonState new GreenButtonState(100);}})}
}// 简单类型的子组件YellowButton通过Link装饰变量yellowButtonState同步到父组件Index中的State变量yellowButtonProp
Component
struct YellowButton {Link yellowButtonState: number;build() {Button(黄色按钮).width(this.yellowButtonState).height(150.0).fontWeight(FontWeight.Bold).backgroundColor(#ffdb944d).onClick(() {// 子组件的简单类型可以同步回父组件this.yellowButtonState 50.0;})}
}// 父组件Index初始化并管理子组件的状态变量
Entry
Component
struct Index {State greenButtonState: GreenButtonState new GreenButtonState(300);State yellowButtonProp: number 100;build() {Column() {// 按钮父组件修改greenButtonState同步到GreenButton子组件Button(父视图: 设置 绿色按钮).onClick(() {this.greenButtonState.width (this.greenButtonState.width 700) ? this.greenButtonState.width 100 : 100;})// 按钮父组件修改yellowButtonProp同步到YellowButton子组件Button(父视图: 设置 黄色按钮).onClick(() {this.yellowButtonProp (this.yellowButtonProp 700) ? this.yellowButtonProp 100 : 100;})// 子组件GreenButton通过Link同步更新父组件State的greenButtonStateGreenButton({ greenButtonState: $greenButtonState })// 子组件YellowButton通过Link同步更新父组件State的yellowButtonPropYellowButton({ yellowButtonState: $yellowButtonProp })}}
}
图示 3.1.4 Provide/Consume 装饰器
为了避免在组件中多次传递变量推出了一种使某些变量能被所有后代组件使用的装饰器 Provide后代组件可以使用 Consume去获取 Provide 的值而State和Link 只能在父子组件中传递。
// 通过相同的变量名绑定
Provide a: number 0;
Consume a: number;// 通过相同的变量别名绑定
Provide(a) b: number 0;
Consume(a) c: number;Provide 和 Consume 是双向同步的。
代码示例
Component
struct CompD {// Consume装饰的变量通过相同的属性名绑定其祖先组件CompA内的Provide装饰的变量Consume tickets: number;build() {Column() {Text(评审投票数(${this.tickets}))Button(评审投票数(${this.tickets})1).onClick(() this.tickets 1)}.width(50%)}
}Component
struct CompC {build() {Row({ space: 5 }) {CompD()CompD()}}
}Component
struct CompB {build() {CompC()}
}Entry
Component
struct Index {// Provide装饰的变量reviewVotes由入口组件CompA提供其后代组件Provide tickets: number 0;build() {Column() {Button(评审投票数(${this.tickets})1).onClick(() this.tickets 1)CompB()}}
}
图示 3.1.5 Observed 装饰器和ObjectLink装饰器
对于多层嵌套的情况比如二维数组或者数组项class或者class的属性是class他们的第二层的属性变化是无法观察到的。这就引出了Observed/ObjectLink装饰器。
ObjectLink和Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步
被Observed装饰的类可以被观察到属性的变化子组件中ObjectLink装饰器装饰的状态变量用于接收Observed装饰的类的实例和父组件中对应的状态变量建立双向数据绑定。这个实例可以是数组中的被Observed装饰的项或者是class object中的属性这个属性同样也需要被Observed装饰。单独使用Observed是没有任何作用的需要搭配ObjectLink或者Prop使用。不要用Observed和其他类装饰器装饰同一个classObserved会改变class的原型链
初始化图示
图片来源https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/arkts-observed-and-objectlink-0000001473697338-V2#section2976114355019 以二维数组监听为例
Observed
class StringArray extends ArrayString {
}Component
struct ItemPage {ObjectLink itemArr: StringArray;build() {Row() {Text(ItemPage).width(100).height(100)ForEach(this.itemArr,item {Text(item).width(100).height(100)},item item)}}
}Entry
Component
struct Index {State arr: ArrayStringArray [new StringArray(), new StringArray(), new StringArray()];build() {Column() {ItemPage({ itemArr: this.arr[0] })ItemPage({ itemArr: this.arr[1] })ItemPage({ itemArr: this.arr[2] })Divider()ForEach(this.arr,itemArr {ItemPage({ itemArr: itemArr })},itemArr itemArr[0])Divider()Button(update).onClick(() {console.error(Update all items in arr);if (this.arr[0][0] ! undefined) {// 正常情况下需要有一个真实的ID来与ForEach一起使用但此处没有// 因此需要确保推送的字符串是唯一的。this.arr[0].push(${this.arr[0].slice(-1).pop()}${this.arr[0].slice(-1).pop()});this.arr[1].push(${this.arr[1].slice(-1).pop()}${this.arr[1].slice(-1).pop()});this.arr[2].push(${this.arr[2].slice(-1).pop()}${this.arr[2].slice(-1).pop()});} else {this.arr[0].push(Hello);this.arr[1].push(World);this.arr[2].push(!);}})}}
}效果如下 3.2 App级别的状态管理
ArkTS可以实现多种应用状态管理的能力具体表现在以下几点
LocalStorage: 是页面级的状态储存通常用于 UIAblility内也可以用于页面间状态共享AppStorage: 特殊的单例 LocalStorage 对象为程序的 UI 状态属性提供中央储存PersistentStorage: 持久化 UI 状态储存通常于 AppStorage 一起使用可以把 AppStorage 存储的数据写入磁盘确保数据在重启前后保持一致Environment: 应用程序运行的设备环境参数会同步到 AppStorage 中可以和AppStorage 搭配使用
3.2.1 LocalStorage: 页面级 UI 状态存储
有关 LocalStorage 介绍
一个应用程序可以创建多个 LocalStorage 实例这些实例可以在页面内共享也可以通过 GetShared 接口在 UIAbility 中创建的实例实现跨页面和 UIAbility 内的共享。根据组件树的结构被 Entry 装饰的 Component 实例可以被分配一个 LocalStorage 实例而该组件的所有子组件实例将自动获得对该 LocalStorage 实例的访问权限。被 Component 装饰的组件最多可以访问一个 LocalStorage 实例和 AppStorage而未被 Entry 装饰的组件无法独立分配 LocalStorage 实例只能接受父组件通过 Entry 传递来的 LocalStorage 实例。一个 LocalStorage 实例在组件树上可以被分配给多个组件而其中的所有属性都是可变的。当应用释放最后一个指向LocalStorage的引用时比如销毁最后一个自定义组件LocalStorage将被JS Engine垃圾回收。被绑定的属性值变化无论父子级关系如何都会引起依赖此变量的 UI 刷新渲染。
介绍两个用于属性绑定的装饰器
LocalStorageProp: LocalStorageProp(key) 是一个装饰器用于在 ArkUI 组件框架中建立自定义组件的属性与 LocalStorage 中特定键对应属性之间的单向数据同步。以下是一些关键信息 初始化与绑定 在自定义组件初始化时被 LocalStorageProp(key) 装饰的变量通过给定的 key 绑定到相应的 LocalStorage 属性上完成初始化。本地初始化是必要的因为不能保证在组件初始化之前 LocalStorage 中是否存在给定的 key。 本地修改和同步 对于使用 LocalStorageProp(key) 装饰的变量本地的修改是允许的。但是需要注意本地的修改永远不会同步回 LocalStorage 中。相反如果 LocalStorage 中给定 key 的属性发生改变这个改变会被同步给被 LocalStorageProp(key) 装饰的变量并覆盖本地的修改。 LocalStorageLinkLocalStorageLink 是一个装饰器用于在 ArkUI 组件框架中建立自定义组件的状态变量与 LocalStorage 中特定键对应属性之间的双向数据同步。以下是相关信息的概述 同步规则 LocalStorageLink(key) 与 LocalStorage 中给定 key 对应的属性建立双向数据同步。这包括本地的修改会同步回 LocalStorage 中以及 LocalStorage 中的修改会被同步到所有绑定了相同 key 的属性上。初始化规则LocalStorageLink 不支持从父节点初始化只能从 LocalStorage 中的 key 对应的属性初始化。如果没有对应的 key则使用本地默认值初始化。可用于初始化 State、Link、Prop、Provide但不支持组件外访问
语法示例
let storage new LocalStorage({ KeyA: 47 }); // 创建新实例并使用给定对象初始化
let keyA storage.get(KeyA); // keyA 47let link1 storage.link(KeyA); // link1.get() 47
let link2 storage.link(KeyA); // link2.get() 47let prop storage.prop(KeyA); // prop.get() 47link1.set(48); // 双向绑定 link1.get() link2.get() prop.get() 48prop.set(1); // 单向绑定 prop.get() 1但是 link1.get() link2.get() 48link1.set(49); // 双向绑定 link1.get() link2.get() prop.get() 49
案例理解
// 创建新实例并使用给定对象初始化
let localStorageInstance new LocalStorage({ PropertyA: 47 });Component
struct ChildComponent {// 与LocalStorage中的PropertyA属性建立双向绑定LocalStorageLink(PropertyA) linkedProperty2: number 1;// 与LocalStorage中的PropertyA属性建立单向绑定LocalStorageProp(PropertyA) linkedProperty3: number 1;build() {Row() {Button(子组件的值 ${this.linkedProperty2})// 更改将同步至LocalStorage中的PropertyA以及ParentComponent.linkedProperty1.onClick(() this.linkedProperty2 1)// Local 中值变化linkedProperty3 也会变反之不会Button(子组件的值-单向 Prop ${this.linkedProperty3}).onClick(() this.linkedProperty3 1)}}
}// 使LocalStorage可被Component组件访问
Entry(localStorageInstance)
Component
struct ParentComponent {// LocalStorageLink变量装饰器与LocalStorage中的PropertyA属性建立双向绑定LocalStorageLink(PropertyA) linkedProperty1: number 1;build() {Column({ space: 15 }) {Button(父组件的值 ${this.linkedProperty1}) // 初始化值从LocalStorage中获取因为PropertyA已经初始化为47.onClick(() this.linkedProperty1 1)// Component子组件自动获得对ParentComponent LocalStorage实例的访问权限。ChildComponent()}}
}效果如下 3.2.2 AppStorage应用全局的UI状态存储
概述
AppStorage是应用全局的UI状态存储是和应用的进程绑定的由UI框架在应用程序启动时创建为应用程序UI状态属性提供中央存储。AppStorage是应用级的全局状态共享相当于整个应用的“中枢”持久化数据PersistentStorage和 环境变量Environment都是通过AppStorage的中转才可以和UI交互。AppStorage中的属性可以被双向同步数据可以是存在于本地或远程设备上并具有不同的功能比如数据持久化。
基本用法
// 在AppStorage中设置或创建 PropA 属性初始值为 47
AppStorage.SetOrCreate(PropA, 47);let localStorageInstance: LocalStorage new LocalStorage({ PropA: 17 });// 从AppStorage中获取 PropA 的值此时 propA 在 AppStorage 中为 47在 LocalStorage 中为 17
let propAFromAppStorage: number AppStorage.Get(PropA);// 使用AppStorage的Link方法创建两个链接link1和link2以同步 PropA 的值
let link1: SubscribedAbstractPropertynumber AppStorage.Link(PropA);
let link2: SubscribedAbstractPropertynumber AppStorage.Link(PropA);// 使用AppStorage的Prop方法创建一个属性prop以单向同步 PropA 的值
let prop: SubscribedAbstractPropertynumber AppStorage.Prop(PropA);// 修改 link1演示双向绑定的效果所有其他绑定到相同键的变量都同步更新
link1.set(48); // link1.get() link2.get() prop.get() 48// 修改属性 prop演示单向绑定的效果只有属性本身更新其他变量不受影响
prop.set(1); // prop.get() 1但是 link1.get() link2.get() 48// 再次修改 link1验证双向绑定所有绑定到相同键的变量都同步更新
link1.set(49); // link1.get() link2.get() prop.get() 49// 使用 PropA 的值从 LocalStorage 中获取此时为 17
let valueFromLocalStorage: number localStorageInstance.get(PropA);// 设置 PropA 的值为 101
localStorageInstance.set(PropA, 101);// 从 LocalStorage 中获取 PropA 的值此时为 101
let valueFromLocalStorageAfterSet: number localStorageInstance.get(PropA);// 从 AppStorage 中获取 PropA 的值此时为 49
let valueFromAppStorage: number AppStorage.Get(PropA);// 获取 link1 的值此时为 49
let valueFromLink1: number link1.get();// 获取 link2 的值此时为 49
let valueFromLink2: number link2.get();// 获取 prop 的值此时为 49
let valueFromProp: number prop.get();
3.2.3 PersistentStorage持久化存储UI状态
LocalStorage和AppStorage都是运行时的内存但是在应用退出再次启动后依然能保存选定的结果是应用开发中十分常见的现象这就需要用到PersistentStorage。
注意
持久化数据操作相对较慢应避免持久化大型数据集和经常变化的变量。PersistentStorage的持久化变量最好是小于2kb的数据以避免影响UI渲染性能。PersistentStorage只能在UI页面内使用否则无法持久化数据。不在调用 PersistentStorage 前调用 AppStorage会导致上次退出应用保存的值丢失。
代码示例
PersistentStorage.PersistProp(userScore, 100);Entry
Component
struct Game {State message: string Welcome to the GameStorageLink(userScore) userScore: number 50build() {Row() {Column() {Text(this.message)Text(你的得分: ${this.userScore}).onClick(() {this.userScore 10;})}}}
}3.2.4 Environment设备环境查询
Environment设备环境查询用于查询设备运行环境参数是ArkUI框架在应用程序启动时创建的单例对象它为AppStorage提供了一系列描述应用程序运行状态的属性。Environment的所有属性都是不可变的即应用不可写入所有的属性都是简单类型。
使用场景
Environment.EnvProp将设备的语言设置为英语 zh。然后StorageProp将设备语言与AppStorage中的 deviceLanguage 建立了单向同步。
// 将设备的语言code存入AppStorage
Environment.EnvProp(deviceLanguage, zh);Entry
Componentstruct Main {// 使用StorageProp链接到Component中StorageProp(deviceLanguage) selectedLanguage: string en;build() {Row() {Column() {Text(Device Language:)Text(this.selectedLanguage)}}}
}// 应用逻辑使用Environment
// 从AppStorage获取单向绑定的languageCode的变量
const lang: SubscribedAbstractPropertystring AppStorage.Prop(deviceLanguage);
if (lang.get() zh) {console.info(你好);
} else {console.info(Hello!);
}3.3 其他状态管理
3.3.1 Watch 监听
Watch用于监听状态变量的变化当状态变量变化时Watch的回调方法将被调用。Watch在ArkUI框架内部判断数值有无更新使用的是严格相等遵循严格相等规范。当在严格相等为false的情况下就会触发Watch的回调。
观察变化和行为表现
当观察到状态变量的变化包括双向绑定的AppStorage和LocalStorage中对应的key发生的变化的时候对应的Watch的回调方法将被触发Watch方法在自定义组件的属性变更之后同步执行如果在Watch的方法里改变了其他的状态变量也会引起状态变更和Watch的执行在第一次初始化的时候Watch装饰的方法不会被调用即认为初始化不是状态变量的改变。只有在后续状态改变时才会调用Watch回调方法。
注意点
避免无限循环。循环可能是因为在Watch的回调方法里直接或者间接地修改了同一个状态变量引起的。为了避免循环的产生建议不要在Watch的回调方法里修改当前装饰的状态变量开发者应关注性能属性值更新函数会延迟组件的重新渲染具体请见上面的行为表现因此回调函数应仅执行快速运算不建议在Watch函数中调用async await因为Watch设计的用途是为了快速的计算异步行为可能会导致重新渲染速度的性能问题。
示例
Component
struct TotalView {Prop Watch(onCountUpdated) count: number;State total: number 0;// Watch cbonCountUpdated(propName: string): void {this.total this.count;}build() {Text(Total: ${this.total})}
}Entry
Component
struct CountModifier {State count: number 0;build() {Column() {Button(add to basket).onClick(() {this.count})TotalView({ count: this.count })}}
}3.3.2 $$语法内置组件双向同步
$$运算符为系统内置组件提供TS变量的引用使得TS变量和系统内置组件的内部状态保持同步。
内部状态具体指什么取决于组件。
当前$$支持基础类型变量以及State、Link和Prop装饰的变量。当前$$仅支持 bindPopup 属性方法的show参数Radio组件的checked属性Refresh组件的refreshing参数。$$绑定的变量变化时会触发UI的同步刷新。 Entry
Component
struct bindPopupPage {State customPopup: boolean false;build() {Column() {Button(Popup).margin(20).onClick(() {this.customPopup !this.customPopup}).bindPopup($$this.customPopup, {message: showPopup})}}
}4. 渲染控制
tch方法在自定义组件的属性变更之后同步执行 3. 如果在Watch的方法里改变了其他的状态变量也会引起状态变更和Watch的执行 4. 在第一次初始化的时候Watch装饰的方法不会被调用即认为初始化不是状态变量的改变。只有在后续状态改变时才会调用Watch回调方法。
注意点
避免无限循环。循环可能是因为在Watch的回调方法里直接或者间接地修改了同一个状态变量引起的。为了避免循环的产生建议不要在Watch的回调方法里修改当前装饰的状态变量开发者应关注性能属性值更新函数会延迟组件的重新渲染具体请见上面的行为表现因此回调函数应仅执行快速运算不建议在Watch函数中调用async await因为Watch设计的用途是为了快速的计算异步行为可能会导致重新渲染速度的性能问题。
示例
Component
struct TotalView {Prop Watch(onCountUpdated) count: number;State total: number 0;// Watch cbonCountUpdated(propName: string): void {this.total this.count;}build() {Text(Total: ${this.total})}
}Entry
Component
struct CountModifier {State count: number 0;build() {Column() {Button(add to basket).onClick(() {this.count})TotalView({ count: this.count })}}
}3.3.2 $$语法内置组件双向同步
$$运算符为系统内置组件提供TS变量的引用使得TS变量和系统内置组件的内部状态保持同步。
内部状态具体指什么取决于组件。
当前$$支持基础类型变量以及State、Link和Prop装饰的变量。当前$$仅支持 bindPopup 属性方法的show参数Radio组件的checked属性Refresh组件的refreshing参数。$$绑定的变量变化时会触发UI的同步刷新。 Entry
Component
struct bindPopupPage {State customPopup: boolean false;build() {Column() {Button(Popup).margin(20).onClick(() {this.customPopup !this.customPopup}).bindPopup($$this.customPopup, {message: showPopup})}}
}4. 渲染控制
对于 UI 渲染可以基于数据结构选择一些内置方法例如ForEach快速渲染 UI 结构。
4.1 if-else条件渲染
ArkTS提供了渲染控制的能力。条件渲染可根据应用的不同状态使用if、else和else if渲染对应状态下的UI内容。
条件渲染语句允许基于条件在组件内构建不同子组件支持if、else、else if语句条件可以使用状态变量但需遵循父子组件关系规则确保每个分支内创建至少一个组件且子组件类型和数量需符合父组件限制。
代码示例
Entry
Component
struct ViewA {State count: number 0;build() {Column() {Text(count${this.count})if (this.count 0) {Text(count 为正数).fontColor(Color.Green)} else {Text(count 为负数).fontColor(Color.Red)}Button(增 count).onClick(() {this.count;})Button(减 count).onClick(() {this.count--;})}}
}图示 4.2 ForEach循环渲染
必须使用数组允许空数组设置的循环函数不允许改变源数组。 ForEach组件接受arr属性作为数组输入必须是数组类型允许为空数组可用返回数组的函数。数组为空时不会创建子组件。 itemGenerator是必需的是一个lambda函数根据数组中的每个数据项生成一个或多个子组件。生成的子组件类型必须符合ForEach的父容器组件的规定。 可选的keyGenerator是一个匿名函数用于生成数组中每个数据项的唯一键值。虽然可选但建议提供以优化性能和识别数组更改。在没有提供键值生成器的情况下反转数组可能导致ForEach中的所有节点重建。
简单的示例
Entry
Component
struct MyComponent {State arr: number[] [10, 20, 30];build() {Column({ space: 5 }) {Button(翻转数组).onClick(() {this.arr.reverse();})ForEach(this.arr, (item: number) {Text(此项值: ${item}).fontSize(18)Divider().strokeWidth(2)}, (item: number) item.toString())}}
}效果如下 配合 ObjectLInk 的 ForEach 示例
let NextID: number 0;Observed
class MyCounter {public id: number;public c: number;constructor(c: number) {this.id NextID;this.c c;}
}Component
struct CounterView {ObjectLink counter: MyCounter;label: string 计数器视图;build() {Button(计数器视图 [${this.label}] this.counter.c${this.counter.c} 1).width(400).height(50).onClick(() {this.counter.c 1;})}
}Entry
Component
struct MainView {State firstIndex: number 0;State counters: ArrayMyCounter [new MyCounter(0), new MyCounter(0), new MyCounter(0),new MyCounter(0), new MyCounter(0)];build() {Column() {ForEach(this.counters.slice(this.firstIndex, this.firstIndex 3),(item) {CounterView({ label: 计数器项目 #${item.id}, counter: item })},(item) item.id.toString())Button(计数器向上移动).width(200).height(50).onClick(() {this.firstIndex Math.min(this.firstIndex 1, this.counters.length - 3);})Button(计数器向下移动).width(200).height(50).onClick(() {this.firstIndex Math.max(0, this.firstIndex - 1);})}}
}图示 4.3 LazyForEach数据懒加载
LazyForEach是一个用于按需迭代数据并创建组件的接口适用于滚动容器以提高性能。它接受数据源、子组件生成函数和可选的键值生成函数作为参数。在每次迭代中子组件生成函数生成一个子组件并且键值生成函数可选地用于为数据项生成唯一的键值。
注意键值生成器应确保生成的键值唯一以避免框架忽略相同键值的UI组件。数据源接口IDataSource定义了操作数据的方法包括获取总数、获取特定索引的数据以及注册和注销数据变化监听器。
同时数据变化监听器DataChangeListener提供了各种通知方法如重新加载数据、数据添加、数据移动、数据删除和数据变化。在使用LazyForEach时需要注意它必须在支持懒加载的容器组件内使用且生成的子组件必须符合容器组件的规定。
IDataSource类型说明
interface IDataSource {totalCount(): number; // 获得数据总数getData(index: number): any; // 获取索引值对应的数据registerDataChangeListener(listener: DataChangeListener): void; // 注册数据改变的监听器unregisterDataChangeListener(listener: DataChangeListener): void; // 注销数据改变的监听器
}DataChangeListener类型说明
interface DataChangeListener {onDataReloaded(): void; // 重新加载数据时调用onDataAdded(index: number): void; // 添加数据时调用onDataMoved(from: number, to: number): void; // 数据移动起始位置与数据移动目标位置交换时调用onDataDeleted(index: number): void; // 删除数据时调用onDataChanged(index: number): void; // 改变数据时调用onDataAdd(index: number): void; // 添加数据时调用onDataMove(from: number, to: number): void; // 数据移动起始位置与数据移动目标位置交换时调用onDataDelete(index: number): void; // 删除数据时调用onDataChange(index: number): void; // 改变数据时调用
}懒加载示例
// 实现基本的IDataSource以处理数据监听器
class BasicDataSource implements IDataSource {private listeners: DataChangeListener[] [];// 获取数据总数public totalCount(): number {return 0;}// 获取特定索引的数据public getData(index: number): any {return undefined;}// 注册数据变化监听器registerDataChangeListener(listener: DataChangeListener): void {if (this.listeners.indexOf(listener) 0) {console.info(添加监听器);this.listeners.push(listener);}}// 注销数据变化监听器unregisterDataChangeListener(listener: DataChangeListener): void {const pos this.listeners.indexOf(listener);if (pos 0) {console.info(移除监听器);this.listeners.splice(pos, 1);}}// 通知数据重新加载notifyDataReload(): void {this.listeners.forEach(listener {listener.onDataReloaded();});}// 通知数据添加notifyDataAdd(index: number): void {this.listeners.forEach(listener {listener.onDataAdd(index);});}// 通知数据变化notifyDataChange(index: number): void {this.listeners.forEach(listener {listener.onDataChange(index);});}// 通知数据删除notifyDataDelete(index: number): void {this.listeners.forEach(listener {listener.onDataDelete(index);});}// 通知数据移动notifyDataMove(from: number, to: number): void {this.listeners.forEach(listener {listener.onDataMove(from, to);});}
}class MyDataSource extends BasicDataSource {private dataArray: string[] [];// 获取数据总数public totalCount(): number {return this.dataArray.length;}// 获取特定索引的数据public getData(index: number): any {return this.dataArray[index];}// 添加数据public addData(index: number, data: string): void {this.dataArray.splice(index, 0, data);this.notifyDataAdd(index);}// 推送数据public pushData(data: string): void {this.dataArray.push(data);this.notifyDataAdd(this.dataArray.length - 1);}
}Entry
Component
struct MyComponent {aboutToAppear() {for (var i 100; i 80; i--) {this.data.pushData(Hello ${i});}}private data: MyDataSource new MyDataSource();build() {List({ space: 3 }) {// 使用LazyForEach按需迭代数据LazyForEach(this.data, (item: string) {ListItem() {Row() {Text(item).fontSize(50).onAppear(() {console.info(出现 item);});}.margin({ left: 10, right: 10 });}.onClick(() {this.data.pushData(Hello ${this.data.totalCount()});});}, item item);}.cachedCount(5); // 设置缓存的数据数量}
}
图示 文章参考https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/arkts-get-started-0000001504769321-V2