参考资料:
文档中心
ArkTS是HarmonyOS优选的主力应用开发语言。ArkTS围绕应用开发在TypeScript(简称TS)生态基础上做了进一步扩展,继承了TS的所有特性,是TS的超集。因此,在学习ArkTS语言之前,建议开发者具备TS语言开发能力。
当前,ArkTS在TS的基础上主要扩展了如下能力:
未来,ArkTS会结合应用开发/运行的需求持续演进,逐步提供并行和并发能力增强、系统类型增强、分布式开发范式等更多特性。
ArkTS以声明方式组合和扩展组件来描述应用程序的UI,同时还提供了基本的属性、事件和子组件配置方法,帮助开发者实现应用交互逻辑。
- 根据组件构造方法的不同,创建组件包含有参数和无参数两种方式。
- 创建组件时不需要new运算符。
- 最好有Ts语法基础。
注意:Column、Row、Stack、Grid、List等组件都是容器组件,我们的顶层需要在容器组件中
如果组件的接口定义没有包含必选构造参数,则组件后面的“()”不需要配置任何内容。例如,Divider组件不包含构造参数
Column() {
Text('无参数组件Column')
Divider()
Text('item 2')
Divider()
Text($r('app.string.app_name'))
Divider()
Text()
}
Image(this.imagePath)
Image('https://' + this.imageUrl)
Text(`count: ${this.count}`)
组件的样式控制,有点类似Java 中的链式编程
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
@State url: string="https://dogefs.s3.ladydaily.com/~/source/wallhaven/full/p9/wallhaven-p97l5e.png?w=2560&h=1440&fmt=webp"
build() {
Row() {
Column() {
Text('无参数组件Column')
Divider()
Text('item 2')
Divider()
Text($r('app.string.app_name'))
Divider()
Text()
Divider()
Image(this.url)
.alt('error.jpg')
.width(100)
.height(100)
}
}
.height('100%')
}
}
事件方法以“.”链式调用的方式配置系统组件支持的事件,建议每个事件方法单独写一行
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
@State url: string="https://dogefs.s3.ladydaily.com/~/source/wallhaven/full/p9/wallhaven-p97l5e.png?w=2560&h=1440&fmt=webp"
@State backgroundColors:Color=Color.Blue
build() {
Row() {
Column() {
Text('无参数组件Column')
Divider()
Text('item 2')
Divider()
Text($r('app.string.app_name'))
Divider()
Text()
Divider()
Image(this.url)
.alt('error.jpg')
.width(100)
.height(100)
Divider()
Button("点击事件")
.backgroundColor(this.backgroundColors)
.margin(10)
.width(100)
.height(50)
.onClick(()=>{
console.log("你点击了这个按钮哦")
this.backgroundColors=Color.Green
})
.onHover(e=>{
// 改变颜色
console.log("你点击了这个按钮哦1111")
})
}
}
.height('100%')
}
}
在ArkUI中,UI显示的内容均为组件,由框架直接提供的称为系统组件,由开发者定义的称为自定义组件。在进行 UI 界面开发时,通常不是简单的将系统组件进行组合使用,而是需要考虑代码可复用性、业务逻辑与UI分离,后续版本演进等因素。因此,将UI和部分业务逻辑封装成自定义组件是不可或缺的能力。
自定义组件具有以下特点:
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
@State url: string="https://dogefs.s3.ladydaily.com/~/source/wallhaven/full/p9/wallhaven-p97l5e.png?w=2560&h=1440&fmt=webp"
@State backgroundColors:Color=Color.Blue
build() {
Row() {
Column(){
ImageComponent()
}.width(200)
Column(){
ImageComponent()
}.width(200)
}
.height('100%')
}
}
@Component
struct ImageComponent {
@State url: string = 'https://dogefs.s3.ladydaily.com/~/source/wallhaven/full/p9/wallhaven-p97l5e.png?w=2560&h=1440&fmt=webp';
build() {
Row() {
Image(this.url)
.width(200)
.height(200)
}
}
}
@Component
struct MyComponent {
}
@Component
struct MyComponent {
build() {
}
}
@Entry
@Component
struct MyComponent {
}
自定义组件除了必须要实现build()函数外,还可以实现其他成员函数,成员函数具有以下约束:
自定义组件可以包含成员变量,成员变量具有以下约束:
我们已经了解到,可以在build方法或者@Builder装饰的函数里创建自定义组件,在创建自定义组件的过程中,根据装饰器的规则来初始化自定义组件的参数。
@Entry
@Component
struct Index {
build() {
Row() {
Column(){
ImageComponent()
}.width(200)
Column(){
ImageComponent({url:"https://dogefs.s3.ladydaily.com/~/source/wallhaven/full/ex/wallhaven-exwgw8.png?w=2560&h=1440&fmt=webp"})
}.width(200)
}
.height('100%')
}
}
@Component
struct ImageComponent {
@State url: string = 'https://dogefs.s3.ladydaily.com/~/source/wallhaven/full/p9/wallhaven-p97l5e.png?w=2560&h=1440&fmt=webp';
build() {
Row() {
Image(this.url)
.width(200)
.height(200)
}
}
}
所有声明在build()函数的语言,我们统称为UI描述,UI描述需要遵循以下规则:
@Entry
@Component
struct MyComponent {
build() {
// 根节点唯一且必要,必须为容器组件
Row() {
ChildComponent()
}
}
}
@Component
struct ChildComponent {
build() {
// 根节点唯一且必要,可为非容器组件
Image('test.jpg')
}
}
build() {
// 反例:不允许声明本地变量
let a: number = 1;
}
build() {
// 反例:不允许console.info
console.info('print debug log');
}
build() {
// 反例:不允许本地作用域
{
...
}
}
@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())
}
}
}
build() {
Column() {
// 反例:不允许使用switch语法
switch (expression) {
case 1:
Text('...')
break;
case 2:
Image('...')
break;
default:
Text('...')
break;
}
}
}
ArkUI给自定义组件设置样式时,相当于给MyComponent2套了一个不可见的容器组件,而这些样式是设置在容器组件上的,而非直接设置给MyComponent2的Button组件。通过渲染结果我们可以很清楚的看到,背景颜色红色并没有直接生效在Button上,而是生效在Button所处的开发者不可见的容器组件上。
在开始之前,我们先明确自定义组件和页面的关系:
页面生命周期,即被@Entry装饰的组件生命周期,提供以下生命周期接口:
组件生命周期,即一般用@Component装饰的自定义组件的生命周期,提供以下生命周期接口:
生命周期流程如下图所示,下图展示的是被@Entry装饰的组件(首页)生命周期。
根据上面的流程图,我们从自定义组件的初始创建、重新渲染和删除来详细解释。
// Index.ets
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 aboutToAppear
if (this.showChild) {
Child()
}
// this.showChild为false,删除Child子组件,执行Child aboutToDisappear
Button('create or delete Child').onClick(() => {
this.showChild = false;
})
// push到Page2页面,执行onPageHide
Button('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';
})
}
}
说明
从API version 9开始,该装饰器支持在ArkTS卡片中使用。
@Component
struct ImageComponent {
@Builder MyBuilderFunction(){
Row() {
Image(this.url)
.width(200)
.height(200)
}
}
@State url: string = 'https://dogefs.s3.ladydaily.com/~/source/wallhaven/full/p9/wallhaven-p97l5e.png?w=2560&h=1440&fmt=webp';
build() {
this.MyBuilderFunction()
}
}
@Builder MyBuilderFunction(){ ... }
使用方法:
this.MyBuilderFunction(){ ... }
@Builder function MyGlobalBuilderImageComponent(){
Row() {
Image('https://dogefs.s3.ladydaily.com/~/source/wallhaven/full/p9/wallhaven-p97l5e.png?w=2560&h=1440&fmt=webp')
.width(200)
.height(200)
}
}
@Builder function MyGlobalBuilderFunction(){ ... }
使用方法:
MyGlobalBuilderFunction()
自定义构建函数的参数传递有按值传递和按引用传递两种,均需遵守以下规则:
按引用传递参数时,传递的参数可为状态变量,且状态变量的改变会引起@Builder方法内的UI刷新。ArkUI提供$$作为按引用传递参数的范式。
@Builder function MyGlobalBuilderImageComponent($$ : { url: string } ){
Row() {
Image(`${$$.url}`)
.width(200)
.height(200)
}
}
@Entry
@Component
struct Index {
@State urls: string="https://dogefs.s3.ladydaily.com/~/source/wallhaven/full/9d/wallhaven-9d9111.jpg?w=2560&h=1440&fmt=webp"
build() {
Row() {
Column(){
MyGlobalBuilderImageComponent({url:this.urls })
Divider()
Button("改变图片").onClick(()=>{
this.urls="https://dogefs.s3.ladydaily.com/~/source/wallhaven/full/85/wallhaven-8596q2.jpg?w=2560&h=1440&fmt=webp"
})
.width(200)
.width(100)
}.width(200)
Column(){
ImageComponent()
Divider()
Button("改变图片").onClick(()=>{
})
.width(200)
.width(100)
}.width(200)
}
.height('100%')
}
}
调用@Builder装饰的函数默认按值传递。当传递的参数为状态变量时,状态变量的改变不会引起@Builder方法内的UI刷新。所以当使用状态变量的时候,推荐使用按引用传递。
@Entry
@Component
struct Index {
urls: string="https://dogefs.s3.ladydaily.com/~/source/wallhaven/full/9d/wallhaven-9d9111.jpg?w=2560&h=1440&fmt=webp"
build() {
Row() {
Column(){
MyGlobalBuilderImageComponent({url:this.urls })
Divider()
Button("改变图片").onClick(()=>{
this.urls="https://dogefs.s3.ladydaily.com/~/source/wallhaven/full/85/wallhaven-8596q2.jpg?w=2560&h=1440&fmt=webp"
})
.width(200)
.width(100)
}.width(200)
Column(){
ImageComponent()
Divider()
Button("改变图片").onClick(()=>{
})
.width(200)
.width(100)
}.width(200)
}
.height('100%')
}
}
@Component
struct ImageComponent {
@State url:string="https://dogefs.s3.ladydaily.com/~/source/wallhaven/full/9d/wallhaven-9d89zw.jpg?w=2560&h=1440&fmt=webp"
@Builder MyBuilderFunction($$ : { url: string } ){
Row() {
Image(`${$$.url}`)
.width(200)
.height(200)
}
}
build() {
this.MyBuilderFunction({ url: this.url})
}
}
@Builder function MyGlobalBuilderImageComponent($$ : { url: string } ){
Row() {
Image(`${$$.url}`)
.width(200)
.height(200)
}
}
@Entry
@Component
struct Index {
@Builder component1Builder() {
Column(){
Row() {
Image("https://www4.bing.com//th?id=OHR.ThreeElephants_ZH-CN8708711085_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp")
.width(200)
.height(200)
}
Divider()
}.width(200)
}
build() {
Row() {
ImageComponentSolt0({component0:MyGlobalBuilderImageComponent})
ImageComponentSolt01({component1:this.component1Builder})
}
.height('100%')
}
}
@Builder function MyGlobalBuilderImageComponent($$ : { url: string } ){
Row() {
Image(`${$$.url}`)
.width(200)
.height(200)
}
}
@Component
struct ImageComponentSolt0 {
// 一个插槽:内容由里面的决定(有参)
@BuilderParam component0: ($$ : { url : string}) => void;
build() {
Column(){
this.component0({url:"https://www4.bing.com//th?id=OHR.CameraSquirrel_ZH-CN3580119980_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp"})
Divider()
}.width(200)
}
}
@Component
struct ImageComponentSolt01{
// 一个插槽:内容由里面的决定(无参)
@BuilderParam component1: () => void;
build() {
Column(){
this.component1()
Divider()
}.width(200)
}
}
build() {
Row() {
ImageComponentSolt0({component0:MyGlobalBuilderImageComponent})
// ImageComponentSolt01({component1:this.component1Builder})
ImageComponentSolt0(){
MyGlobalBuilderImageComponent({url:"https://files.codelife.cc/wallpaper/wallspic/20231122c33j29.jpeg?x-oss-process=image/resize,limit_0,m_fill,w_2560,h_1440/quality,Q_92/format,webp"})
}
}
.height('100%')
}
我们来重点理解一下:@BuilderParam最重要的是对@Builder装饰器的引用
如果每个组件的样式都需要单独设置,在开发过程中会出现大量代码在进行重复样式设置,虽然可以复制粘贴,但为了代码简洁性和后续方便维护,我们推出了可以提炼公共样式进行复用的装饰器@Styles。
@Styles装饰器可以将多条样式设置提炼成一个方法,直接在组件声明的位置调用。通过@Styles装饰器可以快速定义并复用自定义样式。用于快速定义并复用自定义样式。
在上面的案例上为他添加统一的样式,统一的高度与宽度
@Entry
@Component
struct Index {
@Builder component1Builder() {
Column(){
Row() {
Image("https://www4.bing.com//th?id=OHR.ThreeElephants_ZH-CN8708711085_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp")
.globalImagesStyle()
}
Divider()
}.width(200)
}
build() {
Row() {
ImageComponentSolt0({component0:MyGlobalBuilderImageComponent})
ImageComponentSolt01({component1:this.component1Builder})
// ImageComponentSolt0(){
// MyGlobalBuilderImageComponent({url:"https://files.codelife.cc/wallpaper/wallspic/20231122c33j29.jpeg?x-oss-process=image/resize,limit_0,m_fill,w_2560,h_1440/quality,Q_92/format,webp"})
// }
}
.height('100%')
}
}
@Builder function MyGlobalBuilderImageComponent($$ : { url: string } ){
Row() {
Image(`${$$.url}`)
.globalImagesStyle()
}
}
@Component
struct ImageComponentSolt0 {
// 一个插槽:内容由里面的决定(有参)
@BuilderParam component0: ($$ : { url : string}) => void;
build() {
Column(){
this.component0({url:"https://www4.bing.com//th?id=OHR.CameraSquirrel_ZH-CN3580119980_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp"})
Divider()
}.width(200)
}
}
@Component
struct ImageComponentSolt01{
// 一个插槽:内容由里面的决定(有参)
@BuilderParam component1: () => void;
build() {
Column(){
this.component1()
Divider()
}.width(200)
}
}
@Styles function globalImagesStyle(){
.width(400)
.height(400)
.backgroundColor(Color.Black)
}
@Extend,用于扩展原生组件样式,简单来说扩展组件
我们在上一个案例上进一步扩展,改变图片的高度
@Extend(Image) function changeHeight(h:number){
.height(h)
}
@Entry
@Component
struct Index {
@Builder component1Builder() {
Column(){
Row() {
Image("https://www4.bing.com//th?id=OHR.ThreeElephants_ZH-CN8708711085_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp")
.globalImagesStyle()
.changeHeight(900)
}
Divider()
}.width(200)
}
build() {
Row() {
ImageComponentSolt0({component0:MyGlobalBuilderImageComponent})
ImageComponentSolt01({component1:this.component1Builder})
// ImageComponentSolt0(){
// MyGlobalBuilderImageComponent({url:"https://files.codelife.cc/wallpaper/wallspic/20231122c33j29.jpeg?x-oss-process=image/resize,limit_0,m_fill,w_2560,h_1440/quality,Q_92/format,webp"})
// }
}
.height('100%')
}
}
@Builder function MyGlobalBuilderImageComponent($$ : { url: string } ){
Row() {
Image(`${$$.url}`)
.globalImagesStyle()
.changeHeight(900)
}
}
@Component
struct ImageComponentSolt0 {
// 一个插槽:内容由里面的决定(有参)
@BuilderParam component0: ($$ : { url : string}) => void;
build() {
Column(){
this.component0({url:"https://www4.bing.com//th?id=OHR.CameraSquirrel_ZH-CN3580119980_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp"})
Divider()
}.width(200)
}
}
@Component
struct ImageComponentSolt01{
// 一个插槽:内容由里面的决定(有参)
@BuilderParam component1: () => void;
build() {
Column(){
this.component1()
Divider()
}.width(200)
}
}
@Styles function globalImagesStyle(){
.width(400)
.height(400)
.backgroundColor(Color.Black)
}
@Extend(Image) function changeHeight(h:number){
.height(h)
}
stateStyles是属性方法,可以根据UI内部状态来设置样式,类似于css伪类,但语法不同。ArkUI提供以下四种状态:
@Entry
@Component
struct MyComponent {
@Styles normalStyle() {
.backgroundColor(Color.Gray)
}
@Styles pressedStyle() {
.backgroundColor(Color.Red)
}
build() {
Column() {
Text('Text1')
.fontSize(50)
.fontColor(Color.White)
.stateStyles({
normal: this.normalStyle,
pressed: this.pressedStyle,
})
}
}
}
@Entry
@Component
struct CompWithInlineStateStyles {
@State focusedColor: Color = Color.Red;
normalColor: Color = Color.Green
build() {
Button('clickMe').height(100).width(100)
.stateStyles({
normal: {
.backgroundColor(this.normalColor)
},
focused: {
.backgroundColor(this.focusedColor)
}
})
.onClick(() => {
this.focusedColor = Color.Pink
})
.margin('30%')
}
}
Button默认获焦显示红色,点击事件触发后,获焦态变为粉色
ArkUI提供了多种装饰器,通过使用这些装饰器,状态变量不仅可以观察在组件内的改变,还可以在不同组件层级间传递,比如父子组件、跨组件层级,也可以观察全局范围内的变化。根据状态变量的影响范围,将所有的装饰器可以大致分为:
从数据的传递形式和同步类型层面看,装饰器也可分为:
我们来个加法计数器案例:
@Entry
@Component
struct Index {
@State count:number=0
build() {
Row() {
Column() {
Text("总数:"+this.count)
Divider()
Button("加法").width(100).height(30).margin(10).onClick(()=>{
this.count ++;
})
Divider()
Button("减法").width(100).height(30).margin(10).onClick(()=>{
this.count --;
})
}
.width('100%')
}
.height('100%')
}
}
@State变量装饰器 | 说明 |
---|---|
装饰器参数 | 无 |
同步类型 | 不与父组件中任何类型的变量同步。 |
允许装饰的变量类型 | Object、class、string、number、boolean、enum类型,以及这些类型的数组。嵌套类型的场景请参考观察变化。 类型必须被指定。 不支持any,不支持简单类型和复杂类型的联合类型,不允许使用undefined和null。 说明 |
被装饰变量的初始值 | 必须本地初始化。 |
其他内容参考官网:内容限制,使用能够引起UI变化条件
@Entry
@Component
struct StateManagement {
// @State 必须初始化
@State name: string = '帝心'
build() {
Row() {
Column() {
Text(this.name).StateManagement_textSty()
Button('修改数据').StateManagement_btnStyle(() => {
this.name = this.name === '帝心' ? '庄生' : '帝心'
})
Divider()
StateManagement_prop({ content_prop: this.name })
Divider()
}
.width('100%')
}
.height('100%')
}
}
// 存放一个 @Prop 装饰的状态数据。方便父子组件之间进行数据传递和同步 State ----> prop
@Component
struct StateManagement_prop {
@Prop content_prop: string
build() {
Column() {
Text('prop:' + this.content_prop).StateManagement_textSty()
Button('修改prop数据').StateManagement_btnStyle(() => {
this.content_prop = 'HarmonyOS4.0'
})
}
}
}
// 同样的样式记得复用 text
@Extend(Text) function StateManagement_textSty() {
.fontSize(30)
.fontWeight(FontWeight.Bold)
.fontColor(Color.Green)
}
// button样式
@Extend(Button) function StateManagement_btnStyle(click: Function) {
.fontSize(30)
.onClick(() => {
click()
})
}
限制条件
规则限制
@Prop变量装饰器 | 说明 |
---|---|
装饰器参数 | 无 |
同步类型 | 单向同步:对父组件状态变量值的修改,将同步给子组件@Prop装饰的变量,子组件@Prop变量的修改不会同步到父组件的状态变量上 |
允许装饰的变量类型 | string、number、boolean、enum类型。 不支持any,不允许使用undefined和null。 必须指定类型。 在父组件中,传递给@Prop装饰的值不能为undefined或者null,反例如下所示。 CompA ({ aProp: undefined }) CompA ({ aProp: null }) @Prop和数据源类型需要相同,有以下三种情况(数据源以@State为例): - @Prop装饰的变量和父组件状态变量类型相同,即@Prop : S和@State : S,示例请参考父组件@State到子组件@Prop简单数据类型同步。 - 当父组件的状态变量为数组时,@Prop装饰的变量和父组件状态变量的数组项类型相同,即@Prop : S和@State : Array - 当父组件状态变量为Object或者class时,@Prop装饰的变量和父组件状态变量的属性类型相同,即@Prop : S和@State : { propA: S },示例请参考从父组件中的@State类对象属性到@Prop简单类型的同步。 |
被装饰变量的初始值 | 允许本地初始化。 |
@Entry
@Component
struct StateManagement {
// @State 必须初始化: 本地初始化
@State name: string = '我是初始化本地数据'
build() {
Row() {
Column() {
Text(this.name).StateManagement_textSty()
Button('修改数据').StateManagement_btnStyle(() => {
this.name = this.name === '我是初始化本地数据' ? '我是初始化本地数据01' : '我是初始化本地数据02'
})
Divider()
StateManagement_prop({ content_prop: this.name })
Divider()
StateManagement_Link({ content_link: $name })
}
.width('100%')
}
.height('100%')
}
}
// 存放一个 @Prop 装饰的状态数据。方便父子组件之间进行数据传递和同步 State ----> prop
@Component
struct StateManagement_prop {
@Prop content_prop: string
build() {
Column() {
Text('prop:' + this.content_prop).StateManagement_textSty()
Button('修改prop数据').StateManagement_btnStyle(() => {
this.content_prop = 'HarmonyOS4.0'
})
}
}
}
@Component
struct StateManagement_Link {
@Link content_link: string
build() {
Column() {
Text('link:' + this.content_link).StateManagement_textSty()
Button('修改Link数据').StateManagement_btnStyle(() => {
this.content_link = 'HarmonyOS4.0---Link'
})
}
}
}
// 同样的样式记得复用 text
@Extend(Text) function StateManagement_textSty() {
.fontSize(30)
.fontWeight(FontWeight.Bold)
.fontColor(Color.Green)
}
// button样式
@Extend(Button) function StateManagement_btnStyle(click: Function) {
.fontSize(30)
.onClick(() => {
click()
})
}
规则
@Link变量装饰器 | 说明 |
---|---|
装饰器参数 | 无 |
同步类型 | 双向同步。 父组件中@State, @StorageLink和@Link 和子组件@Link可以建立双向数据同步,反之亦然。 |
允许装饰的变量类型 | Object、class、string、number、boolean、enum类型,以及这些类型的数组。嵌套类型的场景请参考观察变化。 类型必须被指定,且和双向绑定状态变量的类型相同。 不支持any,不支持简单类型和复杂类型的联合类型,不允许使用undefined和null。 说明 |
被装饰变量的初始值 | 无,禁止本地初始化。 |
@Provide/@Consume装饰的状态变量有以下特性:
@Component
struct CompD {
@Consume reviewVotes: number;
@State url: string="组件"
build() {
Column() {
Text(`reviewVotes(${this.url})`)
Button(`reviewVotes(${this.reviewVotes}), give +1`)
.onClick(() => this.reviewVotes += 1)
}
.width('50%')
}
}
@Entry
@Component
struct CompA {
@Provide reviewVotes: number = 0;
build() {
Column() {
Button(`reviewVotes(${this.reviewVotes}), give +1`)
.onClick(() => this.reviewVotes += 1)
CompD({url:"组件一"})
Divider()
CompD({url:"组件二"})
}
}
}
我们可以发现同级组件中发生变化相互影响
规则
@State的规则同样适用于@Provide,差异为@Provide还作为多层后代的同步源。
@Provide变量装饰器 | 说明 |
---|---|
装饰器参数 | 别名:常量字符串,可选。 如果指定了别名,则通过别名来绑定变量;如果未指定别名,则通过变量名绑定变量。 |
同步类型 | 双向同步。 从@Provide变量到所有@Consume变量以及相反的方向的数据同步。双向同步的操作与@State和@Link的组合相同。 |
允许装饰的变量类型 | Object、class、string、number、boolean、enum类型,以及这些类型的数组。嵌套类型的场景请参考观察变化。 不支持any,不支持简单类型和复杂类型的联合类型,不允许使用undefined和null。 必须指定类型。@Provide变量的@Consume变量的类型必须相同。 说明 |
被装饰变量的初始值 | 必须指定。 |
@Consume变量装饰器 | 说明 |
---|---|
装饰器参数 | 别名:常量字符串,可选。 如果提供了别名,则必须有@Provide的变量和其有相同的别名才可以匹配成功;否则,则需要变量名相同才能匹配成功。 |
同步类型 | 双向:从@Provide变量(具体请参见@Provide)到所有@Consume变量,以及相反的方向。双向同步操作与@State和@Link的组合相同。 |
允许装饰的变量类型 | Object、class、string、number、boolean、enum类型,以及这些类型的数组。嵌套类型的场景请参考观察变化。 不支持any,不允许使用undefined和null。 必须指定类型。@Provide变量的@Consume变量的类型必须相同。 说明 |
被装饰变量的初始值 | 无,禁止本地初始化。 |
@Component
struct CompD {
@ObjectLink b: ClassB
build() {
Column() {
Text("@Component"+this.b.a.c)
Text("@Component"+this.b.b)
}
.width('50%')
}
}
@Entry
@Component
struct CompA {
@State b: ClassB=new ClassB(new ClassA(1),100)
build() {
Column() {
CompD({b:this.b})
Divider()
Button("修改值").width(100).height(30).margin(10).onClick(()=>{
this.b=new ClassB(new ClassA(200),300)
})
}
}
}
class ClassA {
public c: number;
constructor(c: number) {
this.c = c;
}
}
@Observed
class ClassB {
public a: ClassA;
public b: number;
constructor(a: ClassA, b: number) {
this.a = a;
this.b = b;
}
}
规则
@Observed类装饰器 | 说明 |
---|---|
装饰器参数 | 无 |
类装饰器 | 装饰class。需要放在class的定义前,使用new创建类对象。 |
@ObjectLink变量装饰器 | 说明 |
---|---|
装饰器参数 | 无 |
同步类型 | 不与父组件中的任何类型同步变量。 |
允许装饰的变量类型 | 必须为被@Observed装饰的class实例,必须指定类型。 不支持简单类型,可以使用@Prop。 @ObjectLink的属性是可以改变的,但是变量的分配是不允许的,也就是说这个装饰器装饰变量是只读的,不能被改变。 |
被装饰变量的初始值 | 不允许。 |
如果开发者要实现应用级的,或者多个页面的状态数据共享,就需要用到应用级别的状态管理的概念。ArkTS根据不同特性,提供了多种应用状态管理的能力:
// 创建新实例并使用给定对象初始化
let storage = new LocalStorage({ 'PropA': 47 });
// 使LocalStorage可从@Component组件访问
@Entry(storage)
@Component
struct CompA {
// @LocalStorageProp变量装饰器与LocalStorage中的'PropA'属性建立单向绑定
@LocalStorageProp('PropA') storProp1: number = 1;
build() {
Column() {
Text("默认数字:"+this.storProp1).StateManagement_textSty()
Button('修改数据').StateManagement_btnStyle(() => {
this.storProp1 += 1
})
Divider()
StateManagement_prop()
Divider()
StateManagement_link()
}
}
}
// 存放一个 @Prop 装饰的状态数据。方便父子组件之间进行数据传递和同步 State ----> prop
@Component
struct StateManagement_prop {
// @LocalStorageProp变量装饰器与LocalStorage中的'PropA'属性建立单向绑定
@LocalStorageProp('PropA') storProp2: number = 2;
build() {
Column() {
Text('prop:' + this.storProp2).StateManagement_textSty()
Button('修改prop数据').StateManagement_btnStyle(() => {
this.storProp2++;
})
}
}
}
@Component
struct StateManagement_link {
// @LocalStorageProp变量装饰器与LocalStorage中的'PropA'属性建立单向绑定
@LocalStorageLink('PropA') storProp2: number = 2;
build() {
Column() {
Text('link:' + this.storProp2).StateManagement_textSty()
Button('修改link数据').StateManagement_btnStyle(() => {
this.storProp2++;
})
}
}
}
// 同样的样式记得复用 text
@Extend(Text) function StateManagement_textSty() {
.fontSize(30)
.fontWeight(FontWeight.Bold)
.fontColor(Color.Green)
}
// button样式
@Extend(Button) function StateManagement_btnStyle(click: Function) {
.fontSize(30)
.onClick(() => {
click()
})
}
规则
@LocalStorageProp变量装饰器 | 说明 |
---|---|
装饰器参数 | key:常量字符串,必填(字符串需要有引号)。 |
允许装饰的变量类型 | Object、class、string、number、boolean、enum类型,以及这些类型的数组。嵌套类型的场景请参考观察变化和行为表现。 类型必须被指定,且必须和LocalStorage中对应属性相同。不支持any,不允许使用undefined和null。 |
同步类型 | 单向同步:从LocalStorage的对应属性到组件的状态变量。组件本地的修改是允许的,但是LocalStorage中给定的属性一旦发生变化,将覆盖本地的修改。 |
被装饰变量的初始值 | 必须指定,如果LocalStorage实例中不存在属性,则作为初始化默认值,并存入LocalStorage中。 |
@LocalStorageLink变量装饰器 | 说明 |
---|---|
装饰器参数 | key:常量字符串,必填(字符串需要有引号)。 |
允许装饰的变量类型 | Object、class、string、number、boolean、enum类型,以及这些类型的数组。嵌套类型的场景请参考观察变化和行为表现。 类型必须被指定,且必须和LocalStorage中对应属性相同。不支持any,不允许使用undefined和null。 |
同步类型 | 双向同步:从LocalStorage的对应属性到自定义组件,从自定义组件到LocalStorage对应属性。 |
被装饰变量的初始值 | 必须指定,如果LocalStorage实例中不存在属性,则作为初始化默认值,并存入LocalStorage中。 |
@StorageProp
AppStorage.SetOrCreate('PropC', 10086);
// 创建新实例并使用给定对象初始化
let storage = new LocalStorage({ 'PropA': 47 });
// 使LocalStorage可从@Component组件访问
@Entry(storage)
@Component
struct CompA {
// 全局 @StorageProp与AppStorage进行绑定
@StorageProp('PropC') storLink: number = 1;
// @LocalStorageProp变量装饰器与LocalStorage中的'PropA'属性建立单向绑定
@LocalStorageProp('PropA') storProp1: number = 1;
build() {
Column() {
Text("默认数字:"+this.storProp1).StateManagement_textSty()
Button('修改数据页面ui').StateManagement_btnStyle(() => {
this.storProp1 += 1
})
Text("默认数字:"+this.storLink).StateManagement_textSty()
Button('修改数据全局ui').StateManagement_btnStyle(() => {
this.storLink += 1
})
}
}
}
// 存放一个 @Prop 装饰的状态数据。方便父子组件之间进行数据传递和同步 State ----> prop
@Component
struct StateManagement_prop {
// @LocalStorageProp变量装饰器与LocalStorage中的'PropA'属性建立单向绑定
@LocalStorageProp('PropA') storProp2: number = 2;
build() {
Column() {
Text('prop:' + this.storProp2).StateManagement_textSty()
Button('修改prop数据').StateManagement_btnStyle(() => {
this.storProp2++;
})
}
}
}
@Component
struct StateManagement_link {
// @LocalStorageProp变量装饰器与LocalStorage中的'PropA'属性建立单向绑定
@LocalStorageLink('PropA') storProp2: number = 2;
build() {
Column() {
Text('link:' + this.storProp2).StateManagement_textSty()
Button('修改link数据').StateManagement_btnStyle(() => {
this.storProp2++;
})
}
}
}
// 同样的样式记得复用 text
@Extend(Text) function StateManagement_textSty() {
.fontSize(30)
.fontWeight(FontWeight.Bold)
.fontColor(Color.Green)
}
// button样式
@Extend(Button) function StateManagement_btnStyle(click: Function) {
.fontSize(30)
.onClick(() => {
click()
})
}
我们可以看到一个全局的修改,一个是页面的修改并不会互相影响
规则
@StorageProp变量装饰器 | 说明 |
---|---|
装饰器参数 | key:常量字符串,必填(字符串需要有引号)。 |
允许装饰的变量类型 | Object class、string、number、boolean、enum类型,以及这些类型的数组。嵌套类型的场景请参考观察变化和行为表现。 类型必须被指定,且必须和LocalStorage中对应属性相同。不支持any,不允许使用undefined和null。 |
同步类型 | 单向同步:从AppStorage的对应属性到组件的状态变量。 组件本地的修改是允许的,但是AppStorage中给定的属性一旦发生变化,将覆盖本地的修改。 |
被装饰变量的初始值 | 必须指定,如果AppStorage实例中不存在属性,则作为初始化默认值,并存入AppStorage中。 |
@StorageLink
AppStorage.SetOrCreate('PropC', 10086);
// 创建新实例并使用给定对象初始化
let storage = new LocalStorage({ 'PropA': 47 });
// 使LocalStorage可从@Component组件访问
@Entry(storage)
@Component
struct CompA {
// 全局 @StorageProp与AppStorage进行绑定
@StorageProp('PropC') storLink: number = 1;
// @LocalStorageProp变量装饰器与LocalStorage中的'PropA'属性建立单向绑定
@LocalStorageProp('PropA') storProp1: number = 1;
@StorageLink("PropC") storLink1: number = 1;
build() {
Column() {
Text("默认数字:"+this.storProp1).StateManagement_textSty()
Button('修改数据页面ui').StateManagement_btnStyle(() => {
this.storProp1 += 1
})
Text("默认数字:"+this.storLink).StateManagement_textSty()
Button('修改数据全局ui-prop').StateManagement_btnStyle(() => {
this.storLink += 1
})
Text("默认数字:"+this.storLink1).StateManagement_textSty()
Button('修改数据全局ui-link').StateManagement_btnStyle(() => {
this.storLink1 += 1
})
Divider()
StateManagement_link()
}
}
}
@Component
struct StateManagement_link {
// @LocalStorageProp变量装饰器与LocalStorage中的'PropA'属性建立单向绑定
@StorageLink('PropC') storProp2: number = 2;
build() {
Column() {
Text('link:' + this.storProp2).StateManagement_textSty()
Button('修改link数据').StateManagement_btnStyle(() => {
this.storProp2++;
})
}
}
}
// 同样的样式记得复用 text
@Extend(Text) function StateManagement_textSty() {
.fontSize(30)
.fontWeight(FontWeight.Bold)
.fontColor(Color.Green)
}
// button样式
@Extend(Button) function StateManagement_btnStyle(click: Function) {
.fontSize(30)
.onClick(() => {
click()
})
}
我们可以观察到当我们点击全局修改后,使用了同一个Key的值进行修改
规则
@StorageLink变量装饰器 | 说明 |
---|---|
装饰器参数 | key:常量字符串,必填(字符串需要有引号)。 |
允许装饰的变量类型 | Object、class、string、number、boolean、enum类型,以及这些类型的数组。嵌套类型的场景请参考观察变化和行为表现。 类型必须被指定,且必须和AppStorage中对应属性相同。不支持any,不允许使用undefined和null。 |
同步类型 | 双向同步:从AppStorage的对应属性到自定义组件,从自定义组件到AppStorage对应属性。 |
被装饰变量的初始值 | 必须指定,如果AppStorage实例中不存在属性,则作为初始化默认值,并存入AppStorage中。 |
AppStorage.SetOrCreate('PropC', 10086);
// 创建新实例并使用给定对象初始化
let storage = new LocalStorage({ 'PropA': 47 });
PersistentStorage.PersistProp('PropB', 11100215);
// 使LocalStorage可从@Component组件访问
@Entry(storage)
@Component
struct CompA {
// 全局 @StorageProp与AppStorage进行绑定
@StorageProp('PropC') storLink: number = 1;
// @LocalStorageProp变量装饰器与LocalStorage中的'PropA'属性建立单向绑定
@LocalStorageProp('PropA') storProp1: number = 1;
@StorageLink("PropC") storLink1: number = 1;
build() {
Column() {
Text("默认数字:"+this.storProp1).StateManagement_textSty()
Button('修改数据页面ui').StateManagement_btnStyle(() => {
this.storProp1 += 1
})
Text("默认数字:"+this.storLink).StateManagement_textSty()
Button('修改数据全局ui-prop').StateManagement_btnStyle(() => {
this.storLink += 1
})
Text("默认数字:"+this.storLink1).StateManagement_textSty()
Button('修改数据全局ui-link').StateManagement_btnStyle(() => {
this.storLink1 += 1
})
Divider()
StateManagement_link()
Divider()
Text(AppStorage.Get('PropB')).StateManagement_textSty()
}
}
}
@Component
struct StateManagement_link {
// @LocalStorageProp变量装饰器与LocalStorage中的'PropA'属性建立单向绑定
@StorageLink('PropC') storProp2: number = 2;
build() {
Column() {
Text('link:' + this.storProp2).StateManagement_textSty()
Button('修改link数据').StateManagement_btnStyle(() => {
this.storProp2++;
})
}
}
}
// 同样的样式记得复用 text
@Extend(Text) function StateManagement_textSty() {
.fontSize(30)
.fontWeight(FontWeight.Bold)
.fontColor(Color.Green)
}
// button样式
@Extend(Button) function StateManagement_btnStyle(click: Function) {
.fontSize(30)
.onClick(() => {
click()
})
}
开发者如果需要应用程序运行的设备的环境参数,以此来作出不同的场景判断,比如多语言,暗黑模式等,需要用到Environment设备环境查询。
AppStorage.SetOrCreate('PropC', 10086);
let storage = new LocalStorage({ 'PropA': 47 });
Environment.EnvProp('languageCode', 'en');
@Entry(storage)
@Component
struct CompA {
// 全局 @StorageProp与AppStorage进行绑定
@StorageProp('PropC') storLink: number = 1;
// @LocalStorageProp变量装饰器与LocalStorage中的'PropA'属性建立单向绑定
@LocalStorageProp('PropA') storProp1: number = 1;
@StorageLink("PropC") storLink1: number = 1;
build() {
Column() {
Text("默认数字:"+this.storProp1).StateManagement_textSty()
Button('修改数据页面ui').StateManagement_btnStyle(() => {
this.storProp1 += 1
})
Text("默认数字:"+this.storLink).StateManagement_textSty()
Button('修改数据全局ui-prop').StateManagement_btnStyle(() => {
this.storLink += 1
})
Text("默认数字:"+this.storLink1).StateManagement_textSty()
Button('修改数据全局ui-link').StateManagement_btnStyle(() => {
this.storLink1 += 1
})
Divider()
StateManagement_link()
Divider()
Text(AppStorage.Get('languageCode')).StateManagement_textSty()
}
}
}
@Component
struct StateManagement_link {
// @LocalStorageProp变量装饰器与LocalStorage中的'PropA'属性建立单向绑定
@StorageLink('PropC') storProp2: number = 2;
build() {
Column() {
Text('link:' + this.storProp2).StateManagement_textSty()
Button('修改link数据').StateManagement_btnStyle(() => {
this.storProp2++;
})
}
}
}
// 同样的样式记得复用 text
@Extend(Text) function StateManagement_textSty() {
.fontSize(30)
.fontWeight(FontWeight.Bold)
.fontColor(Color.Green)
}
// button样式
@Extend(Button) function StateManagement_btnStyle(click: Function) {
.fontSize(30)
.onClick(() => {
click()
})
}
@Watch用于监听状态变量的变化,当状态变量变化时,@Watch的回调方法将被调用。@Watch在ArkUI框架内部判断数值有无更新使用的是严格相等(===),遵循严格相等规范。当在严格相等为false的情况下,就会触发@Watch的回调。
@Component
struct TotalView {
@Prop @Watch('onCountUpdated') count: number;
@State total: number = 0;
// @Watch cb
onCountUpdated(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 })
}
}
}
@Watch补充变量装饰器 | 说明 |
---|---|
装饰器参数 | 必填。常量字符串,字符串需要有引号。是(string) => void自定义成员函数的方法的引用。 |
可装饰的自定义组件变量 | 可监听所有装饰器装饰的状态变量。不允许监听常规变量。 |
装饰器的顺序 | 建议@State、@Prop、@Link等装饰器在@Watch装饰器之前。 |
$$运算符为系统内置组件提供TS变量的引用,使得TS变量和系统内置组件的内部状态保持同步。
// xxx.ets
@Entry
@Component
struct RefreshExample {
@State isRefreshing: boolean = false
@State counter: number = 0
build() {
Column() {
Text('Pull Down and isRefreshing: ' + this.isRefreshing)
.fontSize(30)
.margin(10)
Refresh({ refreshing: $$this.isRefreshing, offset: 120, friction: 100 }) {
Text('Pull Down and refresh: ' + this.counter)
.fontSize(30)
.margin(10)
}
.onStateChange((refreshStatus: RefreshStatus) => {
console.info('Refresh onStatueChange state is ' + refreshStatus)
})
}
}
}
ArkUI通过自定义组件的build()函数和@builder装饰器中的声明式UI描述语句构建相应的UI。在声明式描述语句中开发者除了使用系统组件外,还可以使用渲染控制语句来辅助UI的构建,这些渲染控制语句包括控制组件是否显示的条件渲染语句,基于数组数据快速生成组件的循环渲染语句以及针对大数据量场景的数据懒加载语句。
ArkTS提供了渲染控制的能力。条件渲染可根据应用的不同状态,使用if、else和else if渲染对应状态下的UI内容。
// 使用Environment.EnvProp将设备运行languageCode存入AppStorage中;
Environment.EnvProp('languageCode', 'zh');
// 从AppStorage获取单向绑定的languageCode的变量
const lang: SubscribedAbstractProperty = AppStorage.Prop('languageCode');
@Entry
@Component
struct RefreshExample {
@State isRefreshing: boolean = false
@State counter: number = 0
build() {
Column() {
Text(lang.get())
Divider()
if (lang.get() === 'zh') {
Text('下拉筛选状态: ' + this.isRefreshing)
.fontSize(30)
.margin(10)
}
else {
Text('Pull Down and isRefreshing: ' + this.isRefreshing)
.fontSize(30)
.margin(10)
}
Refresh({ refreshing: $$this.isRefreshing, offset: 120, friction: 100 }) {
if (lang.get() === 'zh') {
Text('下拉刷新次数: ' + this.counter)
.fontSize(30)
.margin(10)
}else{
Text('Pull Down and refresh: ' + this.counter)
.fontSize(30)
.margin(10)
}
}
.onStateChange((refreshStatus: RefreshStatus) => {
console.info('Refresh onStatueChange state is ' + refreshStatus)
})
}
}
}
ForEach接口基于数组类型数据来进行循环渲染,需要与容器组件配合使用,且接口返回的组件应当是允许包含在ForEach父容器组件中的子组件。
接口描述
ForEach(
arr: Array,
itemGenerator: (item: Array, index?: number) => void,
keyGenerator?: (item: Array, index?: number): string => string
)
在ForEach循环渲染过程中,系统会为每个数组元素生成一个唯一且持久的键值,用于标识对应的组件。当这个键值变化时,ArkUI框架将视为该数组元素已被替换或修改,并会基于新的键值创建一个新的组件。
@Entry
@Component
struct ArticleList {
@State simpleList: Array = [1, 2, 3, 4, 5];
build() {
Column() {
ForEach(this.simpleList,
(item: string,index:number) => {
Text(index+"- " + item)
ArticleSkeletonView().margin({ top: 20 })
},
(item: string) => item)
}
.padding(20)
.width('100%')
.height('100%')
}
}
@Builder
function textArea(width: number | Resource | string = '100%', height: number | Resource | string = '100%') {
Row()
.width(width)
.height(height)
.backgroundColor('#FFF2F3F4')
}
@Component
struct ArticleSkeletonView {
build() {
Row() {
Column() {
textArea(80, 80)
}
.margin({ right: 20 })
Column() {
textArea('60%', 20)
textArea('50%', 20)
}
.alignItems(HorizontalAlign.Start)
.justifyContent(FlexAlign.SpaceAround)
.height('100%')
}
.padding(20)
.borderRadius(12)
.backgroundColor('#FFECECEC')
.height(120)
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
}
LazyForEach从提供的数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。当在滚动容器中使用了LazyForEach,框架会根据滚动容器可视区域按需创建组件,当组件滑出可视区域外时,框架会进行组件销毁回收以降低内存占用。
接口描述
LazyForEach(
dataSource: IDataSource, // 需要进行数据迭代的数据源
itemGenerator: (item: any, index?: number) => void, // 子组件生成函数
keyGenerator?: (item: any, index?: number) => string // 键值生成函数
): void
IDataSource 接口
interface IDataSource {
totalCount(): number; // 获得数据总数
getData(index: number): Object; // 获取索引值对应的数据
registerDataChangeListener(listener: DataChangeListener): void; // 注册数据改变的监听器
unregisterDataChangeListener(listener: DataChangeListener): void; // 注销数据改变的监听器
}
registerDataChangeListener
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; // 改变数据时调用
}
class BaseDataSource implements IDataSource {
private listeners: DataChangeListener[] = [];
private originDataArray: string[] = [];
public totalCount(): number {
return 0;
}
public getData(index: number): string {
return this.originDataArray[index];
}
// 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
console.info('add listener');
this.listeners.push(listener);
}
}
// 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
console.info('remove listener');
this.listeners.splice(pos, 1);
}
}
// 通知LazyForEach组件需要重载所有子组件
notifyDataReload(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded();
})
}
// 通知LazyForEach组件需要在index对应索引处添加子组件
notifyDataAdd(index: number): void {
this.listeners.forEach(listener => {
listener.onDataAdd(index);
})
}
// 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件
notifyDataChange(index: number): void {
this.listeners.forEach(listener => {
listener.onDataChange(index);
})
}
// 通知LazyForEach组件需要在index对应索引处删除该子组件
notifyDataDelete(index: number): void {
this.listeners.forEach(listener => {
listener.onDataDelete(index);
})
}
}
class MyDataSource extends BaseDataSource {
private dataArray: string[] = [];
public totalCount(): number {
return this.dataArray.length;
}
public getData(index: number): string {
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();
aboutToAppear() {
for (let i = 0; i <= 20; i++) {
this.data.pushData(`Hello ${i}`)
}
}
build() {
List({ space: 3 }) {
LazyForEach(this.data, (item: string,index:number) => {
ArticleSkeletonView({text:item}).margin({ top: 20 })
},(item: string) => item)
}.cachedCount(5)
}
}
@Builder
function textArea(width: number | Resource | string = '100%', height: number | Resource | string = '100%',text: string = '') {
Text(text)
Row()
.width(width)
.height(height)
.backgroundColor('#FFF2F3F4')
}
@Component
struct ArticleSkeletonView {
@State text:string ="Hello"
build() {
Row() {
Column() {
textArea(80, 80)
}
.margin({ right: 20 })
Column() {
textArea('60%', 20)
textArea('50%', 20)
}
.alignItems(HorizontalAlign.Start)
.justifyContent(FlexAlign.SpaceAround)
.height('100%')
}
.padding(20)
.borderRadius(12)
.backgroundColor('#FFECECEC')
.height(120)
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
}
键值生成规则是keyGenerator函数的返回值item。
到此:ArkTs基本语法学完了,请多看官网