HarmonyOS鸿蒙Next应用V1状态装饰器使用List组件多层对象嵌套ForEach渲染更新的处理
HarmonyOS鸿蒙Next应用V1状态装饰器使用List组件多层对象嵌套ForEach渲染更新的处理
鸿蒙应用V1状态装饰器使用List组件多层对象嵌套ForEach渲染更新的处理
一、结论
在鸿蒙中UI更新渲染的机制,与传统的Android IOS应用开发相比。开发会简单许多,开发效率提升显著。
一般传统应用开发的流程处理分为三步:1.画UI,2.获得or创建,处理数据,3.增删改数据,找到对应控件,更新到UI上。
而鸿蒙应用开发,大大提供效率其中一点,就是减少了第三步。我们只需要关心数据源的变化,数据自动会更新到对应的控件上。
这种处理机制,其实符合应用开发的时代潮流,目前Android和IOS最新框架机制都有相应类似的处理。例如swiftUI,Compose。并且Vue,Flutter整个刷新机制就是如此。
众所周知,在HarmonyOs的list组件渲染中,如果使用的是V1状态装饰器。如果数据源列表对象是多个对象嵌套的处理,那最底层对象的属性更新时,UI界面是不会渲染的。因为检测不到,目前只能检测到第一层对象。
二、代码实现和详细解释
针对多层对象嵌套,底层对象属性修改后,UI不渲染的问题,有个比较简单又方便的处理方式,思路仅供参考。
即:深拷贝修改的item对象。
这样整个对象相当于都变化了,就符合第一层对象检测的机制,可以被系统捕获到刷新。
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;
}
}
/**
*
*/
@Entry
@Component
struct ForeachPage {
private TAG: string = "ForeachPage";
@State 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));
}
@Builder 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) })
}
}
更多关于HarmonyOS鸿蒙Next应用V1状态装饰器使用List组件多层对象嵌套ForEach渲染更新的处理的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS Next中,使用@State装饰器管理多层嵌套对象时,ForEach渲染更新需注意数据结构的响应式处理。若直接修改嵌套对象的属性,UI可能不会自动刷新。建议使用@Observed装饰器标记嵌套对象的类,并在其属性上使用@ObjectLink或@Prop装饰器来建立响应式关联。这样,当嵌套对象的属性发生变化时,ForEach会触发组件更新。确保数据源为数组且提供唯一键值生成函数。
在HarmonyOS Next中,使用V1状态装饰器(如@State)结合List组件进行多层对象嵌套的ForEach渲染更新时,关键在于确保状态变化的可观测性,以驱动UI正确刷新。
核心原则:@State装饰的变量本身是响应式的,但其嵌套对象内部属性的直接修改可能无法被UI层观测到,从而导致ForEach不更新。
典型场景与解决方案
假设数据结构为:
@State class Group {
name: string = '';
items: Item[] = []; // Item也是一个类
}
@State class Item {
id: number = 0;
text: string = '';
}
@State groups: Group[] = [];
在List中嵌套使用ForEach渲染groups和每个group.items。
1. 更新整个数组(替换引用)
最直接的方式是替换整个数组引用,这会触发最彻底的UI刷新。
// 添加一个新组
this.groups = [...this.groups, new Group(...)];
// 或者修改后重新赋值
this.groups[index].items.push(new Item(...));
this.groups = [...this.groups]; // 关键:创建新数组以触发更新
2. 更新嵌套对象的属性
若只修改深层属性(如item.text),需确保修改操作能被观测:
- 对于
@State装饰的类:直接修改其属性(如this.groups[index].items[i].text = 'new')通常可以触发更新,因为V1装饰器对类实例提供了深度的响应式支持。但为了绝对可靠,尤其是在多层嵌套下,建议在修改后通知父层状态变化:this.groups[index].items[i].text = 'new'; this.groups = [...this.groups]; // 通过父数组引用的变化触发更新 - 使用
@Observed和@ObjectLink:对于更复杂的嵌套对象,这是V1中推荐的深度观测方案。- 用
@Observed装饰嵌套的类(如Group和Item)。 - 在父组件中,使用
@ObjectLink装饰接收嵌套对象的变量(而非@State)。 此组合允许UI直接观测到嵌套对象内部属性的变化,无需替换上层引用。但请注意,@ObjectLink必须用于对象属性,且通常与@Observed配合使用。
- 用
3. 使用唯一标识键
在ForEach中,为每个项目提供稳定且唯一的key(如item.id)至关重要,这能帮助ArkUI框架准确识别项目的增、删、改,从而高效更新DOM。
总结建议
- 优先考虑替换数组引用:对于多层嵌套,在修改深层数据后,直接替换最顶层数组的引用(
this.groups = [...this.groups])是最简单、最可靠的更新触发方式。 - 复杂场景使用
@Observed/@ObjectLink:如果嵌套层次很深,且更新频繁,考虑使用@Observed和@ObjectLink实现精细化的响应式更新。 - 避免在
ForEach内直接修改状态:状态修改应集中在组件的方法中,保证数据流的清晰。
通过上述方法,可以确保多层嵌套对象在List的ForEach中渲染时,状态更新能正确反映到UI界面。

