在本篇案例中,我们将学习如何在HarmonyOS NEXT中创建自定义外观的复选框,以及如何处理复选框的禁用状态,为用户提供更丰富的交互体验。

案例目标

  1. 学习如何创建完全自定义外观的复选框
  2. 掌握复选框禁用状态的处理方法
  3. 实现带动画效果的复选框
  4. 了解不同状态下的样式切换

代码实现

1. 自定义外观复选框

以下是一个完全自定义外观的复选框实现,使用自定义图标替代默认的勾选标记:

@Entry
@Component
struct CustomCheckboxDemo {
  @State isChecked1: boolean = false
  @State isChecked2: boolean = false
  @State isChecked3: boolean = false
  
  build() {
    Column({ space: 30 }) {
      // 标题
      Text('自定义外观复选框')
        .fontSize(22)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 20 })
      
      // 自定义图标复选框
      Row({ space: 10 }) {
        // 使用自定义组件实现复选框
        this.CustomIconCheckbox({
          checked: $isChecked1,
          icon: '❤️',  // 心形图标
          text: '收藏',
          color: '#FF4081'
        })
      }
      .width('100%')
      .padding(10)
      .backgroundColor('#F8F8F8')
      .borderRadius(8)
      
      // 星形评分复选框
      Row({ space: 10 }) {
        // 使用自定义组件实现复选框
        this.CustomIconCheckbox({
          checked: $isChecked2,
          icon: '⭐',  // 星形图标
          text: '收藏到我的最爱',
          color: '#FFC107'
        })
      }
      .width('100%')
      .padding(10)
      .backgroundColor('#F8F8F8')
      .borderRadius(8)
      
      // 自定义形状复选框
      Row({ space: 10 }) {
        // 使用自定义组件实现复选框
        this.CustomShapeCheckbox({
          checked: $isChecked3,
          text: '同意用户协议',
          color: '#4CAF50'
        })
      }
      .width('100%')
      .padding(10)
      .backgroundColor('#F8F8F8')
      .borderRadius(8)
    }
    .width('100%')
    .height('100%')
    .padding(20)
  }
  
  @Builder
  CustomIconCheckbox({checked, icon, text, color}: {checked: Observed<boolean>, icon: string, text: string, color: string}) {
    Row({ space: 10 }) {
      // 自定义复选框容器
      Stack() {
        // 背景
        Rect()
          .width(28)
          .height(28)
          .radius(6)
          .fill(checked.get() ? color : '#FFFFFF')
          .stroke(color)
          .strokeWidth(2)
        
        // 图标
        if (checked.get()) {
          Text(icon)
            .fontSize(16)
            .fontColor('#FFFFFF')
        }
      }
      .width(28)
      .height(28)
      .onClick(() => {
        checked.set(!checked.get())
      })
      
      // 文本
      Text(text)
        .fontSize(16)
    }
    .width('100%')
    .alignItems(VerticalAlign.Center)
  }
  
  @Builder
  CustomShapeCheckbox({checked, text, color}: {checked: Observed<boolean>, text: string, color: string}) {
    Row({ space: 10 }) {
      // 自定义复选框容器
      Stack() {
        // 背景 - 使用六边形
        Polygon({
          points: [
            [14, 0], [28, 7], 
            [28, 21], [14, 28], 
            [0, 21], [0, 7]
          ]
        })
          .fill(checked.get() ? color : '#FFFFFF')
          .stroke(color)
          .strokeWidth(2)
        
        // 勾选标记
        if (checked.get()) {
          Path()
            .commands('M6 14 L12 20 L22 8')
            .stroke('#FFFFFF')
            .strokeWidth(2)
            .strokeLineCap(LineCapStyle.Round)
            .strokeLineJoin(LineJoinStyle.Round)
        }
      }
      .width(28)
      .height(28)
      .onClick(() => {
        checked.set(!checked.get())
      })
      
      // 文本
      Text(text)
        .fontSize(16)
    }
    .width('100%')
    .alignItems(VerticalAlign.Center)
  }
}

2. 禁用状态与动画效果

以下是一个实现禁用状态和动画效果的复选框示例:

@Entry
@Component
struct DisabledAndAnimatedCheckboxDemo {
  @State isChecked1: boolean = false
  @State isChecked2: boolean = true
  @State isDisabled1: boolean = true
  @State isDisabled2: boolean = true
  @State isChecked3: boolean = false
  
  build() {
    Column({ space: 30 }) {
      // 标题
      Text('禁用状态与动画效果')
        .fontSize(22)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 20 })
      
      // 禁用状态 - 未选中
      Row({ space: 10 }) {
        Checkbox()
          .select(this.isChecked1)
          .disabled(this.isDisabled1)
          .onChange((value: boolean) => {
            this.isChecked1 = value
          })
        
        Text(`禁用状态 - 未选中`)
          .fontSize(16)
          .opacity(this.isDisabled1 ? 0.5 : 1.0)  // 文本也显示禁用效果
      }
      .width('100%')
      .padding(10)
      .backgroundColor('#F8F8F8')
      .borderRadius(8)
      
      // 禁用状态 - 已选中
      Row({ space: 10 }) {
        Checkbox()
          .select(this.isChecked2)
          .disabled(this.isDisabled2)
          .onChange((value: boolean) => {
            this.isChecked2 = value
          })
        
        Text(`禁用状态 - 已选中`)
          .fontSize(16)
          .opacity(this.isDisabled2 ? 0.5 : 1.0)  // 文本也显示禁用效果
      }
      .width('100%')
      .padding(10)
      .backgroundColor('#F8F8F8')
      .borderRadius(8)
      
      // 切换禁用状态的按钮
      Row({ space: 20 }) {
        Button('启用/禁用复选框')
          .onClick(() => {
            this.isDisabled1 = !this.isDisabled1
            this.isDisabled2 = !this.isDisabled2
          })
      }
      .width('100%')
      .justifyContent(FlexAlign.Center)
      .margin({ bottom: 20 })
      
      // 带动画效果的复选框
      Text('带动画效果的复选框')
        .fontSize(18)
        .fontWeight(FontWeight.Medium)
        .margin({ bottom: 10 })
      
      Row({ space: 10 }) {
        // 自定义动画复选框
        this.AnimatedCheckbox({
          checked: $isChecked3,
          text: '带动画效果'
        })
      }
      .width('100%')
      .padding(10)
      .backgroundColor('#F8F8F8')
      .borderRadius(8)
    }
    .width('100%')
    .height('100%')
    .padding(20)
  }
  
  @Builder
  AnimatedCheckbox({checked, text}: {checked: Observed<boolean>, text: string}) {
    Row({ space: 10 }) {
      // 自定义动画复选框
      Stack() {
        // 背景
        Rect()
          .width(28)
          .height(28)
          .radius(6)
          .fill(checked.get() ? '#2196F3' : '#FFFFFF')
          .stroke('#2196F3')
          .strokeWidth(2)
          .animation({
            duration: 300,  // 动画持续时间
            curve: Curve.EaseOut,  // 动画曲线
            iterations: 1,  // 动画重复次数
            playMode: PlayMode.Normal  // 动画播放模式
          })
        
        // 勾选标记
        if (checked.get()) {
          Path()
            .commands('M6 14 L12 20 L22 8')
            .stroke('#FFFFFF')
            .strokeWidth(2)
            .strokeLineCap(LineCapStyle.Round)
            .strokeLineJoin(LineJoinStyle.Round)
            .opacity(0)
            .animation({
              duration: 300,
              curve: Curve.EaseOut,
              delay: 150,  // 延迟显示勾选标记
              iterations: 1,
              playMode: PlayMode.Normal,
              onFinish: () => {}
            })
            .opacity(1)  // 动画结束时的不透明度
        }
      }
      .width(28)
      .height(28)
      .onClick(() => {
        checked.set(!checked.get())
      })
      
      // 文本
      Text(text)
        .fontSize(16)
    }
    .width('100%')
    .alignItems(VerticalAlign.Center)
  }
}

关键点解析

1. 自定义外观复选框要点

  • 自定义组件:使用@Builder装饰器创建可复用的自定义复选框组件
  • 状态管理:使用Observed<boolean>类型参数传递和管理选中状态
  • 自定义图标:使用文本、图形或图标替代默认的勾选标记
  • 自定义形状:使用PolygonPath组件创建非矩形的复选框

2. 禁用状态与动画效果要点

  • 禁用状态:使用disabled属性设置复选框的禁用状态
  • 视觉反馈:通过降低不透明度等方式提供禁用状态的视觉反馈
  • 动画配置:使用animation属性配置动画效果
  • 动画时序:通过delay属性控制动画的时序,实现连贯的动画效果

应用场景

  1. 品牌定制:根据应用的品牌风格定制复选框外观
  2. 游戏界面:在游戏中使用主题化的复选框增强用户体验
  3. 特殊交互:在需要特殊交互反馈的场景中使用动画效果
  4. 表单验证:在表单验证中使用禁用状态表示某些选项不可用

注意事项

  1. 自定义复选框时,确保保留基本的交互逻辑和视觉反馈
  2. 动画效果应适度,避免过于复杂的动画影响用户体验
  3. 禁用状态应有明确的视觉区分,让用户能够识别
  4. 自定义组件应考虑无障碍设计,确保所有用户都能正常使用

小结

通过本案例,我们学习了如何在HarmonyOS NEXT中创建自定义外观的复选框,以及如何处理复选框的禁用状态和动画效果。这些技巧可以帮助我们创建更具个性化和交互性的用户界面,提升用户体验。

在下一篇案例中,我们将学习如何在实际应用场景中综合运用Checkbox组件,实现更复杂的业务需求。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐