HarmonyOS 状态管理问题

怎么在外部的onclick事件中修改JhkDeviceItem中的switch状态。

业务场景:组件内的switch点击后,外部业务处理失败后,需要将switch状态重置回去。

具体代码如下:

import { JuColor } from '../../color/colors'
import { ResManager } from '../../resources_manager/ResManager'

export enum RightType {
  // 胶囊按钮
  capsuleButton,
  // 箭头按钮
  arrowButton,
  // 开关按钮
  switchButton,
}

interface ImageInfo {
  url: string;
  errorImageUrl?: String|null;
  placeholderImageUrl?: String|null;
}

export interface ItemInfo {
  image?:ImageInfo|null;
  title: string;
  description: string;
  rightWidgetType: RightType;
  // capsuleButton按钮文字
  buttonText?: string;
  // switch组件状态
  isOn?: boolean|null;
  // 右侧组件点击回调
  onClick?: ((type: RightType, data?: ItemInfo|null) => void)|null
}

@Component
export struct JhkDeviceItem {
  @State
  itemInfo: ItemInfo|undefined = {
    title: '',
    description: '',
    rightWidgetType: RightType.arrowButton
  };

  aboutToAppear(): void {
    this.itemInfo ??= {
      title: '',
      description: '',
      rightWidgetType: RightType.arrowButton
    }
  }

  @Builder
  image() {
    if (this.itemInfo?.image) {
      Image(this.itemInfo?.image?.url)
        .objectFit(ImageFit.Contain)
        .width('44lpx')
        .height('44lpx')
      Blank('11lpx')
    }
  }

  @Builder
  rightWidget() {
    if (this.itemInfo?.rightWidgetType === RightType.capsuleButton) {
      Button(this.itemInfo?.buttonText, {
        type: ButtonType.Normal,
        stateEffect: true,
      })
        .backgroundColor(JuColor.ff13bdc0)
        .fontSize('12lpx')
        .fontWeight(FontWeight.Regular)
        .fontColor(Color.White)
        .width('54lpx')
        .height('23lpx')
        .borderRadius('23lpx')
    } else if (this.itemInfo?.rightWidgetType === RightType.arrowButton) {
      Image(ResManager.getMediaPic('arrow_right'))
        .objectFit(ImageFit.Contain)
        .width('10lpx')
        .height('16lpx')
    } else if (this.itemInfo?.rightWidgetType === RightType.switchButton) {
      Toggle({
        type: ToggleType.Switch,
        isOn: this.itemInfo?.isOn ?? false,
      })
        .onChange((isOn: boolean) => {
          this.itemInfo!.isOn = isOn;
          this.itemInfo?.onClick?.(this.itemInfo?.rightWidgetType, this.itemInfo);
        })
        .selectedColor(JuColor.ff13bdc0)
        .width('37.5lpx')
        .height('19lpx')
    }
  }

  @Builder
  content() {
    Column(){
      Row(){
        Text(this.itemInfo?.title)
          .fontColor(JuColor.ff1a1a1a)
          .fontSize('15lpx')
          .fontWeight(FontWeight.Regular)
          .textAlign(TextAlign.Start)
          .maxLines(1)
          .textOverflow({
            overflow: TextOverflow.Ellipsis
          })
          .constraintSize({
            maxWidth: '216lpx'
          })
        Blank()
        this.rightWidget()
      }
      .width('100%')
      .alignItems(VerticalAlign.Center)

      Blank('8lpx')
      Text(this.itemInfo?.description)
        .fontColor(JuColor.ff666666)
        .fontSize('12lpx')
        .fontWeight(FontWeight.Regular)
        .textAlign(TextAlign.Start)
        .maxLines(2)
        .textOverflow({
          overflow: TextOverflow.Ellipsis
        })
        .constraintSize({
          maxWidth: '216lpx'
        })
    }
    .layoutWeight(1)
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Start)
  }

  build() {
    Stack(){
      Row(){
        this.image()
        this.content()
      }
      .justifyContent(FlexAlign.Start)
      .alignItems(VerticalAlign.Center)
      .padding({
        left: '15lpx',
        right: '15lpx',
        top: '16lpx',
        bottom: '16lpx',
      })
      .width('100%')
      // TODO: 将透明按钮放到指定位置
    }
    .width('347lpx')
    .borderRadius('12lpx')
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.

调用地方:

JhkDeviceItem({
  itemInfo: {
    image: {
      url: 'xxx',
    },
    title: '测试测试哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈',
    description: '哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈',
    rightWidgetType: RightType.switchButton,
    onClick: (type, data) => {
      console.log(`onClick ${data?.isOn}`)
      // 出现一些异常时候,重置isOn属性

      setTimeout(() => {
        data!.isOn = false;
      },
        2*1000
      )
    },
  }
})
  .backgroundColor(JuColor.c8013bdc0)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
HarmonyOS
2025-01-09 14:57:55
浏览
收藏 0
回答 1
待解决
回答 1
按赞同
/
按时间
shlp

可以使用@ObservedV2装饰器和@Trace装饰器来控制toggle开关,参考文档为:

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-new-observedv2-and-trace-V5

以下是参考demo:

// JhkDeviceItem.ets
export enum RightType {
  // 胶囊按钮
  capsuleButton,
  // 箭头按钮
  arrowButton,
  // 开关按钮
  switchButton,
}

export interface ImageInfo {
  url: string;
  errorImageUrl?: String | null;
  placeholderImageUrl?: String | null;
}

@ObservedV2
export class ItemInfo {
  image?: ImageInfo | null;
  title?: string;
  description?: string;
  rightWidgetType?: RightType;

  // capsuleButton按钮文字
  buttonText?: string;

  // switch组件状态
  @Trace isOn?: boolean;

  // 右侧组件点击回调
  onClick?: ((type: RightType,data?: ItemInfo | null) => void) | null

  constructor(image: ImageInfo | null, title: string, description: string, rightWidgetType: RightType, buttonText: string, isOn: boolean,onClick: ((type: RightType,data?: ItemInfo | null) => void) | null
  ) {
    this.image = image
    this.title = title
    this.description = description
    this.rightWidgetType = rightWidgetType
    this.buttonText = buttonText
    this.isOn = isOn
    this.onClick = onClick
  }
}

@Component
export struct JhkDeviceItem {
  private itemInfo: ItemInfo | undefined
  @State isOn:boolean = false;

  @Builder
  image() {
    if (this.itemInfo?.image) {
      Image(this.itemInfo?.image?.url)
        .objectFit(ImageFit.Contain)
        .width('44lpx')
        .height('44lpx')
      Blank('11lpx')
    }
  }

  @Builder
  rightWidget() {
    Text(`itemISon: ${this.itemInfo?.isOn}`)
    if (this.itemInfo?.rightWidgetType === RightType.capsuleButton) {
      Button(this.itemInfo?.buttonText, {
        type: ButtonType.Normal,
        stateEffect: true,
      })
        .backgroundColor('#ff13bdc0')
        .fontSize('12lpx')
        .fontWeight(FontWeight.Regular)
        .fontColor(Color.White)
        .width('54lpx')
        .height('23lpx')
        .borderRadius('23lpx')
    } else if (this.itemInfo?.rightWidgetType === RightType.arrowButton) {
      Image($r('app.media.startIcon'))
        .objectFit(ImageFit.Contain)
        .width('10lpx')
        .height('16lpx')
    } else if (this.itemInfo?.rightWidgetType === RightType.switchButton) {
      Toggle({
        type: ToggleType.Switch,
        isOn: this.itemInfo!.isOn,
      })
        .onChange((isOn: boolean) => {
          this.itemInfo!.isOn = isOn;
          let res = this.itemInfo?.rightWidgetType as RightType;
          this.itemInfo?.onClick?.(res, this.itemInfo);
        })
        .selectedColor('#ff13bdc0')
        .width('37.5lpx')
        .height('19lpx')
    }
  }

  //
  @Builder
  content() {
    Column() {
      Row() {
        Text(this.itemInfo?.title)
          .fontColor('#ff1a1a1a')
          .fontSize('15lpx')
          .fontWeight(FontWeight.Regular)
          .textAlign(TextAlign.Start)
          .maxLines(1)
          .textOverflow({
            overflow: TextOverflow.Ellipsis
          })
          .constraintSize({
            maxWidth: '216lpx'
          })
        Blank()
        this.rightWidget()
      }
      .width('100%')
      .alignItems(VerticalAlign.Center)

      Blank('8lpx')
      Text(this.itemInfo?.description)
        .fontColor('#ff666666')
        .fontSize('12lpx')
        .fontWeight(FontWeight.Regular)
        .textAlign(TextAlign.Start)
        .maxLines(2)
        .textOverflow({
          overflow: TextOverflow.Ellipsis
        })
        .constraintSize({
          maxWidth: '216lpx'
        })
    }
    .layoutWeight(1)
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Start)
  }

  build() {
    Stack() {
      Row() {
        this.image()
        this.content()
      }
      .justifyContent(FlexAlign.Start)
      .alignItems(VerticalAlign.Center)
      .padding({
        left: '15lpx',
        right: '15lpx',
        top: '16lpx',
        bottom: '16lpx',
      })
      .width('100%')

      // TODO: 将透明按钮放到指定位置
    }
    .width('347lpx')
    .borderRadius('12lpx')
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
// IndexPage.ets
import { JhkDeviceItem, RightType, ItemInfo, ImageInfo } from './JhkDeviceItem'

export class ItemInfoTemp {
  image?: ImageInfo | null;
  title?: string;
  description?: string;
  rightWidgetType?: RightType;
  buttonText?: string;
}

@Entry
@Component
struct IndexPage {
  @State respArr: ItemInfoTemp[] = [{
    image: {
      url: 'xxxx'
    },
    title: '1111',
    description: '111asdadasdas',
    rightWidgetType: RightType.switchButton,
    buttonText: 'asadadasasdasdasd',
  }, {
    image: {
      url: 'xxx'
    },
    title: '2222',
    description: '111asdadasdas',
    rightWidgetType: RightType.switchButton,
    buttonText: 'asadadasasdasdasd',
  }]

  @State arr: ItemInfo[] = []
  initData() {
    this.respArr.forEach((item:ItemInfo)=>{
      this.arr.push( new ItemInfo({
        url: item?.image?.url as string,
      }, item.title as string,item.description as string, item.rightWidgetType as RightType,
        item.buttonText as string, false, (type, data) => {
          this.changeData(data as ItemInfo)
        }))
    })
  }

  changeData(data: ItemInfo) {
    // 出现一些异常时候,重置isOn属性
    setTimeout(() => {
      data!.isOn = false;
    },
      2 * 1000
    )
  }

  aboutToAppear(): void {
    this.initData();
  }

  build() {
    Column() {
      List() {
        ForEach(this.arr, (item: ItemInfo) => {
          ListItem() {
            JhkDeviceItem({
              itemInfo: item
            })
              .backgroundColor('#c8013bdc0')
          }.onClick(() => {
            item.title = '修改的'
            item.isOn = !item.isOn;
          })
        })
      }
    }
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
分享
微博
QQ
微信
回复
2025-01-09 17:40:07
相关问题
状态管理相关问题,有人知道吗?
875浏览 • 1回复 待解决
HarmonyOS 状态管理咨询
558浏览 • 1回复 待解决
HarmonyOS 关于状态管理
390浏览 • 1回复 待解决
HarmonyOS 状态管理之ObjectLink的疑问
335浏览 • 1回复 待解决
HarmonyOS 界面管理问题
239浏览 • 1回复 待解决
HarmonyOS 状态同步问题
339浏览 • 1回复 待解决
HarmonyOS 路由页面管理问题
435浏览 • 1回复 待解决
HarmonyOS 父子组件状态问题
153浏览 • 1回复 待解决
HarmonyOS 变量状态同步问题
539浏览 • 1回复 待解决
HarmonyOS 关于@Watch监听状态问题
730浏览 • 1回复 待解决
HarmonyOS 相机会话管理相关问题
79浏览 • 1回复 待解决
HarmonyOS 嵌套Class状态观察问题
165浏览 • 1回复 待解决