未验证 提交 ba673c62 编写于 作者: O openharmony_ci 提交者: Gitee

!10887 指南整改优化1

Merge pull request !10887 from luoying_ace/ly1020
......@@ -4,9 +4,9 @@ ArkTS通过装饰器@Component和@Entry装饰struct关键字声明的数据结
## 基本概念
- struct:组件可以基于struct实现,不能有继承关系,对于struct的实例化,可以省略new。
- struct:自定义组件可以基于struct实现,不能有继承关系,对于struct的实例化,可以省略new。
- 装饰器:装饰器给被装饰的对象赋予某一种能力,其不仅可以装饰类或结构体,还可以装饰类的属性。多个装饰器可以叠加到目标元素,定义在同一行上或者多行上,推荐定义在多行上
- 装饰器:装饰器给被装饰的对象赋予某一种能力,其不仅可以装饰类或结构体,还可以装饰类的属性。多个装饰器可以叠加到目标元素上,定义在同一行中或者分开多行,推荐分开多行定义
```ts
@Entry
......@@ -15,7 +15,7 @@ ArkTS通过装饰器@Component和@Entry装饰struct关键字声明的数据结
}
```
- build函数:自定义组件必须定义build函数,并且自定义组件禁止自定义构造函数。build函数满足Builder构造器接口定义,用于定义组件的声明式UI描述。
- build函数:自定义组件必须定义build函数,并且禁止自定义构造函数。build函数满足Builder构造器接口定义,用于定义组件的声明式UI描述。
```ts
interface Builder {
......@@ -23,21 +23,21 @@ ArkTS通过装饰器@Component和@Entry装饰struct关键字声明的数据结
}
```
- @Component:装饰struct,结构体在装饰后具有基于组件的能力,需要实现build方法来更新UI。
- @Component:装饰struct,结构体在装饰后具有基于组件的能力,需要实现build方法来创建UI。
- @Entry: 装饰struct,组件被装饰后作为页面的入口,页面加载时将被渲染显示。
- @Preview:装饰struct, 用@Preview装饰的自定义组件可以在DevEco Studio的预览器上进行实时预览,加载页面时,将创建并呈现@Preview装饰的自定义组件。
- @Preview:装饰struct, 用@Preview装饰的自定义组件可以在DevEco Studio的预览器上进行实时预览,加载页面时,将创建并显示@Preview装饰的自定义组件。
> **说明:** 在单个源文件中,最多可以使用10个@Preview装饰自定义组件,更多说明请参考[查看ArkTS组件预览效果](https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ohos-previewing-app-service-0000001218760596#section146052489820)。
- 链式调用:以 "." 链式调用的方式配置UI结构及其属性、事件等。
- 链式调用:以 "." 链式调用的方式配置UI组件的属性方法、事件方法等。
## UI描述规范
### 无参数构造配置
组件的接口定义不包含必选构造参数,组件后面的“()”中不需要配置任何内容。例如,Divider组件不包含构造参数:
如果组件的接口定义中不包含必选构造参数,组件后面的“()”中不需要配置任何内容。例如,Divider组件不包含构造参数:
```ts
Column() {
......@@ -49,27 +49,27 @@ Column() {
### 必选参数构造配置
如果组件的接口定义中包含必选构造参数,则在组件后面的“()”中必须配置参数,参数可以使用常量进行赋值。
如果组件的接口定义中包含必选构造参数,则在组件后面的“()”中必须配置相应参数,参数可以使用常量进行赋值。
例如:
- Image组件的必选参数src:
```ts
Image('http://xyz/a.jpg')
Image('https://xyz/test.jpg')
```
- Text组件的必选参数content:
```ts
Text('123')
Text('test')
```
变量或表达式也可以用于参数赋值,其中表达式返回的结果类型必须满足参数类型要求。例如,传递变量或表达式来构造Image和Text组件的参数:
变量或表达式也可以用于参数赋值,其中表达式返回的结果类型必须满足参数类型要求,变量的定义详见[页面级变量的状态管理](arkts-state-mgmt-page-level.md)[应用级变量的状态管理](arkts-state-mgmt-application-level.md)。例如,设置变量或表达式来构造Image和Text组件的参数:
```ts
Image(this.imagePath)
Image('http://' + this.imageUrl)
Image('https://' + this.imageUrl)
Text(`count: ${this.count}`)
```
......@@ -80,14 +80,14 @@ Text(`count: ${this.count}`)
- 配置Text组件的字体大小属性:
```ts
Text('123')
Text('test')
.fontSize(12)
```
- 使用"."操作进行链式调用并同时配置组件的多个属性,如下所示:
- 使用"."运算符进行链式调用并同时配置组件的多个属性,如下所示:
```ts
Image('a.jpg')
Image('test.jpg')
.alt('error.jpg')
.width(100)
.height(100)
......@@ -98,12 +98,12 @@ Text(`count: ${this.count}`)
```ts
Text('hello')
.fontSize(this.size)
Image('a.jpg')
Image('test.jpg')
.width(this.count % 2 === 0 ? 100 : 200)
.height(this.offset + 100)
```
- 对于系统内置组件,框架还为其属性预定义了一些[枚举类型](../reference/arkui-ts/ts-appendix-enums.md)供开发人员调用,枚举类型必须满足参数类型要求,枚举值可以作为参数传递。可以按以下方式配置Text组件的颜色和字体属性:
- 对于系统内置组件,框架还为其属性预定义了一些[枚举类型](../reference/arkui-ts/ts-appendix-enums.md)供开发人员调用,枚举类型可以作为参数传递,且必须满足参数类型要求。例如,可以按以下方式配置Text组件的颜色和字体属性:
```ts
Text('hello')
......@@ -114,7 +114,7 @@ Text(`count: ${this.count}`)
### 事件配置
通过事件方法可以配置组件支持的事件。
通过事件方法可以配置组件支持的事件,事件方法紧随组件,并用"."运算符连接
- 使用lambda表达式配置组件的事件方法:
......@@ -138,18 +138,18 @@ Text(`count: ${this.count}`)
```ts
myClickHandler(): void {
this.counter += 2
}
...
Button('add counter')
.onClick(this.myClickHandler)
```
### 子组件配置
对于支持子组件配置的组件,例如容器组件,在"{ ... }"里为组件添加子组件的UI描述。Column、Row、Stack、Button、Grid和List组件都是容器组件。
对于支持子组件配置的组件,例如容器组件,在"{ ... }"里为组件添加子组件的UI描述。Column、Row、Stack、Grid、List等组件都是容器组件。
- 以下是简单的Column示例:
......@@ -164,32 +164,40 @@ Text(`count: ${this.count}`)
}
```
- 可以嵌套多个子组件
- 容器组件之间也可以互相嵌套,实现相对复杂的多级嵌套效果
```ts
Column() {
Column() {
Button() {
Text('+ 1')
}.type(ButtonType.Capsule)
.onClick(() => console.log ('+1 clicked!'))
Image('1.jpg')
}
Divider()
Column() {
Button() {
Text('+ 2')
}.type(ButtonType.Capsule)
.onClick(() => console.log ('+2 clicked!'))
Image('2.jpg')
}
Divider()
Column() {
Button() {
Text('+ 3')
}.type(ButtonType.Capsule)
.onClick(() => console.log('+3 clicked!'))
Image('3.jpg')
}
}.alignItems(HorizontalAlign.Center)
Row() {
Image('test1.jpg')
.width(100)
.height(100)
Button('click +1')
.onClick(() => {
console.info('+1 clicked!')
})
}
Divider()
Row() {
Image('test2.jpg')
.width(100)
.height(100)
Button('click +2')
.onClick(() => {
console.info('+2 clicked!')
})
}
Divider()
Row() {
Image('test3.jpg')
.width(100)
.height(100)
Button('click +3')
.onClick(() => {
console.info('+3 clicked!')
})
}
}
```
\ No newline at end of file
# 动态构建UI元素
前面章节介绍的是如何创建一个组件内部UI结构固定的自定义组件,为了满足开发者自定义组件内部UI结构的需求,ArkTS同时提供了动态构建UI元素的能力。
[基本UI描述](arkts-basic-ui-description.md)介绍的是如何创建一个内部UI结构固定的自定义组件,为了满足开发者自定义组件内部UI结构的需求,ArkTS同时提供了动态构建UI元素的能力。
## @Builder
可通过@Builder装饰器进行描述,该装饰器可以修饰一个函数,此函数可以在build函数之外声明,并在build函数中或其他@Builder修饰的函数中使用,在一个自定义组件内快速生成多个布局内容。使用方式如下面示例所示。
可通过@Builder装饰器进行描述,该装饰器可以修饰一个函数,此函数可以在build函数之外声明,并在build函数中或其他@Builder修饰的函数中使用,从而实现在一个自定义组件内快速生成多个布局内容。使用方式如下面示例所示。
```ts
// xxx.ets
@Component
struct CompB {
@State CompValue: string = '';
@State CompValue: string = ''
aboutToAppear() {
console.info('CompB aboutToAppear.');
console.info('CompB aboutToAppear.')
}
aboutToDisappear() {
console.info('CompB aboutToDisappear.');
console.info('CompB aboutToDisappear.')
}
build() {
......@@ -33,22 +30,24 @@ struct CompB {
@Entry
@Component
struct CompA {
size1: number = 100;
@State CompValue1: string = "Hello,CompValue1";
@State CompValue2: string = "Hello,CompValue2";
@State CompValue3: string = "Hello,CompValue3";
size1: number = 100
@State CompValue1: string = "Hello,CompValue1"
@State CompValue2: string = "Hello,CompValue2"
@State CompValue3: string = "Hello,CompValue3"
// @Builder装饰的函数内使用自定义组件
// @Builder装饰的函数CompC内使用自定义组件CompB
@Builder CompC(value: string) {
CompB({ CompValue: value });
CompB({ CompValue: value })
}
@Builder SquareText(label: string) {
Text(label)
.fontSize(18)
.width(1 * this.size1)
.height(1 * this.size1)
}
// @Builder装饰的函数RowOfSquareTexts内使用@Builder装饰的函数SquareText
@Builder RowOfSquareTexts(label1: string, label2: string) {
Row() {
this.SquareText(label1)
......@@ -63,7 +62,6 @@ struct CompA {
Row() {
this.SquareText("A")
this.SquareText("B")
// or as long as tsc is used
}
.width(2 * this.size1)
.height(1 * this.size1)
......@@ -71,9 +69,9 @@ struct CompA {
this.RowOfSquareTexts("C", "D")
Column() {
// 使用三次@Builder装饰的自定义组件
this.CompC(this.CompValue1);
this.CompC(this.CompValue2);
this.CompC(this.CompValue3);
this.CompC(this.CompValue1)
this.CompC(this.CompValue2)
this.CompC(this.CompValue3)
}
.width(2 * this.size1)
.height(2 * this.size1)
......@@ -85,32 +83,33 @@ struct CompA {
```
## @BuilderParam<sup>8+<sup>
@BuilderParam装饰器用于修饰自定义组件内函数类型的属性(例如:`@BuilderParam content: () => any;`),并且在初始化自定义组件时被@BuilderParam修饰的属性必须赋值。
@BuilderParam装饰器用于修饰自定义组件内函数类型的属性(例如:`@BuilderParam noParam: () => void`),并且在初始化自定义组件时被@BuilderParam修饰的属性必须赋值。
### 引入动机
当开发者创建自定义组件,想对该组件添加特定功能时(如:仅对自定义组件添加一个点击跳转操作)。若直接在组件内嵌入事件方法,将会导致所有初始化该组件的地方均增加了该功能。为解决此问题,引入了@BuilderParam装饰器,此装饰器修饰的属性值可为@Builder修饰的方法,开发者可在初始化自定义组件时对此属性进行赋值,为自定义组件增加特定的的功能。
当开发者创建自定义组件,并想对该组件添加特定功能时(例如在自定义组件中添加一个点击跳转操作)。若直接在组件内嵌入事件方法,将会导致所有引入该自定义组件的地方均增加了该功能。为解决此问题,引入了@BuilderParam装饰器,此装饰器修饰的属性值可为@Builder装饰的函数,开发者可在初始化自定义组件时对此属性进行赋值,为自定义组件增加特定的功能。
### 参数初始化组件
通过参数初始化组件时,将@Builder装饰的方法赋值给@BuilderParam修饰的属性,并在自定义组件内调用content属性值。对@BuilderParam修饰的属性进行赋值时不带参数(如:`content: this.specificParam`),则此属性的类型需定义成无返回值的函数(如:`@BuilderParam content: () => void`)。若带参数(如:`callContent: this.specificParam1("111")`),则此属性的类型需定义成any(如:`@BuilderParam callContent: any;`)。
通过参数初始化组件时,将@Builder装饰的函数赋值给@BuilderParam修饰的属性,并在自定义组件内调用该属性值。若@BuilderParam修饰的属性在进行赋值时不带参数(如:`noParam: this.specificNoParam`),则此属性的类型需定义为无返回值的函数(如:`@BuilderParam noParam: () => void`);若带参数(如:`withParam: this.SpecificWithParam('WithParamA')`),则此属性的类型需定义成any(如:`@BuilderParam withParam: any`)。
```ts
// xxx.ets
@Component
struct CustomContainer {
header: string = "";
@BuilderParam noParam: () => void;
@BuilderParam withParam: any;
footer: string = "";
header: string = ''
@BuilderParam noParam: () => void
@BuilderParam withParam: any
footer: string = ''
build() {
Column() {
Text(this.header)
.fontSize(50)
.fontSize(30)
this.noParam()
this.withParam()
Text(this.footer)
.fontSize(50)
.fontSize(30)
}
}
}
......@@ -120,22 +119,32 @@ struct CustomContainer {
struct CustomContainerUser {
@Builder specificNoParam() {
Column() {
Text("noParam").fontSize(50)
Text('noParam').fontSize(30)
}
}
@Builder SpecificWithParam(label: string) {
Column() {
Text(label).fontSize(50)
Text(label).fontSize(30)
}
}
build() {
Column() {
CustomContainer({
header: "Header",
header: 'HeaderA',
noParam: this.specificNoParam,
withParam: this.SpecificWithParam('WithParamA'),
footer: 'FooterA'
})
Divider()
.strokeWidth(3)
.margin(10)
CustomContainer({
header: 'HeaderB',
noParam: this.specificNoParam,
withParam: this.SpecificWithParam("WithParam"),
footer: "Footer",
withParam: this.SpecificWithParam('WithParamB'),
footer: 'FooterB'
})
}
}
......@@ -144,45 +153,50 @@ struct CustomContainerUser {
### 尾随闭包初始化组件
在自定义组件中使用@BuilderParam修饰的属性接收尾随闭包(在初始化自定义组件时,组件名称紧跟一个大括号“{}”形成尾随闭包场景(`CustomComponent(){}`)。开发者可把尾随闭包看做一个容器,向其填充内容,如在闭包内增加组件(`{Column(){Text("content")}`),闭包内语法规范与build函数一致。此场景下自定义组件内有且仅有一个使用@BuilderParam修饰的属性。
在自定义组件中使用@BuilderParam修饰的属性时也可通过尾随闭包进行初始化(在初始化自定义组件时,组件后紧跟一个大括号“{}”形成尾随闭包场景(`CustomContainer(){}`)。开发者可把尾随闭包看做一个容器,向其中填充内容,如在闭包内增加组件(`{Column(){...}`),闭包内语法规范与build函数一致。此场景下自定义组件内有且仅有一个使用@BuilderParam修饰的属性。
示例:在闭包内增加Column组件并添加点击事件,在新增的Column组件内调用@Builder修饰的specificParam方法,点击Column组件后该改变自定义组件中header的属性值为“changeHeader”。并且在初始化自定义组件时会把尾随闭包的内容赋值给使用@BuilderParam修饰的closer属性。
示例:在闭包内添加Column组件并设置点击事件,在Column组件内调用@Builder修饰的specificParam函数,点击Column组件后将自定义组件CustomContainer中header的属性值由“header”改变为“changeHeader”。在初始化自定义组件CustomContainer时,尾随闭包的内容会被赋值给@BuilderParam修饰的closer属性。
```ts
// xxx.ets
@Component
struct CustomContainer {
header: string = "";
@BuilderParam closer: () => void;
header: string = ''
@BuilderParam closer: () => void
build() {
Column() {
Text(this.header)
.fontSize(50)
.fontSize(30)
this.closer()
}
}
}
@Builder function specificParam(label1: string, label2: string) {
Column() {
Text(label1)
.fontSize(50)
.fontSize(30)
Text(label2)
.fontSize(50)
.fontSize(30)
}
}
@Entry
@Component
struct CustomContainerUser {
@State text: string = "header"
@State text: string = 'header'
build() {
Column() {
CustomContainer({
header: this.text,
}){
Column(){
specificParam("111", "22")
}.onClick(()=>{
this.text = "changeHeader"
}) {
Column() {
specificParam('testA', 'testB')
}.backgroundColor(Color.Yellow)
.onClick(() => {
this.text = 'changeHeader'
})
}
}
......@@ -192,34 +206,41 @@ struct CustomContainerUser {
## @Styles
ArkTS为了避免开发者对重复样式的设置,通过@Styles装饰器可以将多条样式设置提炼成一个方法,直接在组件声明的位置使用。@Styles装饰器将新的属性函数添加到基本组件上,如Text、Column、Button等。当前@Styles仅支持通用属性。通过@Styles装饰器可以快速定义并复用组件的自定义样式
ArkTS为了避免开发者对重复样式的设置,通过@Styles装饰器可以将多个样式设置提炼成一个方法,直接在组件声明时调用,通过@Styles装饰器可以快速定义并复用自定义样式。当前@Styles仅支持通用属性
@Styles可以定义在组件内或组件外,在组件外定义时需在方法前添加function关键字,组件内定义时不需要添加function关键字。
@Styles可以定义在组件内或组件外,在组件外定义时需在方法名前面添加function关键字,组件内定义时则不需要添加function关键字。
```ts
// xxx.ets
@Styles function globalFancy() {
.backgroundColor(Color.Red)
@Styles function globalFancy () {
.width(150)
.height(100)
.backgroundColor(Color.Pink)
}
@Entry
@Component
struct FancyUse {
@Styles componentFancy() {
.backgroundColor(Color.Blue)
.width(100)
.height(200)
.backgroundColor(Color.Yellow)
}
build() {
Column({ space: 10 }) {
Text("Fancy")
Text('FancyA')
.globalFancy()
.width(100)
.height(100)
.fontSize(30)
Text("Fancy")
Text('FancyB')
.globalFancy()
.fontSize(20)
Text('FancyC')
.componentFancy()
.width(100)
.height(100)
.fontSize(30)
Text('FancyD')
.componentFancy()
.fontSize(20)
}
}
}
......@@ -227,35 +248,37 @@ struct FancyUse {
@Styles还可以在[StateStyles](../reference/arkui-ts/ts-universal-attributes-polymorphic-style.md)属性内部使用,在组件处于不同的状态时赋予相应的属性。
在StateStyles内可以直接调用组件外定义的Styles,但需要通过this关键字调用组件内定义的Styles
在StateStyles内可以直接调用组件外定义的@Styles方法,但需要通过this关键字调用组件内定义的@Styles方法
```ts
// xxx.ets
@Styles function globalFancy() {
.width(100)
.height(100)
@Styles function globalFancy () {
.width(120)
.height(120)
.backgroundColor(Color.Green)
}
@Entry
@Component
struct FancyUse {
@Styles componentFancy() {
.width(50)
.height(50)
.width(80)
.height(80)
.backgroundColor(Color.Red)
}
build() {
Row({ space: 10 }) {
Button() {
Text("Fancy")
}
.stateStyles({
normal: {
.width(80)
.height(80)
},
disabled: this.componentFancy,
pressed: globalFancy
})
Button('Fancy')
.stateStyles({
normal: {
.width(100)
.height(100)
.backgroundColor(Color.Blue)
},
disabled: this.componentFancy,
pressed: globalFancy
})
}
}
}
......@@ -263,14 +286,15 @@ struct FancyUse {
## @Extend
@Extend装饰器将新的属性函数添加到内置组件上,如Text、Column、Button等。通过@Extend装饰器可以快速的扩展原生组件。@Extend装饰器不能用在自定义组件struct定义框内。
@Extend装饰器将新的属性方法添加到Text、Column、Button等内置组件上,通过@Extend装饰器可以快速地扩展原生组件。@Extend不能定义在自定义组件struct内。
```ts
// xxx.ets
@Extend(Text) function fancy(fontSize: number) {
@Extend(Text) function fancy (fontSize: number) {
.fontColor(Color.Red)
.fontSize(fontSize)
.fontStyle(FontStyle.Italic)
.fontWeight(600)
}
@Entry
......@@ -282,62 +306,66 @@ struct FancyUse {
.fancy(16)
Text("Fancy")
.fancy(24)
Text("Fancy")
.fancy(32)
}
}
}
```
> **说明:**
>
> - @Extend装饰器不能用在自定义组件struct定义框内。
> - @Extend装饰器内仅支持属性函数语句
> - @Extend装饰器不能定义在自定义组件struct内。
> - @Extend装饰器内仅支持属性方法设置
## @CustomDialog
@CustomDialog装饰器用于装饰自定义弹窗,使得弹窗可以动态设置样式。
@CustomDialog装饰器用于装饰自定义弹窗组件,使得弹窗可以动态设置内容及样式。
```ts
// custom-dialog-demo.ets
// xxx.ets
@CustomDialog
struct DialogExample {
controller: CustomDialogController;
action: () => void;
build() {
Row() {
Button ("Close CustomDialog")
.onClick(() => {
this.controller.close();
this.action();
})
}.padding(20)
}
controller: CustomDialogController
action: () => void
build() {
Row() {
Button('Close CustomDialog')
.onClick(() => {
this.controller.close()
this.action()
})
}.padding(20)
}
}
@Entry
@Component
struct CustomDialogUser {
dialogController : CustomDialogController = new CustomDialogController({
builder: DialogExample({action: this.onAccept}),
cancel: this.existApp,
autoCancel: true
});
onAccept() {
console.log("onAccept");
}
existApp() {
console.log("Cancel dialog!");
}
dialogController: CustomDialogController = new CustomDialogController({
builder: DialogExample({ action: this.onAccept }),
cancel: this.existApp,
autoCancel: true
});
onAccept() {
console.info('onAccept');
}
build() {
Column() {
Button("Click to open Dialog")
.onClick(() => {
this.dialogController.open()
})
}
existApp() {
console.info('Cancel dialog!');
}
build() {
Column() {
Button('Click to open Dialog')
.onClick(() => {
this.dialogController.open()
})
}
}
}
```
......
......@@ -4,27 +4,27 @@ ArkTS是OpenHarmony优选的主力应用开发语言。ArkTS基于TypeScript(
- ArkTS继承了TS的所有特性。
- 当前,ArkTS在TS基础上主要扩展了[声明式UI](arkts-basic-ui-description.md)能力,让开发者以更简洁、更自然的方式开发高性能应用。
- 当前,ArkTS在TS的基础上主要扩展了[声明式UI](arkts-basic-ui-description.md)能力,让开发者能够以更简洁、更自然的方式开发高性能应用。
当前扩展的声明式UI包括如下特性。
当前扩展的声明式UI能力包括如下特性。
- [基本UI描述](arkts-basic-ui-description.md):ArkTS定义了各种装饰器、自定义组件、UI描述机制,再配合UI开发框架中的UI内置组件、事件方法、属性方法等共同构成了UI开发的主体。
- [状态管理](arkts-state-mgmt-page-level.md):ArkTS提供了多维度的状态管理机制,在UI开发框架中,和UI相关联的数据,不仅可以在组件内使用,还可以在不同组件层级间传递,比如父子组件之间、爷孙组件之间,也可以是全局范围内的传递,还可以是 跨设备传递。另外,从数据的传递形式来看,可分为只读的单向传递和可变更的双向传递。开发者可以灵活的利用这些能力来实现数据和UI的联动。
- [动态构建UI元素](arkts-dynamic-ui-elememt-building.md):ArkTS提供了动态构建UI元素的能力,不仅可以自定义组件内部的UI结构,还可复用组件样式,扩展原生组件。
- [渲染控制](arkts-rendering-control.md):ArkTS提供了渲染控制的能力。条件渲染可根据应用的不同状态,渲染对应状态下的部分内容。循环渲染可从数据源中迭代获取数据,并在每次迭代过程中创建相应的组件。
- [基本UI描述](arkts-basic-ui-description.md):ArkTS定义了各种装饰器、自定义组件、UI描述机制,再配合UI开发框架中的内置组件及其相关的事件方法、属性方法等共同构成了UI开发的主体。
- [状态管理](arkts-state-mgmt-page-level.md):ArkTS提供了多维度的状态管理机制,在UI开发框架中,和UI相关联的数据,不仅可以在组件内使用,还可以在不同组件层级间传递,比如父子组件之间、爷孙组件之间,也可以是应用全局范围内的传递,还可以是跨设备传递。另外,从数据的传递形式来看,可分为只读的单向传递和可变更的双向传递。开发者可以灵活的利用这些能力来实现数据和UI的联动。
- [动态构建UI元素](arkts-dynamic-ui-elememt-building.md):ArkTS提供了动态构建UI元素的能力,不仅可以自定义组件内部的UI结构,还可复用组件样式,扩展原生组件。
- [渲染控制](arkts-rendering-control.md):ArkTS提供了渲染控制的能力。条件渲染可根据应用的不同状态,渲染对应状态下的UI内容。循环渲染可从数据源中迭代获取数据,并在每次迭代过程中创建相应的组件。
- [使用限制与扩展](arkts-restrictions-and-extensions.md):ArkTS在使用过程中存在限制与约束,同时也扩展了双向绑定等能力。
- 未来,ArkTS会结合应用开发/运行的需求持续演进,逐步提供并行和并发能力增强、类型系统增强、分布式开发范式等更多特性。
- 未来,ArkTS会结合应用开发/运行的需求持续演进,逐步提供并行和并发能力增强、系统类型增强、分布式开发范式等更多特性。
下面我们以一个具体的示例来说明ArkTS的基本组成。如下图所示的代码示例,UI界面会两段文本和一个按钮,当开发者点击按钮时,文本内容会从'Hello World'变为 'Hello ArkUI'。
下面我们以一个具体的示例来说明ArkTS的基本组成。如下图所示的代码示例,UI界面包含两段文本、一条分割线和一个按钮,当开发者点击按钮时,文本内容会从'Hello World'变为 'Hello ArkUI'。
![arkts-get-started](figures/arkts-get-started.png)
这个示例中所包含的ArkTS声明式开发范式的基本组成说明如下:
- 装饰器: 用于装饰类、结构、方法以及变量,赋予其特殊的含义,如上述示例中@Entry、@Component和@State都是装饰器。 具体而言,@Component表示这是个自定义组件;@Entry则表示这是个入口组件;@State表示组件中的状态变量,这个状态变换会引起UI变更
- 装饰器: 用于装饰类、结构、方法以及变量,赋予其特殊的含义,如上述示例中@Entry、@Component和@State都是装饰器。 具体而言,@Component表示这是个自定义组件;@Entry则表示这是个入口组件;@State表示这是组件中的状态变量,这个变量变化会触发UI刷新
- 自定义组件:可复用的UI单元,可组合其他组件,如上述被@Component装饰的struct Hello。
- UI描述:声明式的方法来描述UI的结构,例如build()方法中的代码块。
- 内置组件:ArkTS中默认内置的基本组件和布局组件,开发者可以直接调用,如Column、Text、Divider、Button等。
- 内置组件:ArkTS中默认内置的基础组件、容器组件、媒体组件、绘制组件、画布组件等各种组件,开发者可以直接调用,如示例中的Column、Text、Divider、Button等。
- 属性方法:用于组件属性的配置,如fontSize()、width()、height()、color()等,可通过链式调用的方式设置多项属性。
- 事件方法:用于添加组件对事件的响应逻辑,统一通过事件方法进行设置,如跟随在Button后面的onClick()
- 事件方法:用于添加组件对事件的响应逻辑,如跟随在Button后面的onClick(),同样可以通过链式调用的方式设置多个事件响应逻辑
# 渲染控制
ArkTS也提供了渲染控制的能力。条件渲染可根据应用的不同状态,渲染对应状态下的部分内容。循环渲染可从数据源中迭代获取数据,并在每次迭代过程中创建相应的组件。
ArkTS也提供了渲染控制的能力。条件渲染可根据应用的不同状态,渲染对应状态下的UI内容。循环渲染可从数据源中迭代获取数据,并在每次迭代过程中创建相应的组件。
## 条件渲染
......@@ -9,61 +9,61 @@ ArkTS也提供了渲染控制的能力。条件渲染可根据应用的不同状
> **说明:**
>
> - if条件语句可以使用状态变量。
> - if/else条件语句可以使用状态变量。
>
> - 使用if可以使子组件的渲染依赖条件语句。
> - 使用if/else可以使子组件的渲染依赖条件语句。
>
> - 必须在容器组件内使用。
>
> - 某些容器组件限制子组件的类型或数量。将if放置在这些组件内时,这些限制将应用于if和else语句内创建的组件。例如,Grid组件的子组件仅支持GridItem组件,在Grid组件内使用if时,则if条件语句内仅允许使用GridItem组件。
> - 某些容器组件限制子组件的类型或数量,将if/else用于这些组件内时,这些限制将同样应用于if/else语句内创建的组件。例如,Grid容器组件的子组件仅支持GridItem组件,在Grid内使用if/else时,则if/else语句内也仅允许使用GridItem组件。
```ts
Column() {
if (this.count < 0) {
Text('count is negative')
} else if (this.count % 2 === 0) {
Divider()
Text('even')
} else {
Divider()
Text('odd')
}
if (this.count < 0) {
Text('count is negative').fontSize(14)
} else if (this.count % 2 === 0) {
Text('count is even').fontSize(14)
} else {
Text('count is odd').fontSize(14)
}
}
```
## 循环渲染
通过循环渲染(ForEach组件)来迭代数组,并为每个数组项创建相应的组件,可减少代码复杂度。
通过循环渲染(ForEach)从数组中获取数据,并为每个数据项创建相应的组件,可减少代码复杂度。
```
ForEach(
arr: any[],
itemGenerator: (item: any, index?: number) => void,
keyGenerator?: (item: any, index?: number) => string
)
ForEach(
arr: any[],
itemGenerator: (item: any, index?: number) => void,
keyGenerator?: (item: any, index?: number) => string
)
```
**参数:**
| 参数名 | 参数类型 | 必填 | 参数描述 |
| ------------- | ------------------------------------- | ---- | ------------------------------------------------------------ |
| arr | any[] | 是 | 必须是数组,允许空数组,空数组场景下不会创建子组件。同时允许设置返回值为数组类型的函数,例如arr.slice(1, 3),设置的函数不得改变包括数组本身在内的任何状态变量,如Array.splice、Array.sort或Array.reverse这些改变原数组的函数。 |
| itemGenerator | (item: any, index?: number) => void | 是 | 生成子组件的lambda函数,为给定数组项生成一个或多个子组件,单个组件和子组件列表必须括在大括号“{....}”中。 |
| keyGenerator | (item: any, index?: number) => string | 否 | 匿名参数,用于给定数组项生成唯一且稳定的键值。当子项在数组中的位置更改时,子项的键值不得更改,当数组中的子项被新项替换时,被替换项的键值和新项的键值必须不同。键值生成器的功能是可选的,但是,为了使开发框架能够更好地识别数组更改,提高性能,建议提供。如将数组反向时,如果没有提供键值生成器,则ForEach中的所有节点都将重建。 |
| arr | any[] | 是 | 必须是数组,允许设置为空数组,空数组场景下将不会创建子组件。同时允许设置返回值为数组类型的函数,例如arr.slice(1, 3),设置的函数不得改变包括数组本身在内的任何状态变量,如Array.splice、Array.sort或Array.reverse这些改变原数组的函数。 |
| itemGenerator | (item: any, index?: number) => void | 是 | 生成子组件的lambda函数,为数组中的每一个数据项创建一个或多个子组件,单个子组件或子组件列表必须包括在大括号“{...}”中。 |
| keyGenerator | (item: any, index?: number) => string | 否 | 匿名函数,用于给数组中的每一个数据项生成唯一且固定的键值。当数据项在数组中的位置更改时,其键值不得更改,当数组中的数据项被新项替换时,被替换项的键值和新项的键值必须不同。键值生成器的功能是可选的,但是,为了使开发框架能够更好地识别数组更改,提高性能,建议提供。如将数组反向时,如果没有提供键值生成器,则ForEach中的所有节点都将重建。 |
> **说明:**
>
> - ForEach组件必须在容器组件内使用;
> - ForEach必须在容器组件内使用。
>
> - 生成的子组件允许在ForEach的父容器组件中,允许子组件生成器函数中包含if/else条件渲染,同时也允许ForEach包含在if/else条件渲染语句中;
> - 生成的子组件应当是允许包含在ForEach父容器组件中的子组件。
>
> - 子项生成器函数的调用顺序不一定和数组中的数据项相同,在开发过程中不要假设子项生成器和键值生成器函数是否执行以及执行顺序。如下示例可能无法正常工作:
> - 允许子组件生成器函数中包含if/else条件渲染,同时也允许ForEach包含在if/else条件渲染语句中。
>
> ```
> - itemGenerator函数的调用顺序不一定和数组中的数据项相同,在开发过程中不要假设itemGenerator和keyGenerator函数是否执行及其执行顺序。例如,以下示例可能无法正常工作:
>
> ```ts
> ForEach(anArray.map((item1, index1) => { return { i: index1 + 1, data: item1 }; }),
> item => Text(`${item.i}. item.data.label`),
> item => item.data.id.toString())
> item => Text(`${item.i}. item.data.label`),
> item => item.data.id.toString())
> ```
## 示例
......@@ -76,20 +76,16 @@ struct MyComponent {
@State arr: number[] = [10, 20, 30]
build() {
Column() {
Button() {
Text('Reverse Array')
}.onClick(() => {
this.arr.reverse()
})
ForEach(this.arr,
(item: number) => {
Text(`item value: ${item}`)
Divider()
},
(item: number) => item.toString()
)
Column({ space: 5 }) {
Button('Reverse Array')
.onClick(() => {
this.arr.reverse()
})
ForEach(this.arr, (item: number) => {
Text(`item value: ${item}`).fontSize(18)
Divider().strokeWidth(2)
}, (item: number) => item.toString())
}
}
}
......@@ -97,28 +93,28 @@ struct MyComponent {
## 数据懒加载
通过数据懒加载(LazyForEach组件)从提供的数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。
通过数据懒加载(LazyForEach)从提供的数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。
```ts
LazyForEach(
dataSource: IDataSource,
itemGenerator: (item: any) => void,
keyGenerator?: (item: any) => string
dataSource: IDataSource,
itemGenerator: (item: any) => void,
keyGenerator?: (item: any) => string
): void
interface IDataSource {
totalCount(): number;
getData(index: number): any;
registerDataChangeListener(listener: DataChangeListener): void;
unregisterDataChangeListener(listener: DataChangeListener): void;
totalCount(): number;
getData(index: number): any;
registerDataChangeListener(listener: DataChangeListener): void;
unregisterDataChangeListener(listener: DataChangeListener): void;
}
interface DataChangeListener {
onDataReloaded(): void;
onDataAdd(index: number): void;
onDataMove(from: number, to: number): void;
onDataDelete(index: number): void;
onDataChange(index: number): void;
onDataReloaded(): void;
onDataAdd(index: number): void;
onDataMove(from: number, to: number): void;
onDataDelete(index: number): void;
onDataChange(index: number): void;
}
```
......@@ -127,19 +123,19 @@ interface DataChangeListener {
| 参数名 | 参数类型 | 必填 | 参数描述 |
| ------------- | --------------------- | ---- | ------------------------------------------------------------ |
| dataSource | IDataSource | 是 | 实现IDataSource接口的对象,需要开发者实现相关接口。 |
| itemGenerator | (item: any) => void | 是 | 生成子组件的lambda函数,为给定数组项生成一个或多个子组件,单个组件和子组件列表必须括在大括号“{....}”中。 |
| keyGenerator | (item: any) => string | 否 | 匿名函数,用于键值生成,为给定数组项生成唯一且稳定的键值。当子项在数组中的位置更改时,子项的键值不得更改,当数组中的子项被新项替换时,被替换项的键值和新项的键值必须不同。键值生成器的功能是可选的,但是,为了使开发框架能够更好地识别数组更改,提高性能,建议提供。如将数组反向时,如果没有提供键值生成器,则LazyForEach中的所有节点都将重建。 |
| itemGenerator | (item: any) => void | 是 | 生成子组件的lambda函数,为数组中的每一个数据项创建一个或多个子组件,单个子组件或子组件列表必须包括在大括号“{...}”中。 |
| keyGenerator | (item: any) => string | 否 | 匿名函数,用于给数组中的每一个数据项生成唯一且固定的键值。当数据项在数组中的位置更改时,其键值不得更改,当数组中的数据项被新项替换时,被替换项的键值和新项的键值必须不同。键值生成器的功能是可选的,但是,为了使开发框架能够更好地识别数组更改,提高性能,建议提供。如将数组反向时,如果没有提供键值生成器,则LazyForEach中的所有节点都将重建。 |
表2 IDataSource类型说明
### IDataSource类型说明
| 名称 | 描述 |
| ------------------------------------------------------------ | ---------------------- |
| totalCount(): number | 获取数据总数。 |
| getData(index: number): any | 获取索引对应的数据。 |
| registerDataChangeListener(listener:DataChangeListener): void | 注册改变数据的控制器。 |
| unregisterDataChangeListener(listener:DataChangeListener): void | 注销改变数据的控制器。 |
| getData(index: number): any | 获取索引值index对应的数据。 |
| registerDataChangeListener(listener:DataChangeListener): void | 注册数据改变的监听器。 |
| unregisterDataChangeListener(listener:DataChangeListener): void | 注销数据改变的监听器。 |
表3 DataChangeListener类型说明
### DataChangeListener类型说明
| 名称 | 描述 |
| -------------------------------------------------------- | -------------------------------------- |
......@@ -156,115 +152,127 @@ interface DataChangeListener {
## 示例
```ts
// xxx.ets
class BasicDataSource implements IDataSource {
private listeners: DataChangeListener[] = []
private listeners: DataChangeListener[] = []
public totalCount(): number {
return 0
}
public getData(index: number): any {
return undefined
}
public totalCount(): number {
return 0
}
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
console.info('add listener')
this.listeners.push(listener)
}
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
console.info('remove listener')
this.listeners.splice(pos, 1)
}
}
public getData(index: number): any {
return undefined
}
notifyDataReload(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded()
})
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
console.info('add listener')
this.listeners.push(listener)
}
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)
})
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
console.info('remove listener')
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[] = ['/path/image0', '/path/image1', '/path/image2', '/path/image3']
// 初始化数据列表
private dataArray: string[] = ['/path/image0', '/path/image1', '/path/image2', '/path/image3']
public totalCount(): number {
return this.dataArray.length
}
public getData(index: number): any {
return this.dataArray[index]
}
public totalCount(): number {
return this.dataArray.length
}
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)
}
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 {
private data: MyDataSource = new MyDataSource()
build() {
List({space: 3}) {
LazyForEach(this.data, (item: string) => {
ListItem() {
Row() {
Image(item).width("30%").height(50)
Text(item).fontSize(20).margin({left:10})
}.margin({left: 10, right: 10})
}
.onClick(()=>{
this.data.pushData('/path/image' + this.data.totalCount())
})
}, item => item)
private data: MyDataSource = new MyDataSource()
build() {
List({ space: 3 }) {
LazyForEach(this.data, (item: string) => {
ListItem() {
Row() {
Image(item).width(50).height(50)
Text(item).fontSize(20).margin({ left: 10 })
}.margin({ left: 10, right: 10 })
}
.onClick(() => {
// 每点击一次列表项,数据增加一项
this.data.pushData('/path/image' + this.data.totalCount())
})
}, item => item)
}
}
}
```
> **说明:**
>
> - 数据懒加载必须在容器组件内使用,且仅有List、Grid以及Swiper组件支持数据的懒加载(即只加载可视部分以及其前后少量数据用于缓冲),其他组件仍然是一次加载所有的数据;
> - LazyForEach必须在容器组件内使用,目前仅有List、Grid以及Swiper组件支持数据懒加载(即只加载可视部分以及其前后少量数据用于缓冲),其他组件仍然是一次性加载所有的数据。
>
> - LazyForEach在每次迭代中,必须且只允许创建一个子组件;
> - LazyForEach在每次迭代中,必须创建且只允许创建一个子组件。
>
> - 生成的子组件必须允许在LazyForEach的父容器组件中;
> - 生成的子组件必须是允许包含在LazyForEach父容器组件中的子组件。
>
> - 允许LazyForEach包含在if/else条件渲染语句中,不允许LazyForEach中出现if/else条件渲染语句;
> - 允许LazyForEach包含在if/else条件渲染语句中,但不允许LazyForEach中出现if/else条件渲染语句。
>
> - 为了高性能渲染,通过DataChangeListener对象的onDataChange方法来更新UI时,仅itemGenerator中的UI描述的组件内使用了状态变量时,才会触发组件刷新;
> - 为了高性能渲染,通过DataChangeListener对象的onDataChange方法来更新UI时,仅当itemGenerator中创建的子组件内使用了状态变量时,才会触发组件刷新。
>
> - 子项生成器函数的调用顺序不一定和数据源中的数据项相同,在开发过程中不要假设子项生成器和键值生成器函数是否执行以及执行顺序。如下示例可能无法正常工作:
> - itemGenerator函数的调用顺序不一定和数据源中的数据项相同,在开发过程中不要假设itemGenerator和keyGenerator函数是否执行及其执行顺序。例如,以下示例可能无法正常工作:
>
> ```ts
> LazyForEach(dataSource,
> item => Text(`${item.i}. item.data.label`)),
> item => item.data.id.toString())
> item => Text(`${item.i}. item.data.label`)),
> item => item.data.id.toString())
> ```
\ No newline at end of file
......@@ -4,48 +4,47 @@
ArkTS语言的使用在生成器函数中存在一定的限制:
- 表达式仅允许在字符串(${expression})、if条件、ForEach的参数和组件的参数中使用;
- 任何表达式都不能导致任何应用程序状态变量(@State、@Link、@Prop)的改变,否则会导致未定义和潜在不稳定的框架行为;
- 表达式仅允许在字符串(${expression})、if/else条件语句、ForEach的参数以及组件的参数中使用。
- 任何表达式都不能导致应用程序中状态变量(@State、@Link、@Prop)的改变,否则会造成未定义和潜在不稳定的框架行为。
- 生成器函数内部不能有局部变量。
上述限制都不适用于事件处理函数(例如onClick)的匿函数实现。
上述限制都不适用于事件方法(如onClick)的匿名函数实现。
## 变量的双向绑定
ArkTS支持通过$$双向绑定变量,通常应用于状态值频繁改变的变量。
- 当前$$支持基础类型变量,以及@State、@Link和@Prop装饰的变量。
- 当前$$仅支持[bindPopup](../reference/arkui-ts/ts-universal-attributes-popup.md)属性的show参数和@State变量之间的渲染,Radio组件的checked属性
- $$绑定的变量变更时,仅渲染当前组件,提高渲染速度。
- 当前$$仅支持[bindPopup](../reference/arkui-ts/ts-universal-attributes-popup.md)属性方法的show参数,[Radio](../reference/arkui-ts/ts-basic-components-radio.md)组件的checked属性,[Refresh](../reference/arkui-ts/ts-container-refresh.md)组件的refreshing参数
- $$绑定的变量变化时,仅渲染刷新当前组件,提高渲染速度。
```ts
// xxx.ets
@Entry
@Component
struct bindPopup {
@State customPopup: boolean = false
build() {
column() {
button() {
Text('Popup')
}
.onClick(() => {
this.customPopup = !this.customPopup
})
.bindPopup(
$$this.customPopup, {
mesage: "showPopup"
}
)
}
}
}
struct bindPopupPage {
@State customPopup: boolean = false
build() {
Column() {
Button('Popup')
.margin(20)
.onClick(() => {
this.customPopup = !this.customPopup
})
.bindPopup($$this.customPopup, {
message: "showPopup"
})
}
}
}
```
## 状态变量多种数据类型声明使用限制
## 状态变量数据类型声明使用限制
@State、@Provide、 @Link和@Consume四种状态变量的多种数据类型只能同时由简单数据类型或引用数据类型其中一种构成。
@State、@Provide、 @Link和@Consume四种状态变量的数据类型声明只能由简单数据类型或引用数据类型的其中一种构成。
示例:
......@@ -53,14 +52,14 @@ struct bindPopup {
// xxx.ets
@Entry
@Component
struct Index {
struct IndexPage {
//错误写法: @State message: string | Resource = 'Hello World'
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text(`${ this.message }`)
Text(`${this.message}`)
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
......
# 应用级变量的状态管理
在前面的章节中,已经讲述了如何管理页面级变量的状态,本章将说明如何管理应用级变量的状态,具体接口请参考[应用级变量的状态管理接口](../reference/arkui-ts/ts-state-management.md)
在前面的章节中,已经讲述了如何管理页面级变量的状态,本章将说明如何管理应用级变量的状态,具体接口说明请参考[应用级变量的状态管理接口](../reference/arkui-ts/ts-state-management.md)
## AppStorage
## [AppStorage](../reference/arkui-ts/ts-state-management.md#appstorage)
AppStorage是应用程序中的单例对象,由UI框架在应用程序启动时创建,在应用程序退出时销毁,为应用程序范围内的可变状态属性提供中央存储。
AppStorage包含整个应用程序中需要访问的所有状态属性,只要应用程序保持运行,AppStorage就会保存所有属性及属性值,属性值可以通过唯一的键值进行访问。
组件可以通过装饰器将应用程序状态数据与AppStorage进行同步,应用业务逻辑的实现也可以通过接口访问AppStorage。
AppStorage的选择状态属性可以与不同的数据源或数据接收器同步,这些数据源和接收器可以是设备上的本地或远程,并具有不同的功能,如数据持久性。这样的数据源和接收器可以独立于UI在业务逻辑中实现。
默认情况下,AppStorage中的属性是可变的,AppStorage还可使用不可变(只读)属性。
......@@ -22,54 +20,59 @@ AppStorage的选择状态属性可以与不同的数据源或数据接收器同
### @StorageProp装饰器
组件通过使用@StorageProp(key)装饰的状态变量,将与AppStorage建立单向数据绑定,key标识AppStorage中的属性键值。当创建包含@StoageProp的状态变量的组件时,该状态变量的值将使用AppStorage中的值进行初始化。AppStorage中的属性值的更改会导致绑定的UI组件进行状态更新。
组件通过使用@StorageProp(key)装饰的状态变量,与AppStorage建立单向数据绑定,key标识AppStorage中的属性键值。当创建包含@StorageProp的状态变量的组件时,该状态变量的值将使用AppStorage中的值进行初始化。AppStorage中属性值的更改会导致绑定该状态变量的UI组件进行状态更新。
### 示例
每次用户单击Count按钮时,this.varA变量值都会增加,此变量与AppStorage中的varA同步。每次用户单击当前语言按钮时,修改AppStorage中的languageCode,此修改会同步给this.lang变量。
每次用户单击Count按钮时,this.varA变量值都会增加1,此变量与AppStorage中的varA同步。每次用户单击language按钮时,修改AppStorage中的languageCode,此修改会同步给this.languageCode变量。
```ts
// xxx.ets
@Entry
@Component
struct ComponentA {
@StorageLink('varA') varA: number = 2
@StorageProp('languageCode') lang: string = 'en'
@StorageProp('languageCode') languageCode: string = 'en'
private label: string = 'count'
aboutToAppear() {
this.label = (this.lang === 'zh') ? '' : 'Count'
this.label = (this.languageCode === 'zh') ? '数量' : 'Count'
}
build() {
Column(){
Column() {
Row({ space: 20 }) {
Button(`${this.label}: ${this.varA}`)
.onClick(() => {
AppStorage.Set<number>('varA', AppStorage.Get<number>('varA') + 1)
})
Button(`lang: ${this.lang}`)
Button(`language: ${this.languageCode}`)
.onClick(() => {
if (this.lang === 'zh') {
if (AppStorage.Get<string>('languageCode') === 'zh') {
AppStorage.Set<string>('languageCode', 'en')
} else {
AppStorage.Set<string>('languageCode', 'zh')
}
this.label = (this.lang === 'zh') ? '' : 'Count'
this.label = (this.languageCode === 'zh') ? '数量' : 'Count'
})
}
.margin({ bottom: 50 })
Row(){
Button(`更改@StorageLink修饰的变量:${this.varA}`).fontSize(10)
Row() {
Button(`更改@StorageLink修饰的变量:${this.varA}`).height(40).fontSize(14)
.onClick(() => {
this.varA++
})
}.margin({ bottom: 50 })
Row(){
Button(`更改@StorageProp修饰的变量:${this.lang}`).fontSize(10)
Row() {
Button(`更改@StorageProp修饰的变量:${this.languageCode}`).height(40).fontSize(14)
.onClick(() => {
this.lang = 'test'
if (this.languageCode === 'zh') {
this.languageCode = 'en'
} else {
this.languageCode = 'zh'
}
})
}
}
......@@ -77,17 +80,18 @@ struct ComponentA {
}
```
![appstorage](figures/appstorage.gif)
## LocalStorage
## [LocalStorage](../reference/arkui-ts/ts-state-management.md#localstorage9)
> **说明:** 该组件从API version 9开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
> **说明:**
>
> 该组件从API version 9开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
LocalStorage是应用程序中的存储单元,生命周期跟随其关联的Ability。LocalStorage为应用程序范围内的可变状态属性和非可变状态属性提供存储,可变状态属性和非可变状态属性是构建应用程序UI的一部分,如一个Ability的UI。
应用层:一个应用程序可以创建多个LocalStorage实例,应用程序的每一个Ability对应一个LocalStorage实例。
Ability: 一个应用程序可以拥有多个Ability,一个Ability中的所有子组件最多可以分配一个LocalStorage实例。并且,Ability中的所有子组件都将继承对此LocalStorage实例存储对象的访问权。
Ability:一个应用程序可以拥有多个Ability,一个Ability中的所有子组件最多可以分配一个LocalStorage实例。并且,Ability中的所有子组件都将继承对此LocalStorage实例存储对象的访问权。
一个组件最多可以访问一个LocalStorage实例,一个LocalStorage对象可以分配给多个组件。
......@@ -101,150 +105,168 @@ Ability: 一个应用程序可以拥有多个Ability,一个Ability中的所
> **说明:** 创建LocalStorage实例时如未定义初始值,可以使用组件内@LocalStorageLink和@LocalStorageProp的初始值。如果定义时给定了初始值,那么不会再使用@LocalStorageLink和@LocalStorageProp的初始值。
### 示例1(在一个Ability创建的LocalStorage)
### 示例1(在一个Ability中创建LocalStorage)
LocalStorage通过loadContent接口加载,接口详见[loadContent](../reference/apis/js-apis-window.md#loadcontent9-1)
LocalStorage通过loadContent接口加载,接口说明详见[loadContent](../reference/apis/js-apis-window.md#loadcontent9-1)
```
```ts
// MainAbility.ts
import Ability from '@ohos.application.Ability'
export default class MainAbility extends Ability {
storage : LocalStorage
onCreate() {
this.storage = new LocalStorage();
this.storage.setOrCreate("storageSimpleProp",121);
console.log("[Demo MainAbility onCreate]");
}
onDestroy() {
console.log("[Demo MainAbility onDestroy]")
}
onWindowStageCreate(windowStage) {
// storage作为参数传递给loadContent接口。
windowStage.loadContent("pages/index",this.storage)
}
onWindowStageDestroy() {
console.log("[Demo] MainAbility onWindoeStageDestroy")
}
onForeground() {
console.log("[Demo] MainAbility onForeground")
}
onBackground() {
console.log("[Demo] MainAbility onBackground")
}
export default class MainAbility extends Ability {
storage: LocalStorage
onCreate() {
this.storage = new LocalStorage()
this.storage.setOrCreate('storageSimpleProp', 121)
console.info('[Demo MainAbility onCreate]')
}
onDestroy() {
console.info('[Demo MainAbility onDestroy]')
}
onWindowStageCreate(windowStage) {
// storage作为参数传递给loadContent接口
windowStage.loadContent('pages/index', this.storage)
}
onWindowStageDestroy() {
console.info('[Demo] MainAbility onWindowStageDestroy')
}
onForeground() {
console.info('[Demo] MainAbility onForeground')
}
onBackground() {
console.info('[Demo] MainAbility onBackground')
}
}
```
@Component组件获取数据
```
```ts
// index.ets
let storage = LocalStorage.GetShared()
@Entry(storage)
@Component
struct LocalStorageComponent {
@LocalStorageLink("storageSimpleProp") simpleVarName: number = 0
@LocalStorageLink('storageSimpleProp') simpleVarName: number = 0
build() {
Column(){
Button(`LocalStorageLink: ${ this.simpleVarName.toString() }`)
.onClick(()=>{
this.simpleVarName +=1;
Column() {
Button(`LocalStorageLink: ${this.simpleVarName.toString()}`)
.margin(20)
.onClick(() => {
this.simpleVarName += 1
})
Text(JSON.stringify(this.simpleVarName))
.fontSize(50)
LocalStorageComponentProp();
}
.height(500)
LocalStorageComponentProp()
}.width('100%')
}
}
@Component
struct LocalStorageComponentProp {
@LocalStorageProp("storageSimpleProp") simpleVarName: number = 0
@LocalStorageProp('storageSimpleProp') simpleVarName: number = 0
build() {
Column() {
Button(`LocalStorageProp: ${ this.simpleVarName.toString() }`)
Button(`LocalStorageProp: ${this.simpleVarName.toString()}`)
.margin(20)
.onClick(() => {
this.simpleVarName += 1;
this.simpleVarName += 1
})
Text(JSON.stringify(this.simpleVarName))
.fontSize(50)
}
.height(500)
}.width('100%')
}
}
```
### 示例2(在Entry页面定义LocalStorage)
```
let storage = new LocalStorage({"PropA":47});
```ts
// xxx.ets
let storage = new LocalStorage({ "PropA": 47 })
@Entry(storage)
@Component
struct ComA {
@LocalStorageLink("PropA") storLink: number = 1;
build() {
Column() {
Text(`Parent from LocalStorage ${ this.storLink }`)
.onClick(()=>this.storLink+=1)
Child()
}
@Component
struct ComA {
@LocalStorageLink("PropA") storageLink: number = 1
build() {
Column() {
Text(`Parent from LocalStorage ${this.storageLink}`)
.fontSize(18)
.margin(20)
.onClick(() => this.storageLink += 1)
Child()
}
}
}
@Component
struct Child{
@LocalStorageLink("PropA") storLink: number = 1;
build() {
Text(`Parent from LocalStorage ${ this.storLink }`)
.onClick(()=>this.storLink+=1)
struct Child {
@LocalStorageLink("PropA") storageLink: number = 1
build() {
Text(`Child from LocalStorage ${this.storageLink}`)
.fontSize(18)
.margin(20)
.onClick(() => this.storageLink += 1)
}
}
```
## PersistentStorage
## [PersistentStorage](../reference/arkui-ts/ts-state-management.md#persistentstorage)
PersistentStorage提供了一些静态方法用来管理应用持久化数据,可以将特定标记的持久化数据链接到AppStorage中,并由AppStorage接口访问对应持久化数据,或者通过@StorageLink装饰器来访问对应key的变量。
PersistentStorage提供了一些静态方法用来管理应用持久化数据,可以将特定标记的持久化数据链接到AppStorage中,并由AppStorage接口访问对应持久化数据,或者通过@StorageLink装饰器来访问对应key的变量。
> **说明:**
>
> - PersistProp接口使用时,需要保证输入对应的key应当在AppStorage存在。
> - DeleteProp接口使用时,只能对本次启动已经link过的数据生效。
> - PersistentStorage的PersistProp接口使用时,需要保证输入对应的key在AppStorage中存在。
> - PersistentStorage的DeleteProp接口使用时,只能对本次应用启动时已经link过的数据生效。
```ts
// xxx.ets
PersistentStorage.PersistProp("highScore", "0");
PersistentStorage.PersistProp('highScore', '0')
@Entry
@Component
struct PersistentComponent {
@StorageLink('highScore') highScore: string = '0'
@State currentScore: number = 0
build() {
Column() {
if (this.currentScore === Number(this.highScore)) {
Text(`new highScore : ${this.highScore}`)
}
Button() {
Text(`goal!, currentScore : ${this.currentScore}`)
.fontSize(10)
}.onClick(() => {
this.currentScore++
if (this.currentScore > Number(this.highScore)) {
this.highScore = this.currentScore.toString()
}
})
}
}
@StorageLink('highScore') highScore: string = '0'
@State currentScore: number = 0
build() {
Column() {
if (this.currentScore === Number(this.highScore)) {
Text(`new highScore : ${this.highScore}`).fontSize(18)
}
Button(`goal!, currentScore : ${this.currentScore}`)
.margin(20)
.onClick(() => {
this.currentScore++
if (this.currentScore > Number(this.highScore)) {
this.highScore = this.currentScore.toString()
}
})
}.width('100%')
}
}
```
![PersistentStorage](figures/PersistentStorage.gif)
## [Environment](../reference/arkui-ts/ts-state-management.md#environment)
## Environment
Environment是框架在应用程序启动时创建的单例对象,它为AppStorage提供了一系列应用程序需要的环境状态数据,这些数据描述了应用程序运行的设备环境,包括系统语言、深浅色模式等等。Environment及其属性是不可变的,所有数据类型均为简单类型。如下示例展示了从Environment获取系统是否开启无障碍屏幕朗读:
Environment是框架在应用程序启动时创建的单例对象,它为AppStorage提供了一系列应用程序需要的环境状态属性,这些属性描述了应用程序运行的设备环境。Environment及其属性是不可变的,所有属性值类型均为简单类型。如下示例展示了从Environment获取语音环境:
```
Environment.EnvProp("accessibilityEnabled", "default");
var enable = AppStorage.Get("accessibilityEnabled");
```ts
Environment.EnvProp('accessibilityEnabled', 'default')
var enable = AppStorage.Get('accessibilityEnabled')
```
accessibilityEnabled是Environment提供默认系统变量识别符。首先需要将对应系统属性绑定到AppStorage中,再通过AppStorage中的方法或者装饰器访问对应系统的属性数据。
accessibilityEnabled是Environment提供的系统默认变量识别符。首先需要将对应系统属性绑定到AppStorage上,再通过AppStorage中的方法或者装饰器访问对应的系统属性数据。
\ No newline at end of file
# 基本概念
ArkTS提供了多维度的状态管理机制,在UI开发框架中,和UI相关联的数据,不仅可以在组件内使用,还可以在不同组件层级间传递,比如父子组件之间、爷孙组件之前,也可以时全局范围内的传递。另外,从数据的传递形式来看,可分为只读的单向传递和可变更的双向传递。开发者可以灵活的利用这些能力来实现数据和UI的联动。
ArkTS提供了多维度的状态管理机制,在ArkUI开发框架中,和UI相关联的数据,不仅可以在组件内使用,还可以在不同组件层级间传递,比如父子组件之间、爷孙组件之间,也可以是应用全局范围内的传递。另外,从数据的传递形式来看,可分为只读的单向传递和可变更的双向传递。开发者可以灵活地利用这些能力来实现数据和UI的联动。
![](figures/CoreSpec_figures_state-mgmt-overview.png)
......@@ -11,20 +11,20 @@ ArkTS提供了多维度的状态管理机制,在UI开发框架中,和UI相
| 装饰器 | 装饰内容 | 说明 |
| ----------- | ------------------------- | ------------------------------------------------------------ |
| @State | 基本数据类型,类,数组 | 修饰的状态数据被修改时会触发组件的build方法进行UI界面更新。 |
| @Prop | 基本数据类型 | 修改后的状态数据用于在父组件和子组件之间建立单向数据依赖关系。修改父组件关联数据时,更新当前组件的UI。 |
| @Prop | 基本数据类型 | 修改后的状态数据用于在父组件和子组件之间建立单向数据依赖关系。修改父组件关联数据时,当前组件会重新渲染。 |
| @Link | 基本数据类型,类,数组 | 父子组件之间的双向数据绑定,父组件的内部状态数据作为数据源,任何一方所做的修改都会反映给另一方。 |
| @Observed | 类 | @Observed应用于类,表示该类中的数据变更被UI页面管理。 |
| @ObjectLink | 被@Observed所装饰类的对象 | 装饰的状态数据被修改时,在父组件或者其他兄弟组件内与它关联的状态数据所在的组件都会更新UI。 |
| @ObjectLink | 被@Observed所装饰类的对象 | @ObjectLink装饰的状态数据被修改时,在父组件或者其他兄弟组件内与它关联的状态数据所在的组件都会重新渲染。 |
| @Consume | 基本数据类型,类,数组 | @Consume装饰的变量在感知到@Provide装饰的变量更新后,会触发当前自定义组件的重新渲染。 |
| @Provide | 基本数据类型,类,数组 | @Provide作为数据的提供方,可以更新其子孙节点的数据,并触发页面渲染。 |
| @Provide | 基本数据类型,类,数组 | @Provide作为数据的提供方,可以更新其子孙节点的数据,并触发页面重新渲染。 |
## 应用级变量的状态管理
AppStorage是整个UI应用程序状态的中心“数据库”,UI框架会针对应用程序创建单例AppStorage对象,并提供相应的装饰器和接口供应用程序使用。
AppStorage是整个应用程序状态的中心“数据库”,UI框架会针对应用程序创建单例AppStorage对象,并提供相应的装饰器和接口供应用程序使用。
- @StorageLink:@StorageLink(name)的原理类似于@Consume(name),不同的是,该给定名称的链接对象是从AppStorage中获得的,在UI组件和AppStorage之间建立双向绑定同步数据。
- @StorageProp:@StorageProp(name)将UI组件属性与AppStorage进行单向同步,AppStorage中值的更改会更新组件中的属性,但UI组件无法更改AppStorage中的属性值
- AppStorage还提供用于业务逻辑实现的API,用于添加、读取、修改和删除应用程序的状态属性,此API所做的更改会导致修改的状态数据同步到UI组件上进行UI更新。
- PersistentStorage类提供了一些静态方法用来管理应用持久化数据,可以将特定标记的持久化数据链接到AppStorage中,并由AppStorage接口访问对应持久化数据,或者通过@StorageLink装饰器来访问对应key的变量。
- Environment是框架在应用程序启动时创建的单例对象,它为AppStorage提供了一系列应用程序需要的环境状态属性,这些属性描述了应用程序运行的设备环境。
- @StorageProp:@StorageProp(name)将UI组件数据与AppStorage进行单向同步,AppStorage中值的更改会更新UI组件中的数据,但UI组件无法更改AppStorage中的数据
- AppStorage还提供了用于业务逻辑实现的API,用于添加、读取、修改和删除应用程序的状态数据,此API所做的更改会导致修改的状态数据同步到UI组件上进行UI更新。
- PersistentStorage提供了一些静态方法用来管理应用持久化数据,可以将特定标记的持久化数据链接到AppStorage中,并由AppStorage接口访问对应持久化数据,或者通过@StorageLink装饰器来访问对应key的变量。
- Environment是框架在应用程序启动时创建的单例对象,它为AppStorage提供了一系列应用程序需要的环境状态数据,这些数据描述了应用程序运行的设备环境。
......@@ -11,18 +11,15 @@
- [资源文件的分类](ui-ts-basic-resource-file-categories.md)
- [资源访问](ts-resource-access.md)
- [像素单位](ts-pixel-units.md)
- 深入理解组件化
- [自定义组件初始化](ts-custom-component-initialization.md)
- [自定义组件生命周期回调函数](ts-custom-component-lifecycle-callbacks.md)
- [组件创建和重新初始化示例](ts-component-creation-re-initialization.md)
- 常见组件开发指导
- [Button开发指导](ui-ts-basic-components-button.md)
- [Web开发指导](ui-ts-components-web.md)
- 常见布局开发指导
- [弹性布局](ui-ts-layout-flex.md)
- [栅格布局](ui-ts-layout-grid-container.md)
- [媒体查询](ui-ts-layout-mediaquery.md)
- [Web组件开发](ui-ts-components-web.md)
- 体验声明式UI
- [创建声明式UI工程](ui-ts-creating-project.md)
- [初识Component](ui-ts-components.md)
......@@ -33,7 +30,6 @@
- [构建食物分类Grid布局](ui-ts-building-category-grid-layout.md)
- [页面跳转与数据传递](ui-ts-page-redirection-data-transmission.md)
- [性能提升的推荐方法](ts-performance-improvement-recommendation.md)
- UI开发(兼容JS的类Web开发范式)
- [概述](ui-js-overview.md)
- 框架说明
......
......@@ -24,7 +24,7 @@ FA应用的ArkTS模块(entry/src/main)的典型开发目录结构如下:
> **说明:**
>
> - 资源目录resources文件夹位于src/main下,此目录下资源文件的详细规范以及子目录结构规范参看[资源文件的分类](ui-ts-basic-resource-file-categories.md)。
>- 页面支持导入TypeScript和JavaScript文件。
> - 页面支持导入TypeScript和JavaScript文件。
**js标签配置:**
......
# Button
Button是按钮组件,通常用于响应用户的点击操作,如提交表单,其类型包括胶囊按钮、圆形按钮、普通按钮。具体用法请参考[Button](../reference/arkui-ts/ts-basic-components-button.md)
## 创建按钮
Button通过调用接口来创建,接口调用有以下两种形式:
- 创建包含子组件的按钮
`Button(options?: {type?: ButtonType, stateEffect?: boolean})`,该接口用于创建包含子组件的按钮,其中type用于设置Button类型,stateEffect属性设置Button是否开启点击效果。
```
Button({ type: ButtonType.Normal, stateEffect: true }) {
Row() {
Image($r('app.media.loading')).width(20).height(20).margin({ left: 12 })
Text('loading').fontSize(12).fontColor(0xffffff).margin({ left: 5, right: 12 })
}.alignItems(VerticalAlign.Center)
}.borderRadius(8).backgroundColor(0x317aff).width(90)
```
![zh-cn_image_0000001260555857](figures/zh-cn_image_0000001260555857.png)
- 创建不包含子组件的按钮
`Button(label?: string, options?: { type?: ButtonType, stateEffect?: boolean })`,该接口用于创建不包含子组件的按钮,其中label确定所创建的Button是否包含子组件。
```
Button('Ok', { type: ButtonType.Normal, stateEffect: true })
.borderRadius(8)
.backgroundColor(0x317aff)
.width(90)
```
![zh-cn_image_0000001215796030](figures/zh-cn_image_0000001215796030.png)
## 设置按钮类型
Button有三种可选类型,分别为Capsule(胶囊类型)、Circle(圆形按钮)和Normal(普通按钮),通过type进行设置。
- 胶囊按钮(默认类型)
```ts
Button('Disable', { type: ButtonType.Capsule, stateEffect: false })
.backgroundColor(0x317aff)
.width(90)
```
![zh-cn_image_0000001215645452](figures/zh-cn_image_0000001215645452.png)
- 圆形按钮
```ts
Button('Circle', { type: ButtonType.Circle, stateEffect: false })
.backgroundColor(0x317aff)
.width(90)
.height(90)
```
![zh-cn_image_0000001215965420](figures/zh-cn_image_0000001215965420.png)
## 自定义样式
- 设置边框弧度
一般使用通用属性来自定义按钮样式。例如通过borderRadius属性设置按钮的边框弧度。
```ts
Button('circle border', { type: ButtonType.Normal })
.borderRadius(20)
```
![zh-cn_image_0000001190463780](figures/zh-cn_image_0000001190463780.png)
- 设置文本样式
通过添加文本样式设置按钮文本的展示样式。
```ts
Button('font style', { type: ButtonType.Normal })
.fontSize(20)
.fontColor(Color.Red)
.fontWeight(800)
```
![zh-cn_image_0000001189744672](figures/zh-cn_image_0000001189744672.png)
- 设置背景颜色
添加backgroundColor属性设置按钮的背景颜色。
```ts
Button('background color').backgroundColor(0xF55A42)
```
![zh-cn_image_0000001235146483](figures/zh-cn_image_0000001235146483.png)
- 用作功能型按钮
为删除操作创建一个按钮。
```ts
Button({ type: ButtonType.Circle, stateEffect: true }) {
Image($r('app.media.ic_public_delete_filled')).width(30).height(30)
}.width(55).height(55).margin({ left: 20 }).backgroundColor(0xF55A42)
```
![zh-cn_image_0000001260373911](figures/zh-cn_image_0000001260373911.png)
## 添加事件
Button组件通常用于触发某些操作,可以在绑定onClick事件来响应点击操作后的自定义行为。
```ts
Button('Ok', { type: ButtonType.Normal, stateEffect: true })
.onClick(()=>{
console.info('Button onClick')
})
```
## 场景示例
- 用于启动操作
可以将按钮用于启动操作的任何用户界面元素。按钮会根据用户的操作触发相应的事件。如,在List容器里边通过点击按钮进行页面跳转:
```ts
// xxx.ets
import router from '@ohos.router'
@Entry
@Component
struct ButtonCase1 {
build() {
List({ space: 4 }) {
ListItem() {
Button("First").onClick(() => {
router.push({ url: 'xxx' })
})
}
ListItem() {
Button("Second").onClick(() => {
router.push({ url: 'yyy' })
})
}
ListItem() {
Button("Third").onClick(() => {
router.push({ url: 'zzz' })
})
}
}
.listDirection(Axis.Vertical)
.backgroundColor(0xDCDCDC).padding(20)
}
}
```
​ ![zh-cn_image_0000001235626467](figures/zh-cn_image_0000001235626467.png)
- 用于表单的提交
在用户登录/注册页面,用户的登录或注册的提交操作会用按钮。
```ts
// xxx.ets
@Entry
@Component
struct ButtonCase2 {
build() {
Column() {
TextInput({ placeholder: 'input your username' }).margin({ top: 20 })
TextInput({ placeholder: 'input your password' }).type(InputType.Password).margin({ top: 20 })
Button('Register').width(300).margin({ top: 20 })
}.padding(20)
}
}
```
![zh-cn_image_0000001190466492](figures/zh-cn_image_0000001190466492.png)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册