系列:鸿蒙 HarmonyOS 6.1 新特性实战 · 第 34 篇

选择类组件是表单开发的基础。ArkUI 提供了 Checkbox(多选框)、Radio(单选按钮)和 Toggle(开关)三类组件,各自适合不同场景。本篇重点演示全选/反选逻辑、Radio 分组和 Toggle 三种外观。

运行效果

初始状态,Checkbox 列表和 Radio 组:

初始状态

全选状态和 Radio 选中效果:

交互后

一、Checkbox 全选模式

全选需要维护两个状态:列表项的选中数组和全选标志:

@State items: string[] = ['选项 A', '选项 B', '选项 C', '选项 D']
@State checked: boolean[] = [false, false, false, false]
@State allChecked: boolean = false

// 计算全选状态
get isAllChecked(): boolean {
  return this.checked.every(v => v)
}

// 全选/取消全选
toggleAll(): void {
  const val = !this.isAllChecked
  this.checked = this.items.map(() => val)
  this.allChecked = val
}

// 切换单项(必须创建新数组触发 @State 更新)
toggle(idx: number): void {
  const arr = this.checked.slice()
  arr[idx] = !arr[idx]
  this.checked = arr
  this.allChecked = arr.every(v => v)
}

关键:ArkTS 的 @State 数组必须赋值新数组才触发 UI 刷新,直接修改元素 this.checked[i] = true 不会更新界面。

// UI 渲染
Checkbox({ name: 'all', group: 'all' })
  .select(this.allChecked)
  .selectedColor('#0066ff')
  .onChange(() => { this.toggleAll() })

ForEach(this.items, (item: string, idx: number) => {
  Row({ space: 8 }) {
    Checkbox({ name: item, group: 'items' })
      .select(this.checked[idx])
      .selectedColor('#0066ff')
      .onChange(() => { this.toggle(idx) })
    Text(item).fontSize(14)
  }
})

二、Radio 单选组

Radio 按 group 属性自动互斥,不需要手动管理:

@State selectedFruit: string = 'apple'

// 三个 Radio 共享 group='fruit'
Row({ space: 8 }) {
  Radio({ value: 'apple', group: 'fruit' })
    .checked(this.selectedFruit === 'apple')
    .selectedColor('#0066ff')
    .onChange((isOn: boolean) => { if (isOn) this.selectedFruit = 'apple' })
  Text('苹果').fontSize(14)
}

Row({ space: 8 }) {
  Radio({ value: 'banana', group: 'fruit' })
    .checked(this.selectedFruit === 'banana')
    .selectedColor('#0066ff')
    .onChange((isOn: boolean) => { if (isOn) this.selectedFruit = 'banana' })
  Text('香蕉').fontSize(14)
}

最佳实践:用 ForEach 遍历选项数组,避免重复代码:

private fruits: string[] = ['苹果', '香蕉', '橙子']
private fruitValues: string[] = ['apple', 'banana', 'orange']

ForEach(this.fruits, (fruit: string, idx: number) => {
  Row({ space: 8 }) {
    Radio({ value: this.fruitValues[idx], group: 'fruit' })
      .checked(this.selectedFruit === this.fruitValues[idx])
      .onChange((isOn: boolean) => {
        if (isOn) this.selectedFruit = this.fruitValues[idx]
      })
    Text(fruit).fontSize(14)
  }
})

三、Toggle 三种外观

Toggletype 属性控制外观形式:

// Switch:开关(最常用,用于设置项)
Toggle({ type: ToggleType.Switch, isOn: this.switchOn })
  .selectedColor('#0066ff')
  .onChange((val: boolean) => { this.switchOn = val })

// Button:按钮式开关(用于功能切换)
Toggle({ type: ToggleType.Button, isOn: this.btnOn }) {
  Text('蓝牙').fontSize(14)
    .fontColor(this.btnOn ? '#fff' : '#333')
}
.selectedColor('#0066ff')
.onChange((val: boolean) => { this.btnOn = val })

// Checkbox:复选框样式
Toggle({ type: ToggleType.Checkbox, isOn: this.cbOn })
  .selectedColor('#0066ff')
  .onChange((val: boolean) => { this.cbOn = val })

完整代码

@Entry
@Component
struct Index {
  @State items: string[] = ['选项 A', '选项 B', '选项 C', '选项 D']
  @State checked: boolean[] = [false, false, false, false]
  @State allChecked: boolean = false
  @State selectedFruit: string = 'apple'
  @State switchOn: boolean = true
  @State btnOn: boolean = false
  @State cbOn: boolean = false

  private fruits: string[] = ['苹果', '香蕉', '橙子', '葡萄']
  private fruitValues: string[] = ['apple', 'banana', 'orange', 'grape']

  toggleAll(): void {
    const val = !this.checked.every(v => v)
    this.checked = this.items.map(() => val)
    this.allChecked = val
  }

  toggle(idx: number): void {
    const arr = this.checked.slice()
    arr[idx] = !arr[idx]
    this.checked = arr
    this.allChecked = arr.every(v => v)
  }

  build() {
    Scroll() {
      Column({ space: 20 }) {
        Text('Checkbox / Radio / Toggle 选择组件')
          .fontSize(21).fontWeight(FontWeight.Bold).fontColor('#1a1a1a')

        // Checkbox 全选
        Column({ space: 10 }) {
          Text('一、Checkbox 全选模式').fontSize(15).fontWeight(FontWeight.Bold).fontColor('#333')
          Row({ space: 8 }) {
            Checkbox({ name: 'all', group: 'all' })
              .select(this.allChecked)
              .selectedColor('#0066ff')
              .onChange(() => { this.toggleAll() })
            Text('全选').fontSize(14).fontWeight(FontWeight.Medium).fontColor('#333')
          }
          .alignItems(VerticalAlign.Center)

          ForEach(this.items, (item: string, idx: number) => {
            Row({ space: 8 }) {
              Checkbox({ name: item, group: 'items' })
                .select(this.checked[idx])
                .selectedColor('#0066ff')
                .onChange(() => { this.toggle(idx) })
              Text(item).fontSize(14).fontColor('#555')
            }
            .alignItems(VerticalAlign.Center)
          })

          Text('已选:' + this.items.filter((_: string, i: number) => this.checked[i]).join('、'))
            .fontSize(13).fontColor('#0066ff')
        }
        .padding(12).backgroundColor('#fff').borderRadius(8).width('100%')
        .border({ width: 1, color: '#e0e0e0' }).alignItems(HorizontalAlign.Start)

        // Radio
        Column({ space: 10 }) {
          Text('二、Radio 单选组').fontSize(15).fontWeight(FontWeight.Bold).fontColor('#333')
          ForEach(this.fruits, (fruit: string, idx: number) => {
            Row({ space: 8 }) {
              Radio({ value: this.fruitValues[idx], group: 'fruit' })
                .checked(this.selectedFruit === this.fruitValues[idx])
                .selectedColor('#0066ff')
                .onChange((isOn: boolean) => {
                  if (isOn) { this.selectedFruit = this.fruitValues[idx] }
                })
              Text(fruit).fontSize(14).fontColor('#555')
            }
            .alignItems(VerticalAlign.Center)
          })
          Text('已选:' + this.fruits[this.fruitValues.indexOf(this.selectedFruit)])
            .fontSize(13).fontColor('#0066ff')
        }
        .padding(12).backgroundColor('#fff').borderRadius(8).width('100%')
        .border({ width: 1, color: '#e0e0e0' }).alignItems(HorizontalAlign.Start)

        // Toggle
        Column({ space: 10 }) {
          Text('三、Toggle 三种外观').fontSize(15).fontWeight(FontWeight.Bold).fontColor('#333')
          Row({ space: 16 }) {
            Text('Switch:').fontSize(13).fontColor('#555').width(70)
            Toggle({ type: ToggleType.Switch, isOn: this.switchOn })
              .selectedColor('#0066ff')
              .onChange((val: boolean) => { this.switchOn = val })
            Text(this.switchOn ? '开' : '关').fontSize(13)
              .fontColor(this.switchOn ? '#0066ff' : '#aaa')
          }
          .alignItems(VerticalAlign.Center)

          Row({ space: 16 }) {
            Text('Button:').fontSize(13).fontColor('#555').width(70)
            Toggle({ type: ToggleType.Button, isOn: this.btnOn }) {
              Text('蓝牙').fontSize(13)
                .fontColor(this.btnOn ? '#fff' : '#333').padding({ left: 12, right: 12 })
            }
            .selectedColor('#0066ff')
            .onChange((val: boolean) => { this.btnOn = val })
          }
          .alignItems(VerticalAlign.Center)

          Row({ space: 16 }) {
            Text('Checkbox:').fontSize(13).fontColor('#555').width(70)
            Toggle({ type: ToggleType.Checkbox, isOn: this.cbOn })
              .selectedColor('#0066ff')
              .onChange((val: boolean) => { this.cbOn = val })
          }
          .alignItems(VerticalAlign.Center)
        }
        .padding(12).backgroundColor('#fff').borderRadius(8).width('100%')
        .border({ width: 1, color: '#e0e0e0' }).alignItems(HorizontalAlign.Start)
      }
      .padding(20).width('100%')
    }
    .width('100%').height('100%').backgroundColor('#f5f5f5')
  }
}

API 速查

组件/属性 说明
Checkbox({ name, group }) 复选框,同 group 的 Checkbox 联动
.select(bool) 设置选中状态
.selectedColor('#0066ff') 选中颜色
Radio({ value, group }) 单选,同 group 自动互斥
.checked(bool) 初始选中状态
Toggle({ type, isOn }) 开关,type 为 Switch/Button/Checkbox
.onChange((val)=>{}) 状态变化回调

小结

  • @State 数组更新要赋新引用this.checked = arr.slice() 而不是直接 this.checked[i] = val
  • Radio 靠 group 自动互斥:不需要手动取消其他 Radio
  • Toggle Switch 是设置页首选:右对齐 Toggle.Switch + 左侧说明文字,是 HarmonyOS 设置页的标准模式
  • 全选逻辑:every() 判断,整体赋值:不要遍历修改,要新数组整体赋值

上一篇:Button 组件深度解析 | 下一篇:Slider 与 Rating 滑动评分组件

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐