概述
在應(yīng)用中,界面通常都是動態(tài)的。如圖1所示,在子目標(biāo)列表中,當(dāng)用戶點擊目標(biāo)一,目標(biāo)一會呈現(xiàn)展開狀態(tài),再次點擊目標(biāo)一,目標(biāo)一呈現(xiàn)收起狀態(tài)。界面會根據(jù)不同的狀態(tài)展示不一樣的效果。
ArkUI作為一種聲明式UI,具有狀態(tài)驅(qū)動UI更新的特點。當(dāng)用戶進(jìn)行界面交互或有外部事件引起狀態(tài)改變時,狀態(tài)的變化會觸發(fā)組件自動更新。所以在ArkUI中,我們只需要通過一個變量來記錄狀態(tài)。當(dāng)改變狀態(tài)的時候,ArkUI就會自動更新界面中受影響的部分。
ArkUI框架提供了多種管理狀態(tài)的裝飾器來修飾變量,使用這些裝飾器修飾的變量即稱為狀態(tài)變量。
在組件范圍傳遞的狀態(tài)管理常見的場景如下:
場景 | 裝飾器 |
---|---|
組件內(nèi)的狀態(tài)管理 | @State |
從父組件單向同步狀態(tài) | @Prop |
與父組件雙向同步狀態(tài) | @Link |
跨組件層級雙向同步狀態(tài) | @Provide和@Consume |
在組件內(nèi)使用@State裝飾器來修飾變量,可以使組件根據(jù)不同的狀態(tài)來呈現(xiàn)不同的效果。若當(dāng)前組件的狀態(tài)需要通過其父組件傳遞而來,此時需要使用@Prop裝飾器;若是父子組件狀態(tài)需要相互綁定進(jìn)行雙向同步,則需要使用@Link裝飾器。使用@Provide和@Consume裝飾器可以實現(xiàn)跨組件層級雙向同步狀態(tài)。
在實際應(yīng)用開發(fā)中,應(yīng)用會根據(jù)需要封裝數(shù)據(jù)模型。如果需要觀察嵌套類對象屬性變化,需要使用@Observed和@ObjectLink裝飾器,因為上述表格中的裝飾器只能觀察到對象的第一層屬性變化。
另外,當(dāng)狀態(tài)改變,需要對狀態(tài)變化進(jìn)行監(jiān)聽做一些相應(yīng)的操作時,可以使用@Watch裝飾器來修飾狀態(tài)。
組件內(nèi)的狀態(tài)管理:@State
實際開發(fā)中由于交互,組件的內(nèi)容呈現(xiàn)可能產(chǎn)生變化。當(dāng)需要在組件內(nèi)使用狀態(tài)來控制UI的不同呈現(xiàn)方式時,可以使用@State裝飾器。以任務(wù)管理應(yīng)用為例,當(dāng)點擊子目標(biāo)列表的其中一項,列表項會展開。當(dāng)再次點擊同一項,列表項會收起。所以,對于某一個列表項來說,它的呈現(xiàn)方式會受列表項是否展開這個狀態(tài)影響。
將是否展開這個狀態(tài)定義為isExpanded變量,當(dāng)其值為false表示目標(biāo)項收起,值為true時表示目標(biāo)項展開。
此時,需要使用@State裝飾器修飾isExpanded,使其成為目標(biāo)項內(nèi)部的狀態(tài)變量。通過@State裝飾后,框架內(nèi)部會建立數(shù)據(jù)與視圖間的綁定,
當(dāng)isExpanded狀態(tài)變化時,目標(biāo)項會隨之展開或收起。
其具體實現(xiàn)只要用@State修飾isExpanded變量,定義是否展開狀態(tài)。然后通過條件渲染,實現(xiàn)是否顯示進(jìn)度調(diào)整面板和列表項的高度變化。最后,監(jiān)聽列表項的點擊事件,在onClick回調(diào)中改變isExpanded狀態(tài)。
這樣就實現(xiàn)了對相同列表項點擊時,列表項的展開和收起功能。當(dāng)用戶反復(fù)點擊同一個列表項時,組件內(nèi)的isExpanded狀態(tài)變化,列表項會自動更新。
@Component
export default struct TargetListItem {
@State isExpanded: boolean = false;
...
build() {
...
Column() {
...
if (this.isExpanded) {
Blank()
ProgressEditPanel(...)
}
}
.height(this.isExpanded ? $r('app.float.expanded_item_height')
: $r('app.float.list_item_height'))
.onClick(() = > {
...
this.isExpanded = !this.isExpanded;
...
})
...
}
}
從父組件單向同步狀態(tài):@Prop
當(dāng)子組件中的狀態(tài)依賴從父組件傳遞而來時,需要使用@Prop裝飾器,@Prop修飾的變量可以和其父組件中的狀態(tài)建立單向同步關(guān)系。當(dāng)父組件中狀態(tài)變化時,該狀態(tài)值也會更新至@Prop修飾的變量;對@Prop修飾的變量的修改不會影響其父組件中的狀態(tài)。
圖4 列表的編輯模式
如圖4所示,在目標(biāo)管理應(yīng)用中,當(dāng)用戶點擊子目標(biāo)列表的“編輯”文本,列表進(jìn)入編輯模式,點擊取消,列表退出編輯模式。
整個列表是自定義組件TargetList,頂部是文本顯示區(qū)域,主要是Text組件,底部是一個Button組件。中間區(qū)域則是用來顯示每個目標(biāo)項,目標(biāo)項是自定義組件TargetListItem。
從圖中可以看出,TargetListItem是TargetList的子組件。TargetList是TargetListItem父組件。
圖5 TargetList和TargetListItem
對于父組件TargetList,其頂部顯示的文本和底部按鈕會隨編輯模式的變化而變化,因此父組件擁有編輯模式狀態(tài)。
對于子組件TargetListItem,其最右側(cè)是否預(yù)留位置和顯示勾選框也會隨編輯模式變化,因此子組件也擁有編輯模式狀態(tài)。
但是是否進(jìn)入編輯模式,其觸發(fā)點是在用戶點擊列表的“編輯”或取消按鈕,狀態(tài)變化的源頭僅在于父組件TargetList。當(dāng)父組件TargetList中的編輯模式變化時,子組件TargetListItem的編輯模式狀態(tài)需要隨之變化。
圖6 從父組件單向同步isEditMode狀態(tài)
在父組件TargetList中可以定義一個是否進(jìn)入編輯模式的狀態(tài),即用@State修飾isEditMode。@State修飾的變量不僅是組件內(nèi)部的狀態(tài),也可以作為子組件單向或雙向同步的數(shù)據(jù)源。ArkUI提供了@Prop裝飾器,@Prop修飾的變量可以和其父組件中的狀態(tài)建立單向同步關(guān)系,所以用@Prop修飾子組件TargetListItem中的isEditMode變量。
在父組件TargetList中,用@State修飾isEditMode,定義編輯模式狀態(tài)。然后利用條件渲染實現(xiàn)根據(jù)是否進(jìn)入編輯模式,顯示不同的文本和按鈕。同時,在父組件中需要在用戶點擊時改變狀態(tài),觸發(fā)界面更新。
當(dāng)點擊“編輯”事件發(fā)生時,進(jìn)入編輯模式,顯示取消、全選文本和勾選框,同時顯示刪除按鈕;當(dāng)點擊“取消”事件發(fā)生時,退出編輯模式,顯示“編輯”文本和“添加子目標(biāo)”按鈕。
@Component
export default struct TargetList {
@State isEditMode: boolean = false;
...
build() {
Column() {
Row() {
...
if (this.isEditMode) {
Text($r('app.string.cancel_button'))
.onClick(() = > {
this.isEditMode = false;
...
})
...
Text($r('app.string.select_all_button'))...
Checkbox()...
} else {
Text($r('app.string.edit_button'))
.onClick(() = > {
this.isEditMode = true;
})
...
}
...
}
...
List({ space: CommonConstants.LIST_SPACE }) {
ForEach(this.targetData, (item: TaskItemBean, index: number) = > {
ListItem() {
TargetListItem({
isEditMode: this.isEditMode,
...
})
}
}, (item, index) = > JSON.stringify(item) + index)
}
...
if (this.isEditMode) {
Button($r('app.string.delete_button'))
} else {
Button($r('app.string.add_task'))
}
}
...
}
}
在子組件TargetListItem中,使用@Prop修飾子組件的isEditMode變量,定義子組件的編輯模式狀態(tài)。然后同樣根據(jù)是否進(jìn)入編輯模式,控制目標(biāo)項最右側(cè)是否預(yù)留位置和顯示勾選框。
@Component
export default struct TargetListItem {
@Prop isEditMode: boolean;
...
Column() {
...
}
.padding({
...
right: this.isEditMode ? $r('app.float.list_edit_padding')
: $r('app.float.list_padding')
})
...
if (this.isEditMode) {
Row() {
Checkbox()...
}
}
...
}
最后,最關(guān)鍵的一步就是要在父組件中使用子組件時,將父組件的編輯模式狀態(tài)this.isEditMode傳遞給子組件的編輯模式狀態(tài)isEditMode。
@Component
export default struct TargetList {
@State isEditMode: boolean = false;
...
build() {
Column() {
...
List({ space: CommonConstants.LIST_SPACE }) {
ForEach(this.targetData, (item: TaskItemBean, index: number) = > {
ListItem() {
TargetListItem({
isEditMode: this.isEditMode,
...
})
}
}, (item, index) = > JSON.stringify(item) + index)
}
...
}
...
}
}
與父組件雙向同步狀態(tài):@Link
若是父子組件狀態(tài)需要相互綁定進(jìn)行雙向同步時,可以使用@Link裝飾器。父組件中用于初始化子組件@Link變量的必須是在父組件中定義的狀態(tài)變量。
圖7 切換目標(biāo)項
在目標(biāo)管理應(yīng)用中,當(dāng)用戶點擊同一個目標(biāo),目標(biāo)項會展開或者收起。當(dāng)用戶點擊不同的目標(biāo)項時,除了被點擊的目標(biāo)項展開,同時前一次被點擊的目標(biāo)項會收起。
如圖7所示,當(dāng)目標(biāo)一展開時,點擊目標(biāo)三,目標(biāo)三會展開,同時目標(biāo)一會收起。再點擊目標(biāo)一時,目標(biāo)一展開,同時目標(biāo)三會收起。
從目標(biāo)一切換到目標(biāo)三的流程中,關(guān)鍵在于最后目標(biāo)一的收起,當(dāng)點擊目標(biāo)三時,目標(biāo)一需要知道點擊了目標(biāo)三,目標(biāo)一才會收起。
圖8 子目標(biāo)列表目標(biāo)項位置索引
在子目標(biāo)列表中,每個列表項都有其位置索引值index屬性,表示目標(biāo)項在列表中的位置。index從0開始,即第一個目標(biāo)項的索引值為0,第二個目標(biāo)項的索引值為1,以此類推。此外,clickIndex用來記錄被點擊的目標(biāo)項索引。當(dāng)點擊目標(biāo)一時,clickIndex為0,點擊目標(biāo)三時,clickIndex為2。
在父組件子目標(biāo)列表和每個子組件目標(biāo)項中都擁有clickIndex狀態(tài)。當(dāng)目標(biāo)一展開時,clickIndex為0。此時點擊目標(biāo)三,目標(biāo)三的clickIndex變?yōu)?,只要其父組件子目標(biāo)列表感知到clickIndex狀態(tài)變化,同時將此變化傳遞給目標(biāo)一。目標(biāo)一的clickIndex即可同步改變?yōu)?,即目標(biāo)一感知到此時點擊了目標(biāo)三。
圖9 與父組件雙向同步clickIndex狀態(tài)
將列表和目標(biāo)項對應(yīng)到列表組件TargetList和列表項TargetListItem。首先,需要在父組件TargetList中定義clickIndex狀態(tài)。
若此時子組件中的clickIndex用@Prop裝飾器修飾,當(dāng)子組件中clickIndex變化時,父組件無法感知,因為@Prop裝飾器建立的是從父組件到子組件的單向同步關(guān)系。
ArkUI提供了@Link裝飾器,用于與父組件雙向同步狀態(tài)。當(dāng)子組件TargetListItem中的clickIndex用@Link修飾,可與父組件TargetList中的clickIndex建立雙向同步關(guān)系。
@Component
export default struct TargetList {
@State clickIndex: number = CommonConstants.DEFAULT_CLICK_INDEX;
...
TargetListItem({
clickIndex: $clickIndex,
...
})
...
}
首先,在父組件TargetList中用@State裝飾器定義點擊的目標(biāo)項索引狀態(tài)。然后,在子組件TargetListItem中用@Link裝飾器定義clickIndex,當(dāng)點擊目標(biāo)項時,clickIndex更新為當(dāng)前目標(biāo)索引值。
完成在父子組件中定義狀態(tài)后,最關(guān)鍵的就是要建立父子組件的雙向關(guān)聯(lián)關(guān)系。在父組件中使用子組件時,將父組件的clickIndex傳遞給子組件的clickIndex。其中父組件的clickIndex加上$表示傳遞的是引用。
@Component
export default struct TargetListItem {
@Link @Watch('onClickIndexChanged') clickIndex: number;
@State isExpanded: boolean = false
...
onClickIndexChanged() {
if (this.clickIndex != this.index) {
this.isExpanded = false;
}
}
build() {
...
Column() {
...
}
.onClick(() = > {
...
this.clickIndex = this.index;
...
})
...
}
}
當(dāng)目標(biāo)一感知到點擊了目標(biāo)三時,還需要將目標(biāo)一收起,切換列表項的功能才是完整的。此時,目標(biāo)一感知到clickIndex變?yōu)?,需要判斷與目標(biāo)一本身的位置索引值0不相等,從而將目標(biāo)一收起。此時,就需要用到ArkUI中監(jiān)聽狀態(tài)變化@Watch的能力。用@Watch修飾的狀態(tài),當(dāng)狀態(tài)發(fā)生變化時,會觸發(fā)聲明時定義的回調(diào)。
我們給TargetListItem的中的clickIndex狀態(tài)加上@Watch("onClickIndexChanged")。這表示需要監(jiān)聽clickIndex狀態(tài)的變化。當(dāng)clickIndex狀態(tài)變化時,將觸發(fā)onClickIndexChanged回調(diào):如果點擊的列表項索引不等于當(dāng)前列表項索引,則將isExpanded狀態(tài)置為false,從而收起該目標(biāo)項。
跨組件層級雙向同步狀態(tài):@Provide和@Consume
跨組件層級雙向同步狀態(tài)是指@Provide修飾的狀態(tài)變量自動對提供者組件的所有后代組件可用,后代組件通過使用@Consume裝飾的變量來獲得對提供的狀態(tài)變量的訪問。@Provide作為數(shù)據(jù)的提供方,可以更新其子孫節(jié)點的數(shù)據(jù),并觸發(fā)頁面渲染。@Consume在感知到@Provide數(shù)據(jù)的更新后,會觸發(fā)當(dāng)前自定義組件的重新渲染。
使用@Provide的好處是開發(fā)者不需要多次將變量在組件間傳遞。
-
組件
+關(guān)注
關(guān)注
1文章
527瀏覽量
18240 -
鴻蒙
+關(guān)注
關(guān)注
57文章
2469瀏覽量
43642 -
OpenHarmony
+關(guān)注
關(guān)注
26文章
3804瀏覽量
17861
發(fā)布評論請先 登錄
相關(guān)推薦
開源啦!!!基于鴻蒙ArkTS封裝的圖表組件《McCharts》,大家快來一起共創(chuàng)
鴻蒙原生頁面高性能解決方案上線OpenHarmony社區(qū) 助力打造高性能原生應(yīng)用
《HarmonyOS第一課》煥新升級,賦能開發(fā)者快速掌握鴻蒙應(yīng)用開發(fā)
鴻蒙Flutter實戰(zhàn):14-現(xiàn)有Flutter 項目支持鴻蒙 II
鴻蒙ArkTS聲明式組件:PatternLock

鴻蒙開發(fā):任務(wù)(Mission)管理場景介紹

鴻蒙ArkTS聲明式開發(fā):跨平臺支持列表【組件快捷鍵事件】

評論