002 學(xué)習(xí)ArkTs語言

一.ArkTs語言介紹

1.ArkTs語言概述

ArkTs是目前鴻蒙開發(fā)的主要語言,目前鴻蒙的主推模型Stage新版本(3.1.0(API 9))開發(fā)不支持Java和JavaScript了稼病,所以開發(fā)鴻蒙應(yīng)用學(xué)習(xí)ArkTs是很有必要的卓起。ArkTs是Ts(TypeScript)的擴(kuò)展赢织,后面的介紹就是介紹ArkTs的特性朝捆,Ts部分可以參考:https://www.tslang.cn/docs/handbook/typescript-in-5-minutes.html

2.ArkTs語言特性

當(dāng)前的ArkTs語言是在原先的Ts基礎(chǔ)上擴(kuò)展了聲明式UI特性憋他,目的就是簡化構(gòu)建和更新UI释牺,主要有以下特性:

基本UI描述:ArkTs定義了各種裝飾器、自定義組件微服、UI描述以及UI框架的內(nèi)置組件的事件方法和屬性方法等構(gòu)建成UI主體架構(gòu)趾疚。

狀態(tài)管理:ArkTs提供了多維度的狀態(tài)管理機(jī)制,在UI開發(fā)框架中以蕴,與界面綁定的數(shù)據(jù)不僅可以在同一組件中使用糙麦,還可以在不同組件中使用。比如:父子組件丛肮,爺孫組件赡磅,也可以在應(yīng)用全局范圍內(nèi)傳遞,甚至可以跨設(shè)備傳遞數(shù)據(jù)宝与。從數(shù)據(jù)流向來區(qū)分是可讀的單項數(shù)據(jù)傳遞和雙向變更數(shù)據(jù)傳遞焚廊。開發(fā)者可以靈活的利用現(xiàn)有的能力進(jìn)行數(shù)據(jù)與UI聯(lián)動。

動態(tài)UI構(gòu)建:ArkTs提供了動態(tài)UI構(gòu)建能力习劫,不僅可以自定義組件的內(nèi)部結(jié)構(gòu)咆瘟、還可以復(fù)用組件樣式、擴(kuò)展原生組件

渲染控制:ArkTs主要提供了條件渲染控制和循環(huán)渲染控制诽里,條件渲染主要就是根據(jù)條件渲染不同的UI內(nèi)容搞疗,循環(huán)渲染可以從數(shù)據(jù)源開始迭代獲取數(shù)據(jù),并在迭代過程創(chuàng)建UI組件

使用限制與擴(kuò)展:ArkTS在使用過程中存在限制與約束,同時也擴(kuò)展了雙向綁定等能力匿乃。

下面我們以一個具體的示例來說明ArkTS的基本組成桩皿。如下圖所示的代碼示例,UI界面包含一段文本和一個按鈕幢炸,當(dāng)開發(fā)者點擊按鈕時泄隔,文本內(nèi)容會從'Hello World'變?yōu)?'Hello ArkUI'。


這個示例中所包含的ArkTS聲明式開發(fā)范式的基本組成說明如下:

裝飾器: 用于裝飾類宛徊、結(jié)構(gòu)佛嬉、方法以及變量,賦予其特殊的含義闸天,如上述示例中@Entry暖呕、@Component和@State都是裝飾器。 具體而言苞氮,@Component表示這是個自定義組件湾揽;@Entry則表示這是個入口組件;@State表示這是組件中的狀態(tài)變量笼吟,這個變量變化會觸發(fā)UI刷新库物。

自定義組件:可復(fù)用的UI單元,可組合其他組件贷帮,如上述被@Component裝飾的struct Hello戚揭。

UI描述:聲明式的方法來描述UI的結(jié)構(gòu),例如build()方法中的代碼塊撵枢。

內(nèi)置組件:ArkTS中默認(rèn)內(nèi)置的基礎(chǔ)組件民晒、容器組件、媒體組件锄禽、繪制組件潜必、畫布組件等各種組件,開發(fā)者可以直接調(diào)用沟绪,如示例中的Column刮便、Text空猜、Divider绽慈、Button等。

屬性方法:用于組件屬性的配置辈毯,如fontSize()坝疼、width()、height()谆沃、color()等钝凶,可通過鏈?zhǔn)秸{(diào)用的方式設(shè)置多項屬性。

事件方法:用于添加組件對事件的響應(yīng)邏輯唁影,如跟隨在Button后面的onClick()耕陷,同樣可以通過鏈?zhǔn)秸{(diào)用的方式設(shè)置多個事件響應(yīng)邏輯掂名。

狀態(tài)修改:用于修改與UI綁定的數(shù)據(jù),通過@state修飾符修飾后當(dāng)狀態(tài)發(fā)生改變后對應(yīng)的UI界面也會發(fā)生改變哟沫。

二.基本UI描述

ArkTs通過裝飾器@Component與@Entry裝飾struct關(guān)鍵字生命的數(shù)據(jù)結(jié)構(gòu)饺蔑,構(gòu)造一個自定義組件。自定義組件中提供一個build函數(shù)嗜诀,開發(fā)者需在該函數(shù)內(nèi)以鏈?zhǔn)秸{(diào)用的方式進(jìn)行基本的UI描述猾警。

1.基本概念

struct:自定義組件可以基于struct實現(xiàn),不能用于繼承隆敢。

裝飾器:裝飾器給當(dāng)前被裝飾的對象賦予一定的能力发皿,其不僅可以裝飾結(jié)構(gòu)體或者類,還可以裝飾類的屬性拂蝎,多個裝飾器推薦多行方式編寫更加規(guī)范穴墅。

build函數(shù):自定義組件必須定義build函數(shù),并且禁止自定義構(gòu)造函數(shù)匣屡。build函數(shù)滿足Builder構(gòu)造器接口定義封救,用于定義組件的聲明式UI描述。

@Component:裝飾struct捣作,結(jié)構(gòu)體在裝飾后具有基于組件的能力誉结,需要實現(xiàn)build方法來創(chuàng)建UI。

@Entry: 裝飾struct券躁,組件被裝飾后作為頁面的入口惩坑,頁面加載時將被渲染顯示。

鏈?zhǔn)秸{(diào)用:以 "." 鏈?zhǔn)秸{(diào)用的方式配置UI組件的屬性方法也拜、事件方法等以舒。

2.UI描述規(guī)范

無參數(shù)構(gòu)造配置

如果組件的接口定義中不包含必選構(gòu)造參數(shù),組件后面的“()”中不需要配置任何內(nèi)容慢哈。例如蔓钟,Column和Divider組件不包含構(gòu)造參數(shù):

Column() {

??Text('Hello World')

????.fontSize(50)

????.fontWeight(FontWeight.Bold)

??Divider().width(200).height(10).color($r('app.color.button_next_background'))

??Text('Hello ArkTs')

????.fontSize(50)

????.fontWeight(FontWeight.Bold)

}

有參數(shù)構(gòu)造配置

如果組件的接口定義中包含構(gòu)造參數(shù),則在組件后面的“()”中可配置相應(yīng)參數(shù)卵贱,參數(shù)可以使用常量進(jìn)行賦值滥沫。

例如:

Image組件的必選參數(shù)src:

Image($r('app.media.icon'))

Text組件的參數(shù)content,該參數(shù)非必選键俱,即配置或不配置均可:

Text('test')

變量或表達(dá)式也可以用于參數(shù)賦值兰绣,其中表達(dá)式返回的結(jié)果類型必須滿足參數(shù)類型要求,變量的定義詳見頁面級變量的狀態(tài)管理與應(yīng)用級變量的狀態(tài)管理编振。例如缀辩,設(shè)置變量或表達(dá)式來構(gòu)造Image和Text組件的參數(shù):

Image(this.imagePath)

Image('https://'?+?this.imageUrl)

Text(`count: ${this.count}`)

屬性配置

使用屬性方法配置組件的屬性,屬性方法緊隨組件,并用"."運算符連接臀玄。

配置Text組件的字體大小屬性:

Text('test')

??.fontSize(12)

使用"."運算符進(jìn)行鏈?zhǔn)秸{(diào)用并同時配置組件的多個屬性瓢阴,如下所示:

Text("你好").fontSize(20).fontColor($r('app.color.button_next_background')).textAlign(TextAlign.End).width(200)

除了直接傳遞常量參數(shù)外,還可以傳遞變量或表達(dá)式健无,如下所示:

Text('hello')

??.fontSize(this.fontSize)

Image('test.jpg')

??.width(this.count %?2?===?0???100?:?200)???

??.height(this.offsetTest +?100)

對于系統(tǒng)內(nèi)置組件炫掐,框架還為其屬性預(yù)定義了一些枚舉類型供開發(fā)人員調(diào)用,枚舉類型可以作為參數(shù)傳遞睬涧,且必須滿足參數(shù)類型要求募胃。例如,可以按以下方式配置Text組件的顏色和字體屬性

Text('hello')

??.fontSize(20)

??.fontColor(Color.Red)

??.fontWeight(FontWeight.Bold)

事件配置

通過事件方法可以配置組件支持的事件畦浓,事件方法緊隨組件痹束,并用"."運算符連接。

使用lambda表達(dá)式配置組件的事件方法:

Button() {

??????????Text('Add')

????????????.fontSize(45)

????????????.fontColor($r('app.color.start_window_background'))

????????}

????????.onClick(() => {

??????????this.messageNum++

????????})

使用匿名函數(shù)表達(dá)式配置組件的事件方法讶请,要求使用bind(不使用bind點擊事件是無效的)祷嘶,以確保函數(shù)體中的this引用包含的組件:

Button() {

??????????Text('Add'+this.messageNum)

????????????.fontSize(45)

????????????.fontColor($r('app.color.start_window_background'))

????????}

????????.onClick(function (){

??????????this.messageNum++

????????}.bind(this))

使用組件的成員函數(shù)配置組件的事件方法:

Button() {

??????????Text('Add'?+?this.messageNum)

????????????.fontSize(45)

????????????.fontColor($r('app.color.start_window_background'))

????????}

????????.onClick(this.myClick.bind(this))

子組件配置

對于支持子組件配置的組件,例如容器組件夺溢,在"{ ... }"里為組件添加子組件的UI描述论巍。Column、Row风响、Stack嘉汰、Grid、List等組件都是容器組件状勤。

以下是簡單的Column示例:

Column() {

????????Button() {

??????????Text('Add'?+?this.messageNum)

????????????.fontSize(45)

????????????.fontColor($r('app.color.start_window_background'))

????????}

????????.onClick(this.myClick.bind(this))

????????Text("你好").fontSize(20).fontColor($r('app.color.button_next_background')).textAlign(TextAlign.End).width(200)

??????}

容器組件之間也可以互相嵌套鞋怀,實現(xiàn)相對復(fù)雜的多級嵌套效果:

Row() {

??Column() {

????this.TextBuild()

????Child({ count:?this.num })

????Child2({ count: $num })

????Button() {

??????Text('Back')

????????.fontSize(45)

????????.fontColor($r('app.color.start_window_background'))

????}

????.type(ButtonType.Capsule)

????.width('60%')

????.height('10%')

????.backgroundColor($r('app.color.button_next_background'))

????.onClick(() => {

??????router.back()

????})


????Button() {

??????Text(`Num++ ${this.num}`)

????????.fontSize(45)

????????.fontColor($r('app.color.start_window_background'))

????}

????.type(ButtonType.Capsule)

????.width('60%')

????.height('10%')

????.backgroundColor($r('app.color.button_next_background'))

????.margin({ top:?20?})

????.onClick(() => {

??????this.num++

????})

??}

??.width('100%')

}

.height('100%')

三.狀態(tài)管理

1.基本概念

ArkTS提供了多維度的狀態(tài)管理機(jī)制,在ArkUI開發(fā)框架中持搜,和UI相關(guān)聯(lián)的數(shù)據(jù)密似,不僅可以在組件內(nèi)使用,還可以在不同組件層級間傳遞葫盼,比如父子組件之間残腌、爺孫組件之間,也可以是應(yīng)用全局范圍內(nèi)的傳遞贫导。另外抛猫,從數(shù)據(jù)的傳遞形式來看,可分為只讀的單向傳遞和可變更的雙向傳遞脱盲。開發(fā)者可以靈活地利用這些能力來實現(xiàn)數(shù)據(jù)和UI的聯(lián)動邑滨。


2.頁面級狀態(tài)管理

頁面級狀態(tài)管理對比介紹

裝飾器裝飾內(nèi)容說明

@state基本數(shù)據(jù)類型日缨,類钱反,數(shù)組修改后的狀態(tài)數(shù)據(jù)被執(zhí)行時會執(zhí)行自定義組件中與此狀態(tài)變量相關(guān)的UI元素,更新UI界面

@Prop基本數(shù)據(jù)類型,類面哥,數(shù)組修改后的狀態(tài)數(shù)據(jù)用于父組件與子組件之間建立單項數(shù)據(jù)流的綁定關(guān)系哎壳,當(dāng)父組件中狀態(tài)數(shù)據(jù)發(fā)生變化時子組件與之相關(guān)的UI元素也會進(jìn)行頁面更新

@Link基本數(shù)據(jù)類型,類尚卫,數(shù)組父組件與子組件建立雙向的數(shù)據(jù)流綁定關(guān)系归榕,當(dāng)任意一方數(shù)據(jù)發(fā)生改變另一方與之相關(guān)的UI元素都會進(jìn)行頁面更新,將最新的數(shù)據(jù)狀態(tài)展示出來

@Observed類@Observed應(yīng)用于類吱涉,表示該類中的數(shù)據(jù)變更被UI頁面管理

@ObjectLink被@Observed所裝飾類的對象@ObjectLink裝飾的狀態(tài)數(shù)據(jù)被修改時刹泄,在父組件或者其他兄弟組件內(nèi)與它關(guān)聯(lián)的狀態(tài)數(shù)據(jù)所在的組件都會重新渲染。

@Provide基本數(shù)據(jù)類型怎爵,類特石,數(shù)組@Provide作為數(shù)據(jù)的提供方,可以更新其子孫節(jié)點的數(shù)據(jù)鳖链,并觸發(fā)頁面重新渲染姆蘸。

@Consume基本數(shù)據(jù)類型,類芙委,數(shù)組@Consume裝飾的變量在感知到@Provide裝飾的變量更新后逞敷,會觸發(fā)當(dāng)前自定義組件的重新渲染。

@State

@State裝飾的變量是組件內(nèi)部的狀態(tài)數(shù)據(jù)灌侣,當(dāng)這些狀態(tài)數(shù)據(jù)被修改時推捐,將會調(diào)用所在組件的build方法中的部分UI描述(使用該狀態(tài)變量的UI組件相關(guān)描述)進(jìn)行UI刷新。

@State狀態(tài)數(shù)據(jù)具有以下特征:

支持多種類型數(shù)據(jù):支持class侧啼、number玖姑、boolean、string強(qiáng)類型數(shù)據(jù)的值類型和引用類型慨菱,以及這些強(qiáng)類型構(gòu)成的數(shù)組焰络,即Array<class>、Array<string>符喝、Array<boolean>闪彼、Array<number>。不支持any协饲。

支持多實例:組件不同實例的內(nèi)部狀態(tài)數(shù)據(jù)獨立畏腕。

內(nèi)部私有:標(biāo)記為@State的屬性是私有變量,只能在組件內(nèi)訪問茉稠。

需要本地初始化:必須為所有@State變量分配初始值描馅,變量未初始化可能導(dǎo)致未定義的框架異常行為。

創(chuàng)建自定義組件時支持通過狀態(tài)變量名設(shè)置初始值:在創(chuàng)建組件實例時而线,可以通過變量名顯式指定@State狀態(tài)變量的初始值铭污。

現(xiàn)在我們來做一個示例恋日,主要邏輯是在自定義組件MyComponent設(shè)置兩個狀態(tài)值為state的值count與title,如下:

// xxx.ets

@Entry

@Component

struct EntryComponent {

??build() {

????Column() {

??????MyComponent({ count:?1, increaseBy:?2?})?// 第1個MyComponent實例

??????MyComponent({ title:?'Hello World 2', count:?7?})?// 第2個MyComponent實例

????}

??}

}


@Component

struct MyComponent {

??@State?count: number =?0

??@State?title: string =?'Hello World'

??private?increaseBy: number =?1


??build() {

????Column() {

??????Text(this.title)

??????Button('Click to change title')

????????.margin(20)

????????.onClick(() => {

??????????// 修改內(nèi)部狀態(tài)變量title

??????????this.title = (this.title !=?'Hello World') ??'Hello World'?:?'Hello ArkUI'

????????})


??????Button(`Click to increase count=${this.count}`)

????????.margin(20)

????????.onClick(() => {

??????????// 修改內(nèi)部狀態(tài)變量count

??????????this.count +=?this.increaseBy

????????})

????}

??}

}

上面的代碼放在編輯器中嘗試是可以看出點擊Click to change title事件MyComponent是進(jìn)行界面更新的并且兩個MyComponent是相互不干擾的嘹狞,同理點擊Click to increase count=也是如此

@Prop

@Prop與@State有相同的語義岂膳,但初始化方式不同。@Prop裝飾的變量必須使用其父組件提供的@State變量進(jìn)行初始化磅网,允許組件內(nèi)部修改@Prop變量谈截,但變量的更改不會通知給父組件,父組件變量的更改會同步到@prop裝飾的變量涧偷,即@Prop屬于單向數(shù)據(jù)綁定簸喂。

@Prop狀態(tài)數(shù)據(jù)具有以下特征:

支持多種類型數(shù)據(jù):支持class、number燎潮、boolean娘赴、string強(qiáng)類型數(shù)據(jù)的值類型和引用類型,以及這些強(qiáng)類型構(gòu)成的數(shù)組跟啤,即Array<class>诽表、Array<string>、Array<boolean>隅肥、Array<number>竿奏。不支持any。

私有:僅支持組件內(nèi)訪問腥放;

單個數(shù)據(jù)源:父組件中用于初始化子組件@Prop變量的必須是父組件定義的狀態(tài)變量泛啸;

單向通信:子組件對@Prop變量的更改將不會同步修改父組件中的@State變量,父組件中@State的變量修改將會同步給子組件中的@Prop變量秃症;

創(chuàng)建自定義組件時將按值傳遞方式給@Prop變量進(jìn)行初始化:在創(chuàng)建組件的新實例時候址,必須初始化所有@Prop變量,不支持在組件內(nèi)部進(jìn)行初始化种柑。

此處示例與下一個@Link裝飾符一起演示岗仑,可以看出明顯區(qū)別

@Link

@Link裝飾的變量可以和父組件的@State變量建立雙向數(shù)據(jù)綁定:

支持多種類型:@Link支持的數(shù)據(jù)類型與@State相同,即class聚请、number荠雕、string、boolean或這些類型的數(shù)組驶赏;

私有:僅支持組件內(nèi)訪問炸卑;

單個數(shù)據(jù)源:父組件中用于初始化子組件@Link變量的必須是父組件定義的狀態(tài)變量;

雙向通信:子組件對@Link變量的更改將同步修改父組件中的@State變量煤傍;

創(chuàng)建自定義組件時需要將變量的引用傳遞給@Link變量盖文,在創(chuàng)建組件的新實例時,必須使用命名參數(shù)初始化所有@Link變量蚯姆。@Link變量可以使用@State變量或@Link變量的引用進(jìn)行初始化五续,@State變量可以通過'$'操作符創(chuàng)建引用洒敏。

@Link變量不能在組件內(nèi)部進(jìn)行初始化。

下面的示例是演示Child組件中的count變量是@Prop修飾的返帕,Child2組件中的count變量是@Link修飾的,當(dāng)點擊Child組件的按鈕時會發(fā)現(xiàn)只有Child組件count數(shù)據(jù)更新并更新了界面篙挽,當(dāng)點擊Child2組件的按鈕時會發(fā)現(xiàn)所有的count數(shù)據(jù)都更新并且界面更新了荆萤,當(dāng)點擊父組件的按鈕也會發(fā)現(xiàn)所有的組件中count數(shù)據(jù)發(fā)生了更新并且界面發(fā)生更新了

import?router from?'@ohos.router';


@Entry

@Component

struct Second {

??@State?message: string =?'Hello World Second'

??@State?num: number =?1


??build() {

????Row() {

??????Column() {

????????this.TextBuild()

????????Child({ count:?this.num })

????????Child2({ count: $num })

????????Button() {

??????????Text('Back')

????????????.fontSize(45)

????????????.fontColor($r('app.color.start_window_background'))

????????}

????????.type(ButtonType.Capsule)

????????.width('60%')

????????.height('10%')

????????.backgroundColor($r('app.color.button_next_background'))

????????.onClick(() => {

??????????router.back()

????????})


????????Button() {

??????????Text(`Num++ ${this.num}`)

????????????.fontSize(45)

????????????.fontColor($r('app.color.start_window_background'))

????????}

????????.type(ButtonType.Capsule)

????????.width('60%')

????????.height('10%')

????????.backgroundColor($r('app.color.button_next_background'))

????????.margin({ top:?20?})

????????.onClick(() => {

??????????this.num++

????????})

??????}

??????.width('100%')

????}

????.height('100%')

??}


??@Builder?TextBuild(){

????Text(this.message)

??????.align(Alignment.Center)

??????.fontSize(40)

??????.fontWeight(FontWeight.Bold)

??}

}


@Component

struct Child {

??//父組件可以改變子組件,子組件不能改變父組件

??@Prop?count: number


??build() {

????Column() {

??????Text(`You have ${this.count} Nuggets left`).fontSize(18)

??????Button('count - costOfOneAttempt')

????????.margin(15)

????????.onClick(() => {

??????????this.count--

????????})

????}

??}

}


@Component

struct Child2 {

??//父組件與子組件雙向綁定

??@Link?count: number


??build() {

????Column() {

??????Text(`You have ${this.count} Nuggets left`).fontSize(18)

??????Button('count - costOfOneAttempt')

????????.margin(15)

????????.onClick(() => {

??????????this.count--

????????})

????}

??}

}

@Observed和ObjectLink數(shù)據(jù)管理

當(dāng)開發(fā)者需要在子組件中針對父組件的一個變量(parent_a)設(shè)置雙向同步時铣卡,開發(fā)者可以在父組件中使用@State裝飾變量(parent_a)链韭,并在子組件中使用@Link裝飾對應(yīng)的變量(child_a)。這樣不僅可以實現(xiàn)父組件與單個子組件之間的數(shù)據(jù)同步煮落,也可以實現(xiàn)父組件與多個子組件之間的數(shù)據(jù)同步敞峭。如下圖所示,可以看到蝉仇,父子組件針對ClassA類型的變量設(shè)置了雙向同步旋讹,那么當(dāng)子組件1中變量對應(yīng)的屬性c的值變化時,會通知父組件同步變化轿衔,而當(dāng)父組件中屬性c的值變化時沉迹,會通知所有子組件同步變化。而當(dāng)需要傳遞數(shù)組其中一個實例時害驹,使用@Link就不能滿足要求鞭呕。如果這些部分信息是一個類對象,就可以使用@ObjectLink配合@Observed來實現(xiàn)

設(shè)置要求:

@Observed用于類宛官,@ObjectLink用于變量葫松。

@ObjectLink裝飾的變量類型必須為類(class type)。

類要被@Observed裝飾器所裝飾底洗。

不支持簡單類型參數(shù)腋么,可以使用@Prop進(jìn)行單向同步。

@ObjectLink裝飾的變量是不可變的亥揖。

屬性的改動是被允許的党晋,當(dāng)改動發(fā)生時,如果同一個對象被多個@ObjectLink變量所引用徐块,那么所有擁有這些變量的自定義組件都會被通知進(jìn)行重新渲染未玻。

@ObjectLink裝飾的變量不可設(shè)置默認(rèn)值。

必須讓父組件中有一個由@State胡控、@Link扳剿、@StorageLink、@Provide或@Consume裝飾的變量所參與的TS表達(dá)式進(jìn)行初始化昼激。

@ObjectLink裝飾的變量是私有變量庇绽,只能在組件內(nèi)訪問锡搜。

示例與下面的修飾符一起演示

@Provide和@Consume

@Provide作為數(shù)據(jù)的提供方,可以更新其子孫節(jié)點的數(shù)據(jù)瞧掺,并觸發(fā)頁面渲染耕餐。@Consume在感知到@Provide數(shù)據(jù)的更新后,會觸發(fā)當(dāng)前自定義組件的重新渲染辟狈。

@Provide

裝飾器參數(shù)是一個string類型的常量肠缔,用于給裝飾的變量起別名。如果規(guī)定別名哼转,則提供對應(yīng)別名的數(shù)據(jù)更新明未。如果沒有,則使用變量名作為別名壹蔓。推薦使用@Provide('alias')這種形式趟妥。

同步機(jī)制:@Provide的變量類似@State,可以修改對應(yīng)變量進(jìn)行頁面重新渲染佣蓉。也可以修改@Consume裝飾的變量披摄,反向修改@State變量。

必須設(shè)置初始值勇凭。

觸發(fā)頁面渲染的修改:基礎(chǔ)類型(boolean行疏,string,number)變量的改變套像;@Observed class類型變量及其屬性的修改酿联;添加,刪除夺巩,更新數(shù)組中的元素贞让。

@Consume

不可設(shè)置默認(rèn)初始值。

示例如下:

import?router from?'@ohos.router';


@Entry

@Component

struct Third {

??//因為是數(shù)組所以需要用到@ObjectLink和@Observed

??@State?arrA: ClassA[] = [new?ClassA(2),?new?ClassA(0)]

??//此修飾符與@Consume組合可以讓其與孫子節(jié)點數(shù)據(jù)雙向綁定

??@Provide("reviewVote") reviewVotes: number =?0;


??build() {

????Row() {

??????Column() {

????????ForEach(this.arrA, (item) => {

??????????TextChild({ a: item })

????????}, (item) => item.id.toString())

????????ForEach(this.arrA, (item) => {

??????????Child({ a: item })

????????}, (item) => item.id.toString())

????????Button() {

??????????Text('Back')

????????????.fontSize(45)

????????????.fontColor($r('app.color.start_window_background'))

????????}

????????.type(ButtonType.Capsule)

????????.width('60%')

????????.height('10%')

????????.backgroundColor($r('app.color.button_next_background'))

????????.onClick(() => {

??????????router.back()

????????})


????????Button() {

??????????Text('Add')

????????????.fontSize(45)

????????????.fontColor($r('app.color.start_window_background'))

????????}

????????.type(ButtonType.Capsule)

????????.width('60%')

????????.height('5%')

????????.backgroundColor($r('app.color.button_next_background'))

????????.margin({ top:?20?})

????????.onClick(() => {

??????????this.arrA[0].c++

????????})

????????Button() {

??????????Text('AddChildChild'+this.reviewVotes)

????????????.fontSize(25)

????????????.fontColor($r('app.color.start_window_background'))

????????}

????????.type(ButtonType.Capsule)

????????.width('60%')

????????.height('10%')

????????.backgroundColor($r('app.color.button_next_background'))

????????.margin({ top:?20?})

????????.onClick(() => {

??????????this.reviewVotes++

????????})

??????}

??????.width('100%')

????}

????.height('100%')

??}

}


@Component

struct TextChild {

??@ObjectLink?a: ClassA


??build() {

????Column() {

??????Text(this.a.c +?"TextChild")

????????.align(Alignment.Center)

????????.fontSize(40)

????????.fontWeight(FontWeight.Bold)

??????TextChildChild()

????}

??}

}


@Component

struct TextChildChild {

??//此修飾符與爺爺組件的@Provide組合可以與爺爺組件雙向綁定

??@Consume("reviewVote") reviewVotes: number


??build() {

????Column() {

??????Button() {

????????Text('RemoveChildChild'+this.reviewVotes)

??????????.fontSize(20)

??????????.fontColor($r('app.color.start_window_background'))

??????}

??????.type(ButtonType.Capsule)

??????.width('60%')

??????.height('5%')

??????.backgroundColor($r('app.color.button_next_background'))

??????.margin({ top:?20?})

??????.onClick(() => {

????????this.reviewVotes--

??????})

??????Text(this.reviewVotes +?"TextChildChild")

????????.align(Alignment.Center)

????????.fontSize(40)

????????.fontWeight(FontWeight.Bold)

????}

??}

}


@Component

struct Child {

??@ObjectLink?a: ClassA


??build() {

????Column() {

??????Text(this.a.c +?"Child")

????????.align(Alignment.Center)

????????.fontSize(40)

????????.fontWeight(FontWeight.Bold)

??????Button('count - costOfOneAttempt')

????????.margin(15)

????????.onClick(() => {

??????????this.a.c--

????????})

????}

??}

}


var nextID: number =?0


@Observed

class?ClassA {

??public?name: string

??public?c: number

??public?id: number


??constructor(c: number, name: string =?'OK') {

????this.name = name

????this.c = c

????this.id = nextID++

??}

}

@Watch

@Watch用于監(jiān)聽狀態(tài)變量的變化柳譬,語法結(jié)構(gòu)為:

@State?@Watch("onChanged") count :?number?=?0

如上所示喳张,給狀態(tài)變量增加一個@Watch裝飾器,通過@Watch注冊一個回調(diào)方法onChanged美澳, 當(dāng)狀態(tài)變量count被改變時销部, 觸發(fā)onChanged回調(diào)。

裝飾器@State制跟、@Prop舅桩、@Link、@ObjectLink雨膨、@Provide擂涛、@Consume、@StorageProp以及@StorageLink所裝飾的變量均可以通過@Watch監(jiān)聽其變化聊记。

import?router from?'@ohos.router';


// xxx.ets

@Entry

@Component

struct CompA {

??@State?@Watch('onBasketUpdated') shopBasket: Array<number> = [7,?12,?47,?3]

??@State?totalPurchase: number =?0

??@State?addPurchase: number =?0


??aboutToAppear() {

????this.updateTotal()

??}


??updateTotal():?void?{

????let sum =?0;

????this.shopBasket.forEach((i) => {

??????sum += i

????})

????// 計算新的購物籃總價值撒妈,如果超過100恢暖,則適用折扣

????this.totalPurchase = (sum <?100) ? sum :?0.9?* sum

??}


??// shopBasket更改時觸發(fā)該方法

??onBasketUpdated(propName: string):?void?{

????this.updateTotal()

??}


??build() {

????Column() {

??????Button('add to basket '?+?this.addPurchase)

????????.margin(15)

????????.onClick(() => {

??????????this.addPurchase = Math.round(100?* Math.random())

??????????this.shopBasket.push(this.addPurchase)

????????})

??????Text(`${this.totalPurchase}`)

????????.fontSize(30)

??????Button() {

????????Text('Back')

??????????.fontSize(45)

??????????.fontColor($r('app.color.start_window_background'))

??????}

??????.type(ButtonType.Capsule)

??????.width('60%')

??????.height('10%')

??????.backgroundColor($r('app.color.button_next_background'))

??????.onClick(() => {

????????router.back()

??????})

????}

??}

}

3.應(yīng)用級變量的狀態(tài)管理

AppStorage

AppStorage是應(yīng)用程序中的單例對象,由UI框架在應(yīng)用程序啟動時創(chuàng)建狰右,在應(yīng)用程序退出時銷毀杰捂,為應(yīng)用程序范圍內(nèi)的可變狀態(tài)屬性提供中央存儲。

AppStorage包含整個應(yīng)用程序中需要訪問的所有狀態(tài)屬性棋蚌,只要應(yīng)用程序保持運行嫁佳,AppStorage就會保存所有屬性及屬性值,屬性值可以通過唯一的鍵值進(jìn)行訪問附鸽。

組件可以通過裝飾器將應(yīng)用程序狀態(tài)數(shù)據(jù)與AppStorage進(jìn)行同步脱拼,應(yīng)用業(yè)務(wù)邏輯的實現(xiàn)也可以通過接口訪問AppStorage瞒瘸。

AppStorage的選擇狀態(tài)屬性可以與不同的數(shù)據(jù)源或數(shù)據(jù)接收器同步坷备,這些數(shù)據(jù)源和接收器可以是設(shè)備上的本地或遠(yuǎn)程,并具有不同的功能情臭,如數(shù)據(jù)持久性省撑。這樣的數(shù)據(jù)源和接收器可以獨立于UI在業(yè)務(wù)邏輯中實現(xiàn)。

默認(rèn)情況下俯在,AppStorage中的屬性是可變的竟秫,AppStorage還可使用不可變(只讀)屬性。

@StorageLink裝飾器

組件通過使用@StorageLink(key)裝飾的狀態(tài)變量跷乐,與AppStorage建立雙向數(shù)據(jù)綁定肥败,key為AppStorage中的屬性鍵值。當(dāng)創(chuàng)建包含@StorageLink的狀態(tài)變量的組件時愕提,該狀態(tài)變量的值將使用AppStorage中的值進(jìn)行初始化馒稍。在UI組件中對@StorageLink的狀態(tài)變量所做的更改將同步到AppStorage,并從AppStorage同步到任何其他綁定實例中,如PersistentStorage或其他綁定的UI組件。

@StorageProp裝飾器

組件通過使用@StorageProp(key)裝飾的狀態(tài)變量含滴,與AppStorage建立單向數(shù)據(jù)綁定乞而,key標(biāo)識AppStorage中的屬性鍵值。當(dāng)創(chuàng)建包含@StorageProp的狀態(tài)變量的組件時键耕,該狀態(tài)變量的值將使用AppStorage中的值進(jìn)行初始化。AppStorage中屬性值的更改會導(dǎo)致綁定該狀態(tài)變量的UI組件進(jìn)行狀態(tài)更新。

示例

每次用戶單擊Count按鈕時澳化,this.varA變量值都會增加1,此變量與AppStorageA中的varA同步稳吮。每次用戶單擊language按鈕時肆捕,修改AppStorageA中的languageCode,此修改會同步給this.languageCode變量盖高。點擊跳轉(zhuǎn)到AppStorageB慎陵,會發(fā)現(xiàn)varA的值和languageCode的值是傳遞過去了眼虱。

import?router from?'@ohos.router';


// xxx.ets

@Entry

@Component

struct AppStorageA {

??@StorageLink('varA') varA: number =?2

??@StorageProp('languageCode') languageCode: string =?'en'

??@State?private?label: string =?'count'


??build() {

????Column() {

??????Row({ space:?20?}) {

????????Button(`${this.label}: ${this.varA}`)

??????????.onClick(() => {

????????????AppStorage.Set<number>('varA', AppStorage.Get<number>('varA') +?1)

??????????})

????????Button(`language: ${this.languageCode}`)

??????????.onClick(() => {

????????????if?(AppStorage.Get<string>('languageCode') ===?'zh') {

??????????????AppStorage.Set<string>('languageCode',?'en')

????????????}?else?{

??????????????AppStorage.Set<string>('languageCode',?'zh')

????????????}

????????????this.label = (this.languageCode ===?'zh') ??'數(shù)量'?:?'Count'

??????????})

??????}

??????.margin({ bottom:?50?})


??????Row() {

????????Button(`更改@StorageLink修飾的變量:${this.varA}`).height(40).fontSize(14)

??????????.onClick(() => {

????????????this.varA++

??????????})

??????}.margin({ bottom:?50?})


??????Row() {

????????Button(`更改@StorageProp修飾的變量:${this.languageCode}`).height(40).fontSize(14)

??????????.onClick(() => {

????????????if?(this.languageCode ===?'zh') {

??????????????this.languageCode =?'en'

????????????}?else?{

??????????????this.languageCode =?'zh'

????????????}

??????????})

??????}.margin({bottom:100})

??????Row() {

????????Button(`跳轉(zhuǎn)到AppStorageB`).height(40).fontSize(14)

??????????.onClick(() => {

????????????router.pushUrl({url:'pages/AppStorageB'})

??????????})

??????}

????}

??}

}

@Entry

@Component

struct AppStorageB {

??@StorageLink('varA') varA: number =?2

??@StorageProp('languageCode') languageCode: string =?'en'

??@State?private?label: string =?'count'


??build() {

????Column() {

??????Text(`language: ${this.languageCode}`).margin({top:100})

??????Text(`varA: ${this.varA}`).margin({top:100})

????}.height('100%').width('100%')

??}

}

LocalStorage

LocalStorage是應(yīng)用程序中的存儲單元,生命周期跟隨其關(guān)聯(lián)的Ability席纽。在Stage模型下捏悬,LocalStorage解決AppStorage共享范圍過大的問題,提供Ability之間全局?jǐn)?shù)據(jù)的隔離润梯。同時过牙,LocalStorage為應(yīng)用程序范圍內(nèi)的可變狀態(tài)屬性和非可變狀態(tài)屬性提供存儲,可變狀態(tài)屬性和非可變狀態(tài)屬性是構(gòu)建應(yīng)用程序UI的一部分纺铭,如一個Ability的UI寇钉。解決App與Ability之間數(shù)據(jù)互相干擾問題,多實例場景下同一個Ability類的不同Ability實例之間的數(shù)據(jù)互相干擾問題舶赔。在分布式遷移的場景下扫倡,Ability是系統(tǒng)調(diào)度的最小單元,配合LocalStorage更方便實現(xiàn)組件的數(shù)據(jù)遷移竟纳。

應(yīng)用層:一個應(yīng)用程序可以創(chuàng)建多個LocalStorage實例撵溃,應(yīng)用程序的每一個Ability對應(yīng)一個LocalStorage實例。

Ability:一個應(yīng)用程序可以擁有多個Ability锥累,一個Ability中的所有子組件最多可以分配一個LocalStorage實例缘挑。并且,Ability中的所有子組件都將繼承對此LocalStorage實例存儲對象的訪問權(quán)桶略。

一個組件最多可以訪問一個LocalStorage實例语淘,一個LocalStorage對象可以分配給多個組件。

@LocalStorageLink裝飾器

組件通過使用@LocalStorageLink(key)裝飾的狀態(tài)變量际歼,key值為LocalStorage中的屬性鍵值惶翻,與LocalStorage建立雙向數(shù)據(jù)綁定。當(dāng)創(chuàng)建包含@LocalStorageLink的狀態(tài)變量的組件時蹬挺,該狀態(tài)變量的值將會使用LocalStorage中的值進(jìn)行初始化维贺。如果LocalStorage中未定義初始值,將使用@LocalStorageLink定義的初始值巴帮。在UI組件中對@LocalStorageLink的狀態(tài)變量所做的更改將同步到LocalStorage中溯泣,并從LocalStorage同步到Ability下的組件中。

@LocalStorageProp裝飾器

組件通過使用LocalStorageProp(key)裝飾的狀態(tài)變量榕茧,key值為LocalStorage中的屬性鍵值垃沦,與LocalStorage建立單向數(shù)據(jù)綁定。當(dāng)創(chuàng)建包含@LocalStorageProp的狀態(tài)變量的組件時用押,該狀態(tài)變量的值將使用LocalStorage中的值進(jìn)行初始化肢簿。LocalStorage中的屬性值的更改會導(dǎo)致當(dāng)前Ability下的所有UI組件進(jìn)行狀態(tài)更新。

具體用法參考:

import?UIAbility from?'@ohos.app.ability.UIAbility';

import?hilog from?'@ohos.hilog';

import?window from?'@ohos.window';


export?default?class?EntryAbility?extends?UIAbility {

????storage: LocalStorage


????onCreate(want, launchParam) {

????????this.storage =?new?LocalStorage()

????????this.storage.setOrCreate('storageSimpleProp',?8)

????????hilog.info(0x0000,?'testTag',?'%{public}s',?'Ability onCreate');

????}


????onDestroy() {

????????hilog.info(0x0000,?'testTag',?'%{public}s',?'Ability onDestroy');

????}


????onWindowStageCreate(windowStage: window.WindowStage) {

????????// Main window is created, set main page for this ability

????????hilog.info(0x0000,?'testTag',?'%{public}s',?'Ability onWindowStageCreate');

????????windowStage.loadContent('pages/LocalStorageComponent',?this.storage)

????}

}

import?router from?'@ohos.router';


// xxx.ets

// Index.ets

let storage = LocalStorage.GetShared()


@Entry(storage)

@Component

struct LocalStorageComponent {

??@LocalStorageLink('storageSimpleProp') simpleVarName: number =?0


??build() {

????Column() {

??????Button(`LocalStorageLink: ${this.simpleVarName.toString()}`)

????????.margin(20)

????????.onClick(() => {

??????????this.simpleVarName +=?1

????????})

??????Text(JSON.stringify(this.simpleVarName))

????????.fontSize(50)

??????LocalStorageComponentProp()

????}.width('100%')

??}

}


@Component

struct LocalStorageComponentProp {

??@LocalStorageProp('storageSimpleProp') simpleVarName: number =?0


??build() {

????Column() {

??????Button(`LocalStorageProp: ${this.simpleVarName.toString()}`)

????????.margin(20)

????????.onClick(() => {

??????????this.simpleVarName +=?1

????????})

??????Text(JSON.stringify(this.simpleVarName))

????????.fontSize(50)

????}.width('100%')

??}

}

PersistentStorage

PersistentStorage提供了一些靜態(tài)方法用來管理應(yīng)用持久化數(shù)據(jù),可以將特定標(biāo)記的持久化數(shù)據(jù)鏈接到AppStorage中池充,并由AppStorage接口訪問對應(yīng)持久化數(shù)據(jù)桩引,或者通過@StorageLink裝飾器來訪問對應(yīng)key的變量。

// xxx.ets

PersistentStorage.PersistProp('highScore',?10)


@Entry

@Component

struct PersistentComponent {

??@StorageLink('highScore') highScore: number =?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

??????????}

????????})

????}.width('100%')

??}

}

Environment

Environment是框架在應(yīng)用程序啟動時創(chuàng)建的單例對象收夸,它為AppStorage提供了一系列應(yīng)用程序需要的環(huán)境狀態(tài)數(shù)據(jù)坑匠,這些數(shù)據(jù)描述了應(yīng)用程序運行的設(shè)備環(huán)境,包括系統(tǒng)語言卧惜、深淺色模式等等厘灼。Environment及其屬性是不可變的,所有數(shù)據(jù)類型均為簡單類型咽瓷。如下示例展示了從Environment獲取系統(tǒng)是否開啟無障礙屏幕朗讀:

Environment.EnvProp('accessibilityEnabled',?'default')

var enable = AppStorage.Get('accessibilityEnabled')

accessibilityEnabled是Environment提供的系統(tǒng)默認(rèn)變量識別符设凹。首先需要將對應(yīng)系統(tǒng)屬性綁定到AppStorage上,再通過AppStorage中的方法或者裝飾器訪問對應(yīng)的系統(tǒng)屬性數(shù)據(jù)茅姜。

四.動態(tài)構(gòu)建UI元素

基本UI描述介紹的是如何創(chuàng)建一個內(nèi)部UI結(jié)構(gòu)固定的自定義組件闪朱,為了滿足開發(fā)者自定義組件內(nèi)部UI結(jié)構(gòu)的需求,ArkTS同時提供了動態(tài)構(gòu)建UI元素的能力匈睁。

@Builder

可通過@Builder裝飾器進(jìn)行描述监透,該裝飾器可以修飾一個函數(shù)桶错,此函數(shù)可以在build函數(shù)之外聲明航唆,并在build函數(shù)中或其他@Builder修飾的函數(shù)中使用,從而實現(xiàn)在一個自定義組件內(nèi)快速生成多個布局內(nèi)容院刁。使用方式如下面示例所示糯钙。

@Entry

@Component

struct Second {

??@State?message: string =?'Hello World Second'

??@State?num: number =?1


??build() {

????Row() {

??????Column() {

????????this.TextBuild()

??????}

??????.width('100%')

????}

????.height('100%')

??}


??@Builder?TextBuild(){

????Text(this.message)

??????.align(Alignment.Center)

??????.fontSize(40)

??????.fontWeight(FontWeight.Bold)

??}

}

@BuilderParam8+

@BuilderParam裝飾器用于修飾自定義組件內(nèi)函數(shù)類型的屬性(例如:@BuilderParam noParam: () => void),并且在初始化自定義組件時被@BuilderParam修飾的屬性必須賦值退腥。

引入動機(jī)

當(dāng)開發(fā)者創(chuàng)建自定義組件任岸,并想對該組件添加特定功能時(例如在自定義組件中添加一個點擊跳轉(zhuǎn)操作)。若直接在組件內(nèi)嵌入事件方法狡刘,將會導(dǎo)致所有引入該自定義組件的地方均增加了該功能享潜。為解決此問題,引入了@BuilderParam裝飾器嗅蔬,此裝飾器修飾的屬性值可為@Builder裝飾的函數(shù)剑按,開發(fā)者可在初始化自定義組件時對此屬性進(jìn)行賦值,為自定義組件增加特定的功能澜术。

參數(shù)初始化組件

通過參數(shù)初始化組件時艺蝴,將@Builder裝飾的函數(shù)賦值給@BuilderParam修飾的屬性,并在自定義組件內(nèi)調(diào)用該屬性值鸟废。若@BuilderParam修飾的屬性在進(jìn)行賦值時不帶參數(shù)(如:noParam: this.specificNoParam)猜敢,則此屬性的類型需定義為無返回值的函數(shù)(如:@BuilderParam noParam: () => void);若帶參數(shù)(如:withParam: this.SpecificWithParam('WithParamA')),則此屬性的類型需定義成any(如:@BuilderParam withParam: any)缩擂。

// xxx.ets

@Component

struct CustomContainer {

??header: string =?''

??@BuilderParam?noParam: () =>?void

??@BuilderParam?withParam: any

??footer: string =?''


??build() {

????Column() {

??????Text(this.header)

????????.fontSize(30)

??????this.noParam()

??????this.withParam()

??????Text(this.footer)

????????.fontSize(30)

????}

??}

}


@Entry

@Component

struct CustomContainerUser {

??@Builder?specificNoParam() {

????Column() {

??????Text('noParam').fontSize(30)

????}

??}


??@Builder?SpecificWithParam(label: string) {

????Column() {

??????Text(label).fontSize(30)

????}

??}


??build() {

????Column() {

??????CustomContainer({

????????header:?'HeaderA',

????????noParam:?this.specificNoParam,

????????withParam:?this.SpecificWithParam('WithParamA'),

????????footer:?'FooterA'

??????})

??????Divider().color(Color.Black)

????????.strokeWidth(3)

????????.margin(10)

??????CustomContainer({

????????header:?'HeaderB',

????????noParam:?this.specificNoParam,

????????withParam:?this.SpecificWithParam('WithParamB'),

????????footer:?'FooterB'

??????})

????}

??}

}

@Styles

ArkTS為了避免開發(fā)者對重復(fù)樣式的設(shè)置鼠冕,通過@Styles裝飾器可以將多個樣式設(shè)置提煉成一個方法,直接在組件聲明時調(diào)用胯盯,通過@Styles裝飾器可以快速定義并復(fù)用自定義樣式供鸠。當(dāng)前@Styles僅支持通用屬性。

@Styles可以定義在組件內(nèi)或組件外陨闹,在組件外定義時需在方法名前面添加function關(guān)鍵字楞捂,組件內(nèi)定義時則不需要添加function關(guān)鍵字。

// xxx.ets

@Styles?function globalFancy () {

??.width(150)

??.height(100)

??.backgroundColor(Color.Pink)

}


@Entry

@Component

struct FancyUse {

??@Styles?componentFancy() {

????.width(100)

????.height(200)

????.backgroundColor(Color.Yellow)

??}


??build() {

????Column({ space:?10?}) {

??????Text('FancyA')

????????.globalFancy()

????????.fontSize(30)

??????Text('FancyB')

????????.globalFancy()

????????.fontSize(20)

??????Text('FancyC')

????????.componentFancy()

????????.fontSize(30)

??????Text('FancyD')

????????.componentFancy()

????????.fontSize(20)

????}

??}

}

@Styles還可以在StateStyles屬性內(nèi)部使用趋厉,在組件處于不同的狀態(tài)時賦予相應(yīng)的屬性寨闹。

在StateStyles內(nèi)可以直接調(diào)用組件外定義的@Styles方法,但需要通過this關(guān)鍵字調(diào)用組件內(nèi)定義的@Styles方法君账。

// xxx.ets

@Styles?function globalFancy () {

??.width(120)

??.height(120)

??.backgroundColor(Color.Green)

}


@Entry

@Component

struct FancyUse {

??@Styles?componentFancy() {

????.width(80)

????.height(80)

????.backgroundColor(Color.Red)

??}


??build() {

????Row({ space:?10?}) {

??????Button('Fancy')

????????.stateStyles({

??????????normal: {

????????????.width(100)

????????????.height(100)

????????????.backgroundColor(Color.Blue)

??????????},

??????????disabled:?this.componentFancy,

??????????pressed: globalFancy

????????})

????}

??}

}

@Extend

@Extend裝飾器將新的屬性方法添加到Text繁堡、Column、Button等內(nèi)置組件上乡数,通過@Extend裝飾器可以快速地擴(kuò)展原生組件椭蹄。@Extend不能定義在自定義組件struct內(nèi)。

// xxx.ets

@Extend(Text) function fancy (fontSize: number) {

??.fontColor(Color.Red)

??.fontSize(fontSize)

??.fontStyle(FontStyle.Italic)

??.fontWeight(600)

}


@Entry

@Component

struct FancyUse {

??build() {

????Row({ space:?10?}) {

??????Text("Fancy")

????????.fancy(16)

??????Text("Fancy")

????????.fancy(24)

??????Text("Fancy")

????????.fancy(32)

????}

??}

}

@CustomDialog

@CustomDialog裝飾器用于裝飾自定義彈窗組件净赴,使得彈窗可以動態(tài)設(shè)置內(nèi)容及樣式绳矩。

import?router from?'@ohos.router';


// xxx.ets

@CustomDialog

struct DialogExample {

??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.info('onAccept');

??}


??existApp() {

????console.info('Cancel dialog!');

??}


??build() {

????Column() {

??????Button('Click to open Dialog')

????????.onClick(() => {

??????????this.dialogController.open()

????????})

????}

??}

}

五.渲染控制

ArkTS也提供了渲染控制的能力。條件渲染可根據(jù)應(yīng)用的不同狀態(tài)玖翅,渲染對應(yīng)狀態(tài)下的UI內(nèi)容翼馆。循環(huán)渲染可從數(shù)據(jù)源中迭代獲取數(shù)據(jù),并在每次迭代過程中創(chuàng)建相應(yīng)的組件金度。

條件渲染

使用if/else進(jìn)行條件渲染应媚。

f/else語句的每個分支都包含一個構(gòu)建UI的描述,此類描述中必須創(chuàng)建一個或多個子組件猜极。在初始渲染時中姜,if語句會執(zhí)行其中的一個分支并將生成的子組件添加到if/else的父組件中。

每當(dāng)if或else if條件語句中使用的狀態(tài)變量發(fā)生變化時跟伏,條件渲染都會更新并重新進(jìn)行條件評估丢胚,如果條件分支發(fā)生了變化,這意味著需要構(gòu)建另一個條件分支酬姆,此時框架將:

刪除所有以前渲染的(早期分支的)組件嗜桌。

執(zhí)行分支的UI描述,將生成的子組件添加到其父組件中辞色。

在以下示例中骨宠,如果count從0增加到1浮定,那么條件渲染會進(jìn)行更新并計算各個條件分支值:

條件(count > 0)計算結(jié)果為false,未發(fā)生變化层亿,不執(zhí)行額外操作桦卒;

條件(this.count % 2 === 0)從true更改為false,該分支內(nèi)的Text組件將從Column組件中移除并銷毀匿又;

else分支為true方灾,執(zhí)行該條件下的UI描述,創(chuàng)建一個Text組件碌更,并將它放在Column組件內(nèi)裕偿。

@Entry

@Component

struct MyComponent {

??@State?count: number =?300

??build() {

????Column() {

??????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)

??????}

????}

??}

}

循環(huán)渲染

通過循環(huán)渲染(ForEach)從數(shù)組中獲取數(shù)據(jù),并為每個數(shù)據(jù)項創(chuàng)建相應(yīng)的組件痛单,可減少代碼復(fù)雜度嘿棘。

ForEach(

??arr: any[],

??itemGenerator: (item: any, index?: number) =>?void,

??keyGenerator?: (item: any, index?: number) => string

)

參數(shù):

參數(shù)名參數(shù)類型必填參數(shù)描述

arrany[]是必須是數(shù)組,允許設(shè)置為空數(shù)組旭绒,空數(shù)組場景下將不會創(chuàng)建子組件鸟妙。同時允許設(shè)置返回值為數(shù)組類型的函數(shù),例如arr.slice(1, 3)挥吵,設(shè)置的函數(shù)不得改變包括數(shù)組本身在內(nèi)的任何狀態(tài)變量重父,如Array.splice、Array.sort或Array.reverse這些改變原數(shù)組的函數(shù)忽匈。

itemGenerator(item: any, index?: number) => void是生成子組件的lambda函數(shù)房午,為數(shù)組中的每一個數(shù)據(jù)項創(chuàng)建一個或多個子組件,單個子組件或子組件列表必須包括在大括號“{...}”中脉幢。

keyGenerator(item: any, index?: number) => string否匿名函數(shù)歪沃,用于給數(shù)組中的每一個數(shù)據(jù)項生成唯一且固定的鍵值嗦锐。當(dāng)數(shù)據(jù)項在數(shù)組中的位置更改時嫌松,其鍵值不得更改,當(dāng)數(shù)組中的數(shù)據(jù)項被新項替換時奕污,被替換項的鍵值和新項的鍵值必須不同萎羔。鍵值生成器的功能是可選的,不提供時框架默認(rèn)使用index+JSON.stringify(item)的方式生成碳默。

為了使開發(fā)框架能夠更好地識別數(shù)組更改贾陷,提高性能,建議開發(fā)者提供自定義的鍵值生成器嘱根。如將數(shù)組反向時髓废,如果沒有提供鍵值生成器,則ForEach中的所有節(jié)點都將重建该抒。

示例:

@Entry

@Component

struct MyComponent {

??@State?arr: number[] = [10,?20,?30]


??build() {

????Column({ space:?5?}) {

??????Button('Reverse Array')

????????.onClick(() => {

??????????this.arr.reverse()

????????})


??????ForEach(this.arr, (item: number) => {

????????Text(`item value: ${item}`).fontSize(18)

????????Divider().strokeWidth(2).color(Color.Black)

??????}, (item: number) => item.toString())

????}

??}

}

數(shù)據(jù)懶加載

通過數(shù)據(jù)懶加載(LazyForEach)從提供的數(shù)據(jù)源中按需迭代數(shù)據(jù)慌洪,并在每次迭代過程中創(chuàng)建相應(yīng)的組件。

LazyForEach(

??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;

}


interface?DataChangeListener {

??onDataReloaded():?void;?????????????????????

??onDataAdd(index: number):?void;???????????

??onDataMove(from: number, to: number):?void;

??onDataDelete(index: number):?void;????????

??onDataChange(index: number):?void;?????????

}

參數(shù):

參數(shù)名參數(shù)類型必填參數(shù)描述

dataSourceIDataSource是實現(xiàn)IDataSource接口的對象,需要開發(fā)者實現(xiàn)相關(guān)接口冈爹。

itemGenerator(item: any, index?: number) => void是生成子組件的lambda函數(shù)涌攻,為數(shù)組中的每一個數(shù)據(jù)項創(chuàng)建一個或多個子組件,單個子組件或子組件列表必須包括在大括號“{...}”中频伤。

keyGenerator(item: any, index?: number) => string否匿名函數(shù)恳谎,用于給數(shù)組中的每一個數(shù)據(jù)項生成唯一且固定的鍵值。當(dāng)數(shù)據(jù)項在數(shù)組中的位置更改時憋肖,其鍵值不得更改因痛,當(dāng)數(shù)組中的數(shù)據(jù)項被新項替換時,被替換項的鍵值和新項的鍵值必須不同岸更。鍵值生成器的功能是可選的婚肆,不提供時框架默認(rèn)使用index+JSON.stringify(item)的方式生成。

為了使開發(fā)框架能夠更好地識別數(shù)組更改坐慰,提高性能较性,建議提供自定義的鍵值生成器。如將數(shù)組反向時结胀,如果沒有提供鍵值生成器赞咙,則LazyForEach中的所有節(jié)點都將重建。

IDataSource類型說明

名稱描述

totalCount(): number獲取數(shù)據(jù)總數(shù)糟港。

getData(index: number): any獲取索引值index對應(yīng)的數(shù)據(jù)攀操。

registerDataChangeListener(listener:DataChangeListener): void注冊數(shù)據(jù)改變的監(jiān)聽器。

unregisterDataChangeListener(listener:DataChangeListener): void注銷數(shù)據(jù)改變的監(jiān)聽器秸抚。

DataChangeListener類型說明

名稱描述

onDataReloaded(): void重新加載所有數(shù)據(jù)速和。

onDataAdded(index: number): voiddeprecated通知組件index的位置有數(shù)據(jù)添加。從API Version 8開始廢棄剥汤,建議使用onDataAdd颠放。

onDataMoved(from: number, to: number): voiddeprecated通知組件數(shù)據(jù)從from的位置移到to的位置。從API Version 8開始廢棄吭敢,建議使用onDataMove碰凶。

onDataDeleted(index: number): voiddeprecated通知組件index的位置有數(shù)據(jù)刪除。從API Version 8開始廢棄鹿驼,建議使用onDataDelete欲低。

onDataChanged(index: number): voiddeprecated通知組件index的位置有數(shù)據(jù)變化。 從API Version 8開始廢棄畜晰,建議使用onDataChange砾莱。

onDataAdd(index: number): void8+通知組件index的位置有數(shù)據(jù)添加。

onDataMove(from: number, to: number): void8+通知組件數(shù)據(jù)從from的位置移到to的位置凄鼻。

onDataDelete(index: number): void8+通知組件index的位置有數(shù)據(jù)刪除腊瑟。

onDataChange(index: number): void8+通知組件index的位置有數(shù)據(jù)變化面哼。

示例:

// xxx.ets

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('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)

????}

??}


??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 {

??// 初始化數(shù)據(jù)列表

??private?dataArray: string[] = ['/path/image0.png',?'/path/image1.png',?'/path/image2.png',?'/path/image3.png']


??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 {

??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(() => {

??????????// 每點擊一次列表項,數(shù)據(jù)增加一項

??????????this.data.pushData('/path/image'?+?this.data.totalCount() +?'.png')

????????})

??????}, item => item)

????}

??}

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末扫步,一起剝皮案震驚了整個濱河市魔策,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌河胎,老刑警劉巖闯袒,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異游岳,居然都是意外死亡政敢,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門胚迫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來喷户,“玉大人,你說我怎么就攤上這事访锻⊥食ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵期犬,是天一觀的道長河哑。 經(jīng)常有香客問我,道長龟虎,這世上最難降的妖魔是什么璃谨? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮鲤妥,結(jié)果婚禮上佳吞,老公的妹妹穿的比我還像新娘。我一直安慰自己棉安,他們只是感情好底扳,可當(dāng)我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著垂券,像睡著了一般花盐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上菇爪,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機(jī)與錄音柒昏,去河邊找鬼凳宙。 笑死,一個胖子當(dāng)著我的面吹牛职祷,可吹牛的內(nèi)容都是我干的氏涩。 我是一名探鬼主播届囚,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼是尖!你這毒婦竟也來了意系?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤饺汹,失蹤者是張志新(化名)和其女友劉穎蛔添,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體兜辞,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡迎瞧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了逸吵。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凶硅。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖扫皱,靈堂內(nèi)的尸體忽然破棺而出足绅,到底是詐尸還是另有隱情,我是刑警寧澤韩脑,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布编检,位于F島的核電站,受9級特大地震影響扰才,放射性物質(zhì)發(fā)生泄漏允懂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一衩匣、第九天 我趴在偏房一處隱蔽的房頂上張望蕾总。 院中可真熱鬧,春花似錦琅捏、人聲如沸生百。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蚀浆。三九已至,卻和暖如春搜吧,著一層夾襖步出監(jiān)牢的瞬間市俊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工滤奈, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留摆昧,地道東北人。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓蜒程,卻偏偏與公主長得像绅你,于是被迫代替她去往敵國和親伺帘。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容