【HarmonyOS】List組件多層對象嵌套ForEach渲染更新的處理
問題背景:
在鴻蒙中UI更新渲染的機制,與傳統的Android IOS應用開發相比。開發會簡單許多,開發效率提升顯著。
一般傳統應用開發的流程處理分為三步:1.畫UI,2.獲得or創建,處理數據,3.增刪改數據,找到對應控件,更新到UI上。
而鴻蒙應用開發,大大提供效率其中一點,就是減少了第三步。我們只需要關心數據源的變化,數據自動會更新到對應的控件上。
這種處理機制,其實符合應用開發的時代潮流,目前Android和IOS最新框架機制都有相應類似的處理。例如swiftUI,Compose。并且Vue,Flutter整個刷新機制就是如此。
眾所周知,在HarmonyOs的list組件渲染中,如果數據源列表對象是多個對象嵌套的處理,那最底層對象的屬性更新時,UI界面是不會渲染的。因為檢測不到,目前只能檢測到第一層對象。
解決方案:
當然官方的api在持續迭代的過程中。對于冗余渲染,渲染的性能提升,還有多層對象嵌套的數據源更新問題,一直再迭代方法處理。
針對多層對象嵌套,底層對象屬性修改后,UI不渲染的問題,有個比較簡單又方便的處理方式,思路僅供參考。
即:深拷貝修改的item對象。
這樣整個對象相當于都變化了,就符合第一層對象檢測的機制,可以被系統捕獲到刷新。
DEMO示例:
import { util } from '@kit.ArkTS';/*** 二級數據結構*/
class ChildInfo {index: number;constructor(index: number) {this.index = index;}
}/*** 一級數據結構*/
class ItemInfo {key: string = util.generateRandomUUID(true);name: string;icon: Resource;childInfo: ChildInfo;select: boolean;constructor(name: string, icon: Resource, index: number) {this.name = name;this.icon = icon;this.childInfo = new ChildInfo(index);this.select = false;}/*** 重新創建對象,深拷貝處理* @param itemInfo* @param index* @returns*/static deepCopy(itemInfo: ItemInfo, index: number){let info: ItemInfo = new ItemInfo(itemInfo.name, itemInfo.icon, index);info.select = itemInfo.select;info.key = itemInfo.key;info.childInfo = itemInfo.childInfo;return info;}
}/****/
struct ForeachPage {private TAG: string = "ForeachPage"; mListData: Array<ItemInfo> = [];aboutToAppear(): void {this.mListData.push(new ItemInfo('游戲', $r("app.media.iconA"), 1));this.mListData.push(new ItemInfo('游戲', $r("app.media.iconB"), 2));this.mListData.push(new ItemInfo('游戲', $r("app.media.iconA"), 3));this.mListData.push(new ItemInfo('游戲', $r("app.media.iconB"), 4));this.mListData.push(new ItemInfo('游戲', $r("app.media.iconA"), 5));this.mListData.push(new ItemInfo('游戲', $r("app.media.iconB"), 6));} ItemView(item: ItemInfo, index: number){Row() {Image(item.icon).width(px2vp(120)).height(px2vp(120))Text(item.name + "(" + item.childInfo.index + ")").fontSize(20)Blank()if(this.isLog(item, index)){if(item.select){Image($r("app.media.icon_check")).size({width: px2vp(72),height: px2vp(72)})}}}.width('100%').justifyContent(FlexAlign.Start).onClick(()=>{item.select = !item.select;if(item.select){item.childInfo.index = 666;}else{item.childInfo.index = index;}this.mListData[index] = ItemInfo.deepCopy(item, item.childInfo.index);console.log(this.TAG, " ItemView onClick: " + index + " item.select: " + item.select);})}private isLog(item: ItemInfo, index: number){console.log(this.TAG, " ItemView isLog index: " + index + " item.select: " + item.select);return true;}build() {List() {ForEach(this.mListData, (item: ItemInfo, index: number) => {ListItem() {this.ItemView(item, index)}}, (item: ItemInfo) => JSON.stringify(item))//}.width("100%").height("100%").padding({ left: px2vp(60), right: px2vp(60) })}
}