HarmonyOS 鸿蒙Next 自定义弹窗,全选与list中的单个item选中

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

HarmonyOS 鸿蒙Next 自定义弹窗,全选与list中的单个item选中 目前实现了全选以及单个item的选中,但是使用watch触发不了,不能实现手动把每一个item都选中,然后全选框也选中,应该怎样写,是否还有其它方案

pollingDialogController: CustomDialogController | null = new CustomDialogController({
    builder: PlanPollingDialog(),
    alignment: DialogAlignment.TopEnd,
    autoCancel: false,
    customStyle: false,
    width: '40%',
    cornerRadius: 12,
    maskColor: 0x66000000,
    backgroundBlurStyle: BlurStyle.NONE,
    offset: {dx: -50, dy: 80}
})
@Observed
class PlanListItem {
  plan: number
  checked: boolean

  constructor(plan: number, checked: boolean) {
    this.plan = plan
    this.checked = checked
  }
}

@Component
struct PlanView {
  @Link isAllSelected: boolean
  @ObjectLink item: PlanListItem // 改为直接传递item对象

  build() {
    Row() {
      Checkbox()
        .shape(CheckBoxShape.ROUNDED_SQUARE)
        .onChange((checked: boolean) => {
          this.item.checked = checked
        })
        .select(this.item.checked)
        .width(20)
        .height(20)
      Text(this.item.plan.toString())
        .fontColor(Color.White)
        .margin({left: 8})
    }
    .width('100%')
    .padding(8)
    .alignItems(VerticalAlign.Center)
  }
}

@CustomDialog
struct PlanPollingDialog {
  @State isPressed: boolean = false
  @State pollingTime: number = 10

  @State
  @Watch('checkAllSelected')
  planList: Array<PlanListItem> = [
    new PlanListItem(1, true),
    new PlanListItem(2, false),
    new PlanListItem(3, false),
  ]

  // 检查是否全选
  checkAllSelected() {
    console.log('checkAllSelected')
    this.isAllSelected = this.planList.every(item => item.checked)
  }

  @State
  isAllSelected: boolean = false
  controller?: CustomDialogController
  cancel: () => void = () => {}
  confirm: () => void = () => {}

  aboutToAppear() {
    // 初始化时检查是否全选
    this.checkAllSelected()
  }

  // 全选方法
  handleSelectAll(checked: boolean) {
    this.isAllSelected = checked
    this.planList.forEach(item => {
      item.checked = checked
    })
  }

  build() {
    Column() {
      // 标题栏
      Row() {
        Text('1111111111111111')
          .fontColor(Color.White)
          .fontSize(16)
        Blank()
        this.closeButton()
      }
      .width('100%')
      .borderRadius({topLeft: 8, topRight: 8})
      .backgroundColor('#11386F')
      .padding(16)

      // 内容区
      Column() {
        // 预案列表
        List() {
          ForEach(this.planList, (item: PlanListItem) => {
            ListItem() {
              PlanView({
                item: item,
                isAllSelected: $isAllSelected
              })
            }
            .onClick(() => {
              item.checked = !item.checked
              this.checkAllSelected()
            })
          })
        }
        .height(300)

        // 底部控制区
        Row() {
          // 全选
          Row() {
            Checkbox()
              .shape(CheckBoxShape.ROUNDED_SQUARE)
              .onChange((checked: boolean) => {
                this.handleSelectAll(checked)
              })
              .select(this.isAllSelected)
              .width(20)
              .height(20)
            Text('全选')
              .fontColor(Color.White)
              .margin({left: 8})
          }

          // 间隔时间输入
          Row() {
            Text('间隔时间(秒):')
              .fontColor(Color.White)
              .margin({left: 16})

            TextInput({
              text: this.pollingTime.toString()
            })
              .border({width: 0.5, color: '#33A1C9'})
              .borderRadius(0)
              .height(32)
              .width(60)
              .fontColor(Color.White)
              .backgroundColor('transparent')
              .type(InputType.Number)
              .onChange((value: string) => {
                this.pollingTime = parseInt(value) || 0
              })
          }

          // 开始按钮
          Button('开始', {type: ButtonType.Normal})
            .height(32)
            .backgroundColor('#3785C2')
            .borderRadius(8)
            .fontColor(Color.White)
            .margin({left: 16})
            .onClick(() => {
              const selectedPlans = this.planList.filter(item => item.checked)
              if (selectedPlans.length === 0) {
                // TODO:
                return
              }
              this.confirm()
            })
        }
        .width('100%')
        .alignItems(VerticalAlign.Center)
      }
      .width('100%')
      .alignItems(HorizontalAlign.Start)
      .borderRadius({bottomLeft: 8, bottomRight: 8})
      .backgroundColor('#95244D7A')
      .justifyContent(FlexAlign.Start)
      .padding(24)
    }
    .width('100%')
  }

  @Builder
  closeButton() {
    Image(this.isPressed ? $r('app.media.ic_close_pressed') : $r('app.media.ic_close'))
      .width(px2vp(29))
      .height(px2vp(29))
      .onTouch((event: TouchEvent) => {
        if (event?.type === TouchType.Down) {
          this.isPressed = true
        }
        if (event?.type === TouchType.Up) {
          this.isPressed = false
        }
      })
      .onClick(() => {
        this.controller?.close()
      })
  }
}

更多关于HarmonyOS 鸿蒙Next 自定义弹窗,全选与list中的单个item选中的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html

5 回复

可以看一下关于@Observed和@ObjectLink装饰器关于对象数组这一使用场景其中this.arrA[Math.floor(this.arrA.length/2)].info这一使用方式,对于这种在子组件中数组中对象属性直接赋值的方式只能触发对象的刷新,但无法触发父组件中数组的刷新,这种方式其实又加了一层嵌套,因此在父组件中无法被观测到,只能触发子组件的刷新。

有以下几种方法可供参考:

①传递index和planList替代直接传递item,并在复选框变更时创建新的PlanListItem实例赋值,参考如下

// 列表
ForEach(this.planList, (item: PlanListItem, index: number) => {
  ListItem() {
    PlanView({
      planList: this.planList,
      isAllSelected: this.isAllSelected,
      index: index,
    })
      .onClick(() => {
        item.checked = !item.checked
        this.checkAllSelected()
      })
  }
})

// PlanView
@Component
struct PlanView {
  @Link isAllSelected: boolean
  index: number = 0;
  @Link planList: Array<PlanListItem>

  build() {
    Row() {
      Checkbox()
        .shape(CheckBoxShape.ROUNDED_SQUARE)
        .onChange((checked: boolean) => {
          let plan = this.planList[this.index].plan
          this.planList[this.index] = new PlanListItem(plan, checked)
        })
        .select(this.planList[this.index].checked)
        .width(20)
        .height(20)
      Text(this.planList[this.index].plan.toString())
    }
    .width('100%')
    .padding(8)
    .alignItems(VerticalAlign.Center)
  }
}

②父组件事件checkAllSelected()传递给子组件供子组件调用,传递方法参考这里

③只响应自定义组件PlanView的触摸事件,不响应其中CheckBox的触摸事件(慎用,在组件间相互无遮挡,且父组件无点击需要的情况下),参考这里,修改如下

ForEach(this.planList, (item: PlanListItem) => {
  ListItem() {
    PlanView({
      item: item,
      isAllSelected: this.isAllSelected,
    })
      .onClick(() => {
        item.checked = !item.checked
        this.checkAllSelected()
      })
      .hitTestBehavior(HitTestMode.Block)
  }
})

另外,我发现你在全选复选框onChange()的时候执行了handleSelectAll(),这会导致在全选后再点击其中一项单选时触发全选的onChange(),从而所有单选项都被取消,而非只取消其中一项,可以将全选的onChange()改成onClick()或者在handleSelectAll()中增加判断来修正

更多关于HarmonyOS 鸿蒙Next 自定义弹窗,全选与list中的单个item选中的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


解决了这两个问题,

另外我想请问下,@State修饰的对象数组貌似也只能通过 this.planList[this.index] = new PlanListItem(plan, checked),即new去更新,是否有其它方式,这种更新会不会更耗时?

如果是频繁的修改或者复杂场景,这种赋值方式是会对性能有所损耗的,要看具体场景吧,像你使用的这种场景,对性能的影响可以忽略不计,且还有其他方式可选择,其他场景也可以具体问题具体分析的,

在HarmonyOS鸿蒙Next系统中,自定义弹窗并实现全选以及列表中单个item的选中功能,可以通过以下方式实现:

  1. 自定义弹窗

    • 使用AbilitySliceDialog组件来创建自定义弹窗。
    • 在弹窗的布局文件中,设计包含全选按钮和列表项的UI。
  2. 全选功能

    • 为全选按钮设置点击事件监听器。
    • 在监听器中,遍历列表项,将每个列表项的选中状态设置为选中。
  3. 单个item选中

    • 为列表项设置点击事件监听器。
    • 在监听器中,根据点击的列表项,更新其选中状态。
    • 如果需要,还可以实现反选功能,即点击已选中的列表项时取消选中。
  4. 数据绑定

    • 使用双向数据绑定(如通过MVVM模式)来同步UI和数据模型的状态。
    • 这样可以确保当数据模型中的选中状态发生变化时,UI能够自动更新。
  5. 更新UI

    • 在数据模型变化后,通过调用UI更新方法(如setChecked)来刷新列表项的选中状态。

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

回到顶部