HarmonyOS 弹框开发实战【2】

2025-06-25 20:09:16
107次阅读
0个评论

第二篇:自定义弹框的那些事

说到自定义弹框,这个真的是个双刃剑。功能强大,但是坑也多。我记得第一次写自定义弹框的时候,光是搞清楚CustomDialog和CustomDialogController的关系就花了不少时间。

CustomDialog其实就是个普通的组件,只不过加了个装饰器。刚开始我总是搞不清楚为什么要单独写个controller,后来才明白这是为了控制弹框的生命周期。controller就像是弹框的遥控器,你想什么时候显示就open(),想什么时候关闭就close()。

自定义弹框最大的好处就是灵活,想要什么样式就能做成什么样式。我做过一个输入弹框,需要实时验证用户输入的内容,系统弹框肯定做不了,只能自己写。但是自定义弹框也有个问题,就是容易做得太复杂。我见过有些开发者把弹框做得像个完整页面一样,内容特别多,用户根本不知道重点在哪里。

弹框的位置控制是个技术活。大部分情况下用DialogAlignment.Center就够了,但是有些特殊场景需要调整。比如我做过一个评论弹框,如果居中显示会被软键盘挡住,所以设置成DialogAlignment.Top,再加个offset往下偏移一点。底部的ActionSheet类弹框用DialogAlignment.Bottom效果比较好。

说到样式,这个真的很考验审美。我的经验是尽量保持简洁,不要用太多颜色。背景用白色或者浅灰色,重要按钮用主题色,取消按钮用灰色。圆角不要太大,8-12px比较合适。阴影也不要太重,淡一点就行。

动画效果能让弹框显得更自然。我一般用缩放动画,从0.8倍放大到1倍,时间控制在200-300ms。太快了显得突兀,太慢了用户会觉得卡顿。退场动画可以稍微快一点,150-200ms就够了。

@CustomDialog
struct MyInputDialog {
  @State inputText: string = ''
  @State errorMsg: string = ''
  controller: CustomDialogController
  onConfirm?: (text: string) => void

  // 验证输入内容
  validateInput(): boolean {
    if (this.inputText.trim().length === 0) {
      this.errorMsg = '内容不能为空'
      return false
    }
    if (this.inputText.length > 50) {
      this.errorMsg = '内容不能超过50个字符'
      return false
    }
    this.errorMsg = ''
    return true
  }

  build() {
    Column() {
      Text('请输入备注')
        .fontSize(18)
        .fontWeight(FontWeight.Medium)
        .margin({ bottom: 16 })
      
      TextInput({ placeholder: '请输入内容...' })
        .width('100%')
        .maxLength(50)
        .onChange((value: string) => {
          this.inputText = value
          // 输入时清除错误提示
          if (this.errorMsg) {
            this.errorMsg = ''
          }
        })
      
      // 错误提示
      if (this.errorMsg) {
        Text(this.errorMsg)
          .fontSize(12)
          .fontColor(Color.Red)
          .alignSelf(ItemAlign.Start)
          .margin({ top: 4 })
      }
      
      Row() {
        Button('取消')
          .backgroundColor(Color.Grey)
          .fontColor(Color.Black)
          .onClick(() => this.controller.close())
        
        Button('确定')
          .onClick(() => {
            if (this.validateInput()) {
              this.onConfirm?.(this.inputText)
              this.controller.close()
            }
          })
      }
      .justifyContent(FlexAlign.SpaceEvenly)
      .width('100%')
      .margin({ top: 20 })
    }
    .backgroundColor(Color.White)
    .borderRadius(12)
    .padding(20)
    .width('85%')
  }
}

状态管理是自定义弹框的一个难点。弹框内部的状态变化要及时反映到UI上,同时还要考虑与父组件的数据交互。我的做法是通过回调函数传递数据,这样比较清晰。复杂一点的弹框可能需要多个回调,比如确认回调、取消回调、数据变化回调等。

有个细节是弹框的焦点管理。用户打开弹框后,焦点应该自动定位到第一个可交互的元素上。如果是输入弹框,TextInput应该自动获得焦点。如果是确认弹框,确定按钮应该是默认焦点。这样用户可以直接用键盘操作,体验会好很多。

收藏00

登录 后评论。没有帐号? 注册 一个。