OpenHarmony 入门——ArkUI 自定义组件间的父子双向同步状态装饰器@Link语法(四)
文章大纲
- 引言
- 一、组件间状态装饰器@Link 父子双向同步
- 1、使用规则
- 2、支持的观察变化的场景和ArkUI 刷新UI
- 3、@Link变量值初始化和更新机制
- 3.1、初始渲染:执行父组件的build()函数后将创建子组件的新实例。
- 3.2、@Link的数据源的更新:即父组件中状态变量更新,引起相关子组件的@Link的更新。
- 3.2、@Link的更新
- 二、@Link父子双向同步
- 1、简单类型和类对象类型的@Link
- 2、数组类型的@Link
引言
前一篇文章OpenHarmony 入门——ArkUI 自定义组件间父到子单向同步的装饰器@Prop语法(三) 介绍了父组件——>子组件数据同步的装饰器@Prop,今天介绍的是父<——>子双向同步的装饰器@Link.
一、组件间状态装饰器@Link 父子双向同步
父组件中@State, @StorageLink和@Link 和子组件@Link可以建立双向数据同步,@Link 的变量不用初始化。
@Link装饰器不能在@Entry装饰的自定义组件中使用。
1、使用规则
2、支持的观察变化的场景和ArkUI 刷新UI
-
当装饰的数据类型为boolean、string、number类型时,可以同步观察到数值的变化。
-
当装饰的数据类型为class或者Object时,可以观察到赋值和属性赋值的变化,即Object.keys(observedObject)返回的所有属性。
-
当装饰的对象是array时,可以观察到数组添加、删除、更新数组单元的变化
-
当装饰的对象是Date时,可以观察到Date整体的赋值,同时可通过调用Date的接口setFullYear, setMonth, setDate, setHours, setMinutes, setSeconds, setMilliseconds, setTime, setUTCFullYear, setUTCMonth, setUTCDate, setUTCHours, setUTCMinutes, setUTCSeconds, setUTCMilliseconds 更新Date的属性。
-
当装饰的变量是Map时,可以观察到Map整体的赋值,同时可通过调用Map的接口set, clear, delete 更新Map的值。
-
当装饰的变量是Set时,可以观察到Set整体的赋值,同时可通过调用Set的接口add, clear, delete 更新Set的值。
3、@Link变量值初始化和更新机制
@Link装饰的变量和其所属的自定义组件共享生命周期。父组件和拥有@Link变量的子组件初始渲染和双向更新流程如下(以父组件为@State为例):
3.1、初始渲染:执行父组件的build()函数后将创建子组件的新实例。
初始化过程如下:
- 必须指定父组件中的@State变量,用于初始化子组件的@Link变量。
- 子组件的@Link变量值与其父组件的数据源变量保持同步(双向数据同步)。
父组件的@State状态变量包装类通过构造函数传给子组件,子组件的@Link包装类拿到父组件的@State的状态变量后,将当前@Link包装类this指针注册给父组件的@State变量。
3.2、@Link的数据源的更新:即父组件中状态变量更新,引起相关子组件的@Link的更新。
处理步骤:
- 通过初始渲染的步骤可知,子组件@Link包装类把当前this指针注册给父组件。
- 父组件@State变量变更后,会遍历更新所有依赖它的系统组件(elementid)和状态变量(比如@Link包装类)。
- 通知@Link包装类更新后,子组件中所有依赖@Link状态变量的系统组件(elementId)都会被通知更新。以此实现父组件对子组件的状态数据同步。
3.2、@Link的更新
当子组件中@Link更新后,处理步骤如下(以父组件为@State为例):
- @Link更新后,调用父组件的@State包装类的set方法,将更新后的数值同步回父组件。
- 子组件@Link和父组件@State分别遍历依赖的系统组件,进行对应的UI的更新。以此实现子组件@Link同步回父组件@State。
二、@Link父子双向同步
父子双向同步,父组件中@State, @StorageLink和@Link 和子组件@Link可以建立双向数据同步
@Link 的变量不用初始化
1、简单类型和类对象类型的@Link
class GreenButtonState {
width: number = 0;
constructor(width: number) {
this.width = width;
}
}
@Component
struct GreenButton {
@Link greenButtonState: GreenButtonState;
build() {
Button('更新Green @Link属性')
.width(this.greenButtonState.width)
.height(100.0)
.backgroundColor('#00ff00')
.onClick(() => {
if (this.greenButtonState.width < 700) {
// 更新@Link 修饰的class的属性,变化可以被观察到并同步到父组件
this.greenButtonState.width += 125;
} else {
// 更新class,变化可以被观察到同步回父组件
this.greenButtonState = new GreenButtonState(100);
}
})
}
}
@Component
struct YellowButton {
@Link yellowButtonState: number;
build() {
Button('更新子组件的Yellow @Link')
.width(this.yellowButtonState)
.height(120.0)
.backgroundColor('#ffff00')
.onClick(() => {
// 子组件的简单类型可以同步回父组件
this.yellowButtonState += 50.0;
})
}
}
@Entry
@Component
struct ShufflingContainer {
@State greenButtonState: GreenButtonState = new GreenButtonState(300);
@State yellowButtonProp: number = 100;
build() {
Column() {
// 简单类型从父组件@State向子组件@Link数据同步
Button('Parent View: Set@#State yellowButton')
.onClick(() => {
this.yellowButtonProp = (this.yellowButtonProp < 700) ? this.yellowButtonProp + 100 : 100;
})
Divider()
// class类型从父组件@State向子组件@Link数据同步
Button('Parent View: Set@State GreenButton')
.onClick(() => {
this.greenButtonState = new GreenButtonState(80);
//this.greenButtonState.width = (this.greenButtonState.width < 700) ? this.greenButtonState.width + 100 : 100;
})
Divider()
// class类型初始化@Link
GreenButton({ greenButtonState: this.greenButtonState })
// 简单类型初始化@Link
YellowButton({ yellowButtonState: this.yellowButtonProp })
}
}
}
2、数组类型的@Link
@Component
struct Child2 {
@Link items: number[];
build() {
Column() {
Button(`Button1: push`).onClick(() => {
this.items.push(this.items.length + 1);
})
Button(`Button2: replace whole item`).onClick(() => {
this.items = [100, 200, 300];
})
}
}
}
@Component
struct Parent {
@State arr: number[] = [1, 2, 3];
build() {
Column() {
Child2({ items: $arr })
ForEach(this.arr,
(item:number) => {
Text(`${item}`)
},
(item:number) => item.toString()
)
}
}
}