HarmonyOS 鸿蒙Next:Observed类中的数组被修改,其对应的UI无法刷新?

发布于 1周前 作者 eggper 来自 鸿蒙OS

HarmonyOS 鸿蒙Next:Observed类中的数组被修改,其对应的UI无法刷新?

基本逻辑:现在业务中,有个属性是被选中的月份数组(假设为month),month的值显示在控件A中,点击A之后弹出sheet可以修改month,其变化会映射到控件A中。这个month是放在一个被@Observed装饰的类BestTime中的,子控件A中通过ObjectLink接收BestTime的对象

问题:当我直接修改month的时候,控件A中的内容不会更新;如果修改了BestTime中其他基础类型属性(如某个字符串),A中month的内容才会更新。

求助:有什么办法在修改month后,将变化直接传递到A吗

代码如下:

[@Observed](/user/Observed)
export class BestTime {
  month: number[];  // 需要被修改的属性
  timeInDay: string[];

  constructor(month: number[], timeInDay: string[]) {
    this.month = month;
    this.timeInDay = timeInDay;
  }
}

// 承载month属性的控件
@Component
export struct A {
  @ObjectLink bestTime: BestTime
  @State private sheetState: boolean = false

  @Builder
  private buildEditBestTimeSheet() {
    EditBestTimeSheet({ bestTime: this.bestTime })
  }

  build() {
    Column({ space: PaddingValue.small }) {
      Text('选择月份')
      Row() {
        // 显示被选中的月份。
        // 此处不会随month被修改而变化
        Text(JSON.stringify(this.bestTime.month)).layoutWeight(SizeValue.fullWeight)
        RightButton()
      }
      .width(SizeValue.fullWidth)
      .backgroundColor(colorBgPrimary)
      .padding(PaddingValue.small)
      .borderRadius(RadiusValue.small)
      .bindSheet($$this.sheetState, this.buildEditBestTimeSheet,
        bottomSheetOptionWithTitle('选择月份'))
      .onClick(() => this.sheetState = true)

      // 使用列表形式也不会跟随变化
      List(){
        ForEach(
          this.bestTime.month,
          (item: number) => {
            ListItem() {
              Text(item.toString())
            }
          }
        )
      }.width(SizeValue.fullWidth)
      .height(32)
    }
    .width(SizeValue.fullWidth)
    .alignItems(HorizontalAlign.Start)
    .padding(PaddingValue.horizontalNormalPadding)
  }
}

// 点击控件A之后弹出的sheet
@Component
export struct EditBestTimeSheet {
  @ObjectLink bestTime: BestTime
  private monthList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

  // 修改月份
  private selectItem(item: number, isOn: boolean) {
    const current = this.bestTime.month
    const index = current.indexOf(item)
    if (index < 0 && isOn) {
      current.push(item)
      this.bestTime.month = current
      return
    }
    if (index >= 0 && !isOn) {
      current.slice(index, 1)
      this.bestTime.month = current
      return
    }
  }

  build() {
    Column({ space: PaddingValue.small }) {
      Text('选择月份')
      // 月份选择器
      Flex({ wrap: FlexWrap.Wrap, space: { main: LengthMetrics.vp(PaddingValue.small) } }) {
        ForEach(
          this.monthList,
          (item: number) => {
            Toggle({ type: ToggleType.Button, isOn: this.bestTime.month.indexOf(item) >= 0 }) {
              Text(item.toString())
                .fontColor(this.bestTime.month.indexOf(item) >= 0 ? colorBgPrimary : colorContent)
                .fontSize(12)
            }.padding(PaddingValue.small)
            .selectedColor(colorPrimary)
            .onChange((isOn: boolean) => {
              console.log(`select ${item}, state: ${isOn}`)
              this.selectItem(item, isOn)
            })
          },
          (item: number) => item.toString()
        )
      }
    }
    .padding(PaddingValue.horizontalNormalPadding)
  }
}

更多关于HarmonyOS 鸿蒙Next:Observed类中的数组被修改,其对应的UI无法刷新?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html

12 回复

定义一个@Observed 修饰的数组。extends Array<Object>

更多关于HarmonyOS 鸿蒙Next:Observed类中的数组被修改,其对应的UI无法刷新?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


但是把弹窗的ui抽象成一个新的Component,把Observed类传递到这个新的Component,在弹窗中修改属性后可以单向同步回父控件内,无法更新弹窗内的ui。也就是说Observed的双向同步无法在bindSheet绑定的新Component中生效,只有把弹窗的ui和被绑定了bindSheet的ui放在同一个Component中共享Observed才可以双向同步,请问这个问题有就解决办法吗

解决了,在buildSheet的绑定中添加一层Column包裹到ui就可以了:

解决了,在buildSheet的绑定中添加一层Column包裹到ui就可以了:

原生的数组修改数组属性时是不会被监听的,ui也就不会被刷新。可以按照二楼的方法解决,或者仔细看下开发文档

但是把弹窗的ui抽象成一个新的Component,把Observed类传递到这个新的Component,在弹窗中修改属性后可以单向同步回父控件内,无法更新弹窗内的ui。也就是说Observed的双向同步无法在bindSheet绑定的新Component中生效,只有把弹窗的ui和被绑定了bindSheet的ui放在同一个Component中共享Observed才可以双向同步,请问这个问题有就解决办法吗

解决了,在buildSheet的绑定中添加一层Column包裹到ui就可以了:

解决了,在buildSheet的绑定中添加一层Column包裹到ui就可以了:

实现一个数组

class ObservedArray<T> extends Array<T> {}

然后month的类型设置为ObservedArray,初始化时new一个ObservedArray,

但是把弹窗的ui抽象成一个新的Component,把Observed类传递到这个新的Component,在弹窗中修改属性后可以单向同步回父控件内,无法更新弹窗内的ui。也就是说Observed的双向同步无法在bindSheet绑定的新Component中生效,只有把弹窗的ui和被绑定了bindSheet的ui放在同一个Component中共享Observed才可以双向同步,请问这个问题有就解决办法吗

解决了,在buildSheet的绑定中添加一层Column包裹到ui就可以了:

@Builder private buildEditBestTimeSheet() { Column(){ EditBestTimeSheet({ bestTime: this.bestTime }) } }

不是很清楚,这是我之前回复过的帖子,可以看下是否符合你的需求

https://developer.huawei.com/consumer/cn/forum/topic/0201151871879600407?fid=0109140870620153026&pid=0308151962752967581

可以按照楼上的方法解决,新建一个带Observed类继承Array。或者直接创建新数组传递给Observed中的属性也可以。

在HarmonyOS(鸿蒙)开发中,若Observed类中的数组被修改后,UI没有刷新,这通常是因为数据绑定机制未能正确检测到数组内容的变化。HarmonyOS的数据绑定依赖于Observable对象的属性变化通知。对于数组或列表类型的数据,直接修改其内容(如通过索引赋值)通常不会触发属性变更通知,因为数组对象本身的引用没有改变。

解决这一问题的方法通常涉及以下几点:

  1. 使用ObservableList或类似容器:HarmonyOS可能提供了特定的容器类(如ObservableList),用于自动检测内容变化并通知UI更新。检查文档并考虑使用这些类代替普通数组。

  2. 手动通知变更:如果使用的是普通数组,当数组内容变化时,需要手动调用Observable对象的notifyPropertyChanged方法(或类似机制),并传递正确的属性名以触发UI刷新。

  3. 替换整个数组:作为替代方案,当数组内容需要更新时,可以创建一个新数组并替换旧数组。这将触发对象引用的变化,从而可能触发UI更新。

如果问题依旧没法解决请联系官网客服,官网地址是 https://www.itying.com/category-93-b0.html

回到顶部