欢迎光临
我们一直在努力

HarmonyOS7 自定义弹窗别写成一坨:页面和弹窗要有边界

为文章《HarmonyOS7 自定义弹窗别写成一坨:页面和弹窗要有边界》绘制一张 sketch-no

文章目录

      • 前言
      • 为什么这个问题经常被写乱
      • 删除弹窗先定义契约
      • 先把页面和弹窗各自管什么想清楚
      • 完整 ArkUI 示例
      • 把关键代码一段段拆开
      • 真实项目里的补充
      • 新手最容易踩的坑
      • 放进真实项目还要补什么
      • 写在最后

前言

自定义弹窗最怕写成页面里的一大坨:标题、内容、按钮、关闭逻辑、业务提交全塞在一起。刚开始只是确认删除,后来加输入框、loading、错误提示,页面主体和弹窗逻辑就缠住了。

HarmonyOS7 里写 CustomDialog,我更建议先把边界定清楚:页面负责打开弹窗和处理结果,弹窗负责展示内容、收集用户动作、调用回调。

弹窗不是页面的临时补丁,它应该是一个清楚的交互边界。

为什么这个问题经常被写乱

自定义弹窗别写成一坨 这类内容很容易被写成“代码能跑就算讲完了”,但对初学者来说,这恰恰是最不够的地方。真正让人卡住的,往往不是某个组件名记不住,而是不知道这段代码为什么要这样拆、状态为什么要这样放、以后需求变化时应该从哪里改。

所以这篇文章不只想给你一个能跑的例子,更想把背后的判断过程讲清楚。你只要把这个判断过程吃透,后面自己改页面、补需求、查问题时,心里会稳很多。

删除弹窗先定义契约

为文章《HarmonyOS7 自定义弹窗别写成一坨:页面和弹窗要有边界》绘制一张 sketch-no

确认删除这种场景,至少要想清楚这些事:

问题放在哪里处理
删除的是哪个对象 页面保存目标 id 和名称
弹窗显示什么文案 弹窗接收对象名称
点击确认后谁删数据 页面通过回调处理
取消和关闭怎么走 弹窗自己关闭即可

为文章《HarmonyOS7 自定义弹窗别写成一坨:页面和弹窗要有边界》绘制一张 sketch-no

这样弹窗不会偷偷修改页面里一堆状态,页面也不用关心弹窗内部按钮怎么排。

弹窗组件最重要的不是样式,而是别越权。它可以触发动作,但不要拥有页面数据。

先把页面和弹窗各自管什么想清楚

写删除弹窗时,我通常先分两层:页面负责记住“现在删的是谁”,弹窗负责展示确认信息并把用户的选择抛回来。这样一来,页面不用知道弹窗内部按钮怎么排,弹窗也不用直接操作页面列表。

这个边界看起来像多想了一步,实际上是在提前避免后面的混乱。只要以后弹窗里再加输入框、风险提示、接口 loading,你都会明显感觉到:谁的状态该留在页面,谁的状态该留在弹窗,本来就不是一回事。

完整 ArkUI 示例

下面这个例子是一个文件列表,点击删除时打开自定义弹窗,确认后由页面删除对应文件。

interface FileItem {
id: number
name: string
}

@CustomDialog
struct DeleteFileDialog {
controller?: CustomDialogController
fileName: string = ''
onConfirm: () => void = () => {}

build() {
Column({ space: 14 }) {
Text('确认删除文件?')
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text(`删除“${this.fileName}”后,当前示例不会再显示它。`)
.fontSize(14)
.fontColor('#666666')
Row({ space: 10 }) {
Button('取消')
.layoutWeight(1)
.backgroundColor('#F0F2F5')
.fontColor('#333333')
.onClick(() => this.controller?.close())
Button('删除')
.layoutWeight(1)
.backgroundColor('#D32F2F')
.onClick(() => {
this.onConfirm()
this.controller?.close()
})
}
.width('100%')
}
.padding(20)
.backgroundColor(Color.White)
.borderRadius(16)
}
}

@Entry
@Component
struct DeleteDialogPage {
@State files: FileItem[] = [
{ id: 1, name: '需求说明.pdf' },
{ id: 2, name: '页面截图.png' },
{ id: 3, name: '接口草稿.md' }
]
@State targetId: number = 0
@State targetName: string = ''

private dialog: CustomDialogController = new CustomDialogController({
builder: DeleteFileDialog({
fileName: this.targetName,
onConfirm: () => {
this.files = this.files.filter((item: FileItem) => item.id !== this.targetId)
}
}),
alignment: DialogAlignment.Center
})

build() {
Column({ space: 10 }) {
Text('文件列表').fontSize(22).fontWeight(FontWeight.Bold).width('100%')
ForEach(this.files, (item: FileItem) => {
Row() {
Text(item.name).fontSize(16).layoutWeight(1)
Button('删除')
.height(32)
.backgroundColor('#FFF0F0')
.fontColor('#D32F2F')
.onClick(() => {
this.targetId = item.id
this.targetName = item.name
this.dialog.open()
})
}
.padding(14)
.backgroundColor(Color.White)
.borderRadius(10)
}, (item: FileItem) => item.id.toString())
}
.padding(16)
.backgroundColor('#F5F7FA')
}
}

把关键代码一段段拆开

DeleteFileDialog 只接收 fileName 和 onConfirm。它知道要显示哪个名字,也知道确认时要通知外部,但它不知道文件列表长什么样,更不会直接删数组。

controller?: CustomDialogController 用可选写法,关闭时通过 this.controller?.close() 调用。这样即使控制器暂时没有注入,也不会因为强行访问导致问题。

页面在打开弹窗前设置 targetId 和 targetName。这两个状态一个用于删除,一个用于展示。先设置再打开,弹窗内容和确认动作才能对上。

删除文件时使用 filter 生成新数组并赋回 this.files。这条状态更新路径很清楚,列表也能立刻反映变化。

真实项目里的补充

删除类弹窗最好明确对象名称,不要只写“确定删除吗”。用户要知道自己正在删什么,尤其是文件、订单、地址这类不可轻易恢复的数据。

危险按钮要和普通按钮区分开。示例里用红色背景表达风险,真实项目还可以加二次确认、loading 状态和失败提示。

如果确认动作需要请求接口,不建议在弹窗内部直接写网络请求。可以让页面处理接口,弹窗只负责触发确认。这样弹窗组件更容易复用,也更容易测试。

新手最容易踩的坑

这一类示例最容易让人产生错觉:界面出来了,就以为已经掌握了。其实真正容易出问题的地方,通常都在效果之外,比如状态有没有收拢、失败后怎么兜底、以后要扩展时会不会牵一发动全身。

所以你练这篇内容时,别只看“现在能不能跑”,还要继续看“以后好不好改”。能把这个习惯养起来,你写出来的页面会比单纯照着示例拼出来的页面稳很多。

放进真实项目还要补什么

示例代码的重点是把核心思路讲明白,所以很多工程化细节会故意省掉。真正落到项目里时,你通常还要继续补接口联动、异常处理、边界保护、资源抽离,以及和其他页面状态之间的配合。

写在最后

比较稳的做法是分三步走:先把结构和职责立住,再把真实业务接进去,最后再优化视觉和交互体验。这样改出来的页面不只是“能演示”,而是真的更接近可以长期维护的业务代码。

赞(0)
未经允许不得转载:171主机测评 » HarmonyOS7 自定义弹窗别写成一坨:页面和弹窗要有边界
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址