HarmonyOS4.0從零開始的開發教程10管理組件狀態

HarmonyOS(八)管理組件狀態

概述

在應用中,界面通常都是動態的。如圖1所示,在子目標列表中,當用戶點擊目標一,目標一會呈現展開狀態,再次點擊目標一,目標一呈現收起狀態。界面會根據不同的狀態展示不一樣的效果。

圖1 展開/收起目標項
點擊放大

ArkUI作為一種聲明式UI,具有狀態驅動UI更新的特點。當用戶進行界面交互或有外部事件引起狀態改變時,狀態的變化會觸發組件自動更新。所以在ArkUI中,我們只需要通過一個變量來記錄狀態。當改變狀態的時候,ArkUI就會自動更新界面中受影響的部分。

點擊放大

ArkUI框架提供了多種管理狀態的裝飾器來修飾變量,使用這些裝飾器修飾的變量即稱為狀態變量。

在組件范圍傳遞的狀態管理常見的場景如下:

場景裝飾器
組件內的狀態管理@State
從父組件單向同步狀態@Prop
與父組件雙向同步狀態@Link
跨組件層級雙向同步狀態@Provide和@Consume

在組件內使用@State裝飾器來修飾變量,可以使組件根據不同的狀態來呈現不同的效果。若當前組件的狀態需要通過其父組件傳遞而來,此時需要使用@Prop裝飾器;若是父子組件狀態需要相互綁定進行雙向同步,則需要使用@Link裝飾器。使用@Provide和@Consume裝飾器可以實現跨組件層級雙向同步狀態。

在實際應用開發中,應用會根據需要封裝數據模型。如果需要觀察嵌套類對象屬性變化,需要使用@Observed和@ObjectLink裝飾器,因為上述表格中的裝飾器只能觀察到對象的第一層屬性變化。@Observed和@ObjectLink裝飾器的具體使用方法可參考@Observed裝飾器和@ObjectLink裝飾器:嵌套類對象屬性變化。

另外,當狀態改變,需要對狀態變化進行監聽做一些相應的操作時,可以使用@Watch裝飾器來修飾狀態。

組件內的狀態管理:@State

實際開發中由于交互,組件的內容呈現可能產生變化。當需要在組件內使用狀態來控制UI的不同呈現方式時,可以使用@State裝飾器。以任務管理應用為例,當點擊子目標列表的其中一項,列表項會展開。當再次點擊同一項,列表項會收起。所以,對于某一個列表項來說,它的呈現方式會受列表項是否展開這個狀態影響。

圖2 展開/收起目標項
點擊放大

將是否展開這個狀態定義為isExpanded變量,當其值為false表示目標項收起,值為true時表示目標項展開。

此時,需要使用@State裝飾器修飾isExpanded,使其成為目標項內部的狀態變量。通過@State裝飾后,框架內部會建立數據與視圖間的綁定,

當isExpanded狀態變化時,目標項會隨之展開或收起。

圖3 定義是否展開狀態
點擊放大

其具體實現只要用@State修飾isExpanded變量,定義是否展開狀態。然后通過條件渲染,實現是否顯示進度調整面板和列表項的高度變化。最后,監聽列表項的點擊事件,在onClick回調中改變isExpanded狀態。

這樣就實現了對相同列表項點擊時,列表項的展開和收起功能。當用戶反復點擊同一個列表項時,組件內的isExpanded狀態變化,列表項會自動更新。

@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;...})...}
}

從父組件單向同步狀態:@Prop

當子組件中的狀態依賴從父組件傳遞而來時,需要使用@Prop裝飾器,@Prop修飾的變量可以和其父組件中的狀態建立單向同步關系。當父組件中狀態變化時,該狀態值也會更新至@Prop修飾的變量;對@Prop修飾的變量的修改不會影響其父組件中的狀態。

圖4 列表的編輯模式
點擊放大

如圖4所示,在目標管理應用中,當用戶點擊子目標列表的“編輯”文本,列表進入編輯模式,點擊取消,列表退出編輯模式。

整個列表是自定義組件TargetList,頂部是文本顯示區域,主要是Text組件,底部是一個Button組件。中間區域則是用來顯示每個目標項,目標項是自定義組件TargetListItem。

從圖中可以看出,TargetListItem是TargetList的子組件。TargetList是TargetListItem父組件。

圖5 TargetList和TargetListItem
點擊放大

對于父組件TargetList,其頂部顯示的文本和底部按鈕會隨編輯模式的變化而變化,因此父組件擁有編輯模式狀態。

對于子組件TargetListItem,其最右側是否預留位置和顯示勾選框也會隨編輯模式變化,因此子組件也擁有編輯模式狀態。

但是是否進入編輯模式,其觸發點是在用戶點擊列表的“編輯”或取消按鈕,狀態變化的源頭僅在于父組件TargetList。當父組件TargetList中的編輯模式變化時,子組件TargetListItem的編輯模式狀態需要隨之變化。

圖6 從父組件單向同步isEditMode狀態
點擊放大

在父組件TargetList中可以定義一個是否進入編輯模式的狀態,即用@State修飾isEditMode。@State修飾的變量不僅是組件內部的狀態,也可以作為子組件單向或雙向同步的數據源。ArkUI提供了@Prop裝飾器,@Prop修飾的變量可以和其父組件中的狀態建立單向同步關系,所以用@Prop修飾子組件TargetListItem中的isEditMode變量。

在父組件TargetList中,用@State修飾isEditMode,定義編輯模式狀態。然后利用條件渲染實現根據是否進入編輯模式,顯示不同的文本和按鈕。同時,在父組件中需要在用戶點擊時改變狀態,觸發界面更新。

當點擊“編輯”事件發生時,進入編輯模式,顯示取消、全選文本和勾選框,同時顯示刪除按鈕;當點擊“取消”事件發生時,退出編輯模式,顯示“編輯”文本和“添加子目標”按鈕。

@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變量,定義子組件的編輯模式狀態。然后同樣根據是否進入編輯模式,控制目標項最右側是否預留位置和顯示勾選框。

@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()...}}...
}

最后,最關鍵的一步就是要在父組件中使用子組件時,將父組件的編輯模式狀態this.isEditMode傳遞給子組件的編輯模式狀態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)}...}...}
}

與父組件雙向同步狀態:@Link

若是父子組件狀態需要相互綁定進行雙向同步時,可以使用@Link裝飾器。父組件中用于初始化子組件@Link變量的必須是在父組件中定義的狀態變量。

圖7 切換目標項
點擊放大

在目標管理應用中,當用戶點擊同一個目標,目標項會展開或者收起。當用戶點擊不同的目標項時,除了被點擊的目標項展開,同時前一次被點擊的目標項會收起。

如圖7所示,當目標一展開時,點擊目標三,目標三會展開,同時目標一會收起。再點擊目標一時,目標一展開,同時目標三會收起。

從目標一切換到目標三的流程中,關鍵在于最后目標一的收起,當點擊目標三時,目標一需要知道點擊了目標三,目標一才會收起。

圖8 子目標列表目標項位置索引
點擊放大

在子目標列表中,每個列表項都有其位置索引值index屬性,表示目標項在列表中的位置。index從0開始,即第一個目標項的索引值為0,第二個目標項的索引值為1,以此類推。此外,clickIndex用來記錄被點擊的目標項索引。當點擊目標一時,clickIndex為0,點擊目標三時,clickIndex為2。

在父組件子目標列表和每個子組件目標項中都擁有clickIndex狀態。當目標一展開時,clickIndex為0。此時點擊目標三,目標三的clickIndex變為2,只要其父組件子目標列表感知到clickIndex狀態變化,同時將此變化傳遞給目標一。目標一的clickIndex即可同步改變為2,即目標一感知到此時點擊了目標三。

圖9 與父組件雙向同步clickIndex狀態
點擊放大

將列表和目標項對應到列表組件TargetList和列表項TargetListItem。首先,需要在父組件TargetList中定義clickIndex狀態。

若此時子組件中的clickIndex用@Prop裝飾器修飾,當子組件中clickIndex變化時,父組件無法感知,因為@Prop裝飾器建立的是從父組件到子組件的單向同步關系。

ArkUI提供了@Link裝飾器,用于與父組件雙向同步狀態。當子組件TargetListItem中的clickIndex用@Link修飾,可與父組件TargetList中的clickIndex建立雙向同步關系。

@Component
export default struct TargetList {@State clickIndex: number = CommonConstants.DEFAULT_CLICK_INDEX;...TargetListItem({clickIndex: $clickIndex,...})...
}

首先,在父組件TargetList中用@State裝飾器定義點擊的目標項索引狀態。然后,在子組件TargetListItem中用@Link裝飾器定義clickIndex,當點擊目標項時,clickIndex更新為當前目標索引值。

完成在父子組件中定義狀態后,最關鍵的就是要建立父子組件的雙向關聯關系。在父組件中使用子組件時,將父組件的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;...})...}
}

當目標一感知到點擊了目標三時,還需要將目標一收起,切換列表項的功能才是完整的。此時,目標一感知到clickIndex變為2,需要判斷與目標一本身的位置索引值0不相等,從而將目標一收起。此時,就需要用到ArkUI中監聽狀態變化@Watch的能力。用@Watch修飾的狀態,當狀態發生變化時,會觸發聲明時定義的回調。

我們給TargetListItem的中的clickIndex狀態加上@Watch(“onClickIndexChanged”)。這表示需要監聽clickIndex狀態的變化。當clickIndex狀態變化時,將觸發onClickIndexChanged回調:如果點擊的列表項索引不等于當前列表項索引,則將isExpanded狀態置為false,從而收起該目標項。

跨組件層級雙向同步狀態:@Provide和@Consume

點擊放大

跨組件層級雙向同步狀態是指@Provide修飾的狀態變量自動對提供者組件的所有后代組件可用,后代組件通過使用@Consume裝飾的變量來獲得對提供的狀態變量的訪問。@Provide作為數據的提供方,可以更新其子孫節點的數據,并觸發頁面渲染。@Consume在感知到@Provide數據的更新后,會觸發當前自定義組件的重新渲染。

使用@Provide的好處是開發者不需要多次將變量在組件間傳遞。@Provide和@Consume的具體使用方法請參見開發指南:@Provide裝飾器和@Consume裝飾器:與后代組件雙向同步。

參考

更多狀態管理場景和相關知識請參考開發指南:狀態管理。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/214361.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/214361.shtml
英文地址,請注明出處:http://en.pswp.cn/news/214361.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ERROR: [BD 41-237] Bus Interface property FREQ_HZ does not match between

在自定義IP出現以上錯誤時可以通過雙擊模塊clk屬性 如果是灰色無法二次編輯時,在封裝IP時,選擇以下菜單

復雜sql分析 以及 索引合并

復雜sql分析 簡單的sql語句我們很簡單的就可以分析出來它的執行計劃&#xff0c;但是復雜的sql呢。例如 SELECT * FROM single_table WHERE(key1 > xyz AND key2 748 ) OR(key1 < abc AND key1 > lmn) OR(key1 LIKE %suf AND key1 > zzz AND (key2 < 8000 OR…

為什么同一張顯卡,深度學習的模型訓練的時候,有時候成功了,有時候失敗了

在同一張顯卡上進行深度學習模型訓練時&#xff0c;成功或失敗的結果可能受到以下因素的影響&#xff1a; 隨機性: 深度學習模型中的一些組件&#xff08;如權重初始化、數據的隨機排列等&#xff09;可能涉及到隨機性。這可能導致在每次訓練時得到不同的結果&#xff0c;有時成…

Mybatis進階知識

Mybatis的事務管理機制 在mybatis-config.xml文件中.可以進行mybatis的事務管理 <transactionManager type"JDBC"/> type的值有兩個 JDBCMANAGED JDBC事務管理器 mybatis框架自己管理事務&#xff0c;自己采用原生的JDBC代碼去管理事務 底層創建的事務管…

匯總區間算法(leetcode第228題)

題目描述&#xff1a; 給定一個 無重復元素 的 有序 整數數組 nums 。返回 恰好覆蓋數組中所有數字 的 最小有序 區間范圍列表 。也就是說&#xff0c;nums 的每個元素都恰好被某個區間范圍所覆蓋&#xff0c;并且不存在屬于某個范圍但不屬于 nums 的數字 x 。列表中的每個區…

財務機器人(RPA)會影響會計人員從業嗎?

財務機器人會對會計從業人員有影響。 不過是正面積極的影響。 它是財務人員工作的好助手好幫手。 具體展開聊聊財務RPA機器人是如何成為財務人員的好幫手。 財務機器人是在人工智能和自動化技術的基礎上建立的、以軟件機器人作為虛擬勞動力、依據預先設定的程序與現有用戶系…

活用異常處理(try-except)

介紹 簡要介紹Python的異常處理&#xff08;try-except&#xff09;。 基本語法 Try-except是Python中異常處理的基礎。 作為例子&#xff0c;我們將解釋執行除零時發生 ZeroDivisionError 的情況。 當除以零時&#xff0c;會出現以下錯誤。 print(1 / 0) # ZeroDivisionE…

藍牙配對、連接和刪除匯總

目的&#xff1a;處理PC連接藍牙設備的配對、連接和刪除操作&#xff0c;以及常見故障。 命令行配對藍牙設備并連接 要求&#xff1a;配對BLUET043藍牙&#xff0c;密碼為4444&#xff0c;然后連接該藍牙。操作步驟如下&#xff1a; Step1.下載 修復工具&#xff0c;然后安裝…

利用python編寫簡易POC腳本

POC&#xff1a;概念證明&#xff0c;即概念驗證&#xff08;英語&#xff1a;Proof of concept&#xff0c;簡稱POC&#xff09;是對某些想法的一個較短而不完整的實現&#xff0c;以證明其可行性&#xff0c;示范其原理&#xff0c;其目的是為了驗證一些概念或理論。 聲明&a…

三哥的黑科技,印度發布無線加熱服裝專利,冬季神器要來了

眾所周知風和自由在冬天是不存在的&#xff0c;冬天只剩下冰冷的像刀子一樣的風刮在你的臉上&#xff0c;哪怕穿的很厚&#xff0c;戴上全盔&#xff0c;也無法阻擋冰冷的風帶走你身體溫度&#xff0c;如果穿的特別多&#xff0c;騎車時候的舒適感和穿脫衣物的繁瑣也是一大頭疼…

【MySQL系列】Centos安裝MySQL

&#x1f49d;&#x1f49d;&#x1f49d;歡迎來到我的博客&#xff0c;很高興能夠在這里和您見面&#xff01;希望您在這里可以感受到一份輕松愉快的氛圍&#xff0c;不僅可以獲得有趣的內容和知識&#xff0c;也可以暢所欲言、分享您的想法和見解。 推薦:kwan 的首頁,持續學…

vim常用鍵位總結

1、移動光標 按鍵作用h或←光標向左移動一個字符j或↓光標向下移動一個字符k或↑光標向上移動一個字符l或→光標向右移動一個字符Ctrl f或Page Down屏幕向下移動一頁&#xff08;常用&#xff09;Ctrl b或Page Up屏幕向上移動一頁&#xff08;常用&#xff09;Ctrl d屏幕向…

Kafka使用總結

1、Kafka是何如做到高性能的&#xff1f; a、消息批處理減少網絡通信開銷&#xff0c;提升系統吞吐能力&#xff08;先攢一波&#xff0c;消息以“批”為單位進行處理&#xff09; 生產端&#xff1a;無論是同步發送還是異步發送&#xff0c;Kafka都不會立即就把這條消息發送出…

【C++】算法庫(復制操作、交換操作、變換操作)

C算法庫 文章目錄 C算法庫復制操作copy , copy_ifcopy_ncopy_backward 交換操作swapswap_rangesiter_swap 變換操作transformreplacereplace_copy replace_copy_if 算法庫提供大量用途的函數&#xff08;例如查找、排序、計數、操作&#xff09;&#xff0c;它們在元素范圍上操…

java--StringBuilder、StringBuffer、StringJoiner

1.StringBuilder ①StringBuilder代表可變字符串對象&#xff0c;相當于是一個容器&#xff0c;它里面裝的字符串是可以改變的&#xff0c;就是用來操作字符串的。 ②好處&#xff1a;StringBuilder比String更適合做字符串的修改操作&#xff0c;效率會比更高&#xff0c;代碼…

Java入門:EditPlus的安裝與配置講解

1. 前言 Java是一種面向對象的編程語言&#xff0c;廣泛應用于各種平臺和設備。在Java開發過程中&#xff0c;一個好的集成開發環境&#xff08;IDE&#xff09;是非常重要的。EditPlus是一款簡單易用的文本編輯器&#xff0c;它提供了一些方便的功能來支持Java開發。本文將介…

【開源】基于JAVA的木馬文件檢測系統

項目編號&#xff1a; S 041 &#xff0c;文末獲取源碼。 \color{red}{項目編號&#xff1a;S041&#xff0c;文末獲取源碼。} 項目編號&#xff1a;S041&#xff0c;文末獲取源碼。 目錄 一、摘要1.1 項目介紹1.2 項目錄屏 二、功能模塊2.1 數據中心模塊2.2 木馬分類模塊2.3 木…

Leo贈書活動-13期 【以企業架構為中心的SABOE數字化轉型五環法】文末送書

Leo贈書活動-13期 【以企業架構為中心的SABOE數字化轉型五環法】文末送書 ?作者簡介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;熱愛Java后端開發者&#xff0c;一個想要與大家共同進步的男人&#x1f609;&#x1f609; &#x1f34e;個人主頁&#xff1a;Leo的博客…

記錄 | xshell輸出錯亂解決

輸出錯亂問題&#xff1a; 解決方法&#xff1a;