从0到1掌握鸿蒙仓颉:700行代码实现企业级待办事项管理系统
从0到1掌握鸿蒙仓颉:700行代码实现企业级待办事项管理系统
你是否还在为鸿蒙应用开发中的状态管理、UI交互和数据流动而头疼?是否想快速掌握仓颉(Cangjie)语言的核心开发模式?本文将通过一个完整的企业级待办事项(To-Do List)案例,带你实战掌握鸿蒙应用开发的关键技术点,从数据建模到UI实现,从状态管理到用户交互,700行代码构建一个功能完备的任务管理系统。
读完本文你将获得:
- 仓颉语言的MVVM开发模式实战经验
- 复杂列表交互(左滑删除、状态切换)的实现方案
- 自定义对话框与用户输入处理技巧
- 鸿蒙组件通信与状态管理最佳实践
- 企业级应用的代码组织与架构设计思路
项目背景与技术选型
随着鸿蒙生态的快速发展,仓颉作为鸿蒙原生应用开发的新语言,以其现代化的语法特性和高效的性能表现,正在成为开发者的新选择。CangjieAppDevelopment项目作为纯仓颉开发的鸿蒙应用案例集,为开发者提供了丰富的实战参考。
本文将深入解析其中的"列表编辑实现案例",该案例虽然名为待办事项管理,但其核心技术点覆盖了企业级应用开发中的常见需求:
项目架构概览
CangjieAppDevelopment项目采用模块化架构设计,将功能按照业务领域进行拆分:
CangjieAppDevelopment/
├── AppScope/ # 应用全局配置
├── common/ # 公用模块
├── entry/ # 应用入口
└── feature/ # 业务功能模块
└── pendingitems/ # 待办事项模块
├── src/main/cangjie/
│ ├── model/ # 数据模型
│ └── pages/ # 页面组件
└── README.md # 模块说明文档
这种架构设计带来的优势:
- 关注点分离:业务逻辑与UI展示分离
- 代码复用:公共功能抽取为独立模块
- 团队协作:不同模块可并行开发
- 可维护性:单一职责原则使代码更易维护
数据模型设计:应用的"骨架"
任何应用的开发都始于数据模型的设计。一个良好的数据模型能够清晰地表达业务概念,为后续开发奠定坚实基础。
待办事项模型(ToDo.cj)
@Observed
public class ToDo {
public let key: String = random.next(64).toString()
@Publish public var name: String = "" // 待办事项名称
@Publish public var isFinished: Bool = false;
}
这个看似简单的类包含了几个关键技术点:
- @Observed装饰器:标记该类为可观察对象,当实例数据变化时,UI会自动更新
- @Publish装饰器:标记需要被观察的属性,属性变化会触发UI刷新
- 唯一标识key:用于列表渲染时的高效Diff算法,确保性能优化
注意:当前仓颉缺少生成UUID的标准接口,这里使用随机数模拟,实际项目中可考虑引入第三方UUID库或自行实现。
数据模型设计最佳实践
| 原则 | 说明 | 本案例实现 |
|---|---|---|
| 单一职责 | 一个模型只表示一个业务实体 | ToDo类专注于待办事项的数据和状态 |
| 不可变性 | 尽量使用不可变属性 | key使用let声明为不可变 |
| 可观察性 | 状态变化需通知UI | 使用@Observed和@Publish装饰器 |
| 唯一标识 | 每个实例应有唯一标识 | 生成随机key作为唯一标识 |
UI组件实现:从静态到动态
UI实现是应用开发的核心环节,鸿蒙仓颉采用声明式UI,通过组件组合和状态绑定实现丰富的界面效果。
主页面结构(ToDoList.cj)
@Entry
@Component
public class ToDoList {
@State
var toDoData: ObservedArrayList<ToDo> = ObservedArrayList(); // 待办事项
@State
var achieveData: ObservedArrayList<ToDo> = ObservedArrayList(); // 已完成事项
func build() {
Column() {
// 标题栏与添加按钮
Row(StyleConfig.LIST_ITEM_GUTTER) {
Text("待办")
.fontSize(@r(sys.float.ohos_id_text_size_headline7))
Blank()
Image(@r(app.media.pendingitems_ic_public_add_filled))
.width(StyleConfig.MENU_IMAGE_SIZE)
.aspectRatio(1)
.onClick({ event =>
dialogController.open()
})
}
// 待办事项列表
List(initialIndex: 0, space: StyleConfig.LIST_ITEM_GUTTER) {
// 未完成列表项
if (this.toDoData.size != 0) {
ListItem() {
Text("未完成").fontSize(@r(sys.float.ohos_id_text_size_headline8))
}
}
ForEach(
this.toDoData,
itemGeneratorFunc: { toDoItem: ToDo, index: Int64 =>
ListItem() {
ToDoListItem(
toDoItem: toDoItem,
achieveData: achieveData,
toDoData: toDoData
)
}
.swipeAction(end: {=> bind(itemEnd, this)(toDoItem)}, edgeEffect: SwipeEdgeEffect.Spring)
},
keyGeneratorFunc: { toDoItem: ToDo, index: Int64 =>
toDoItem.key
}
)
// 已完成列表项
// ...类似未完成列表实现
}
}
.backgroundColor(Color(0xFFF1F3F5))
.width(100.percent)
.height(100.percent)
}
}
这个主页面实现了几个关键功能:
- 双列表设计:将待办和已完成事项分开展示,提高用户体验
- 响应式布局:使用Column、Row等容器组件实现自适应布局
- 状态驱动UI:通过@State装饰的数组驱动List组件渲染
- 高效列表渲染:使用ForEach和keyGeneratorFunc确保列表高效更新
列表项组件(ToDoListItem)
列表项作为可复用组件,负责展示单个待办事项并处理用户交互:
@Component
public struct ToDoListItem {
@Link toDoItem: ToDo
@Link achieveData: ObservedArrayList<ToDo>
@Link toDoData: ObservedArrayList<ToDo>
func build() {
Row(StyleConfig.LIST_ITEM_GUTTER) {
Checkbox()
.isChecked(this.toDoItem.isFinished)
.onChange({ isChecked: Bool =>
this.toDoItem.isFinished = isChecked
if (isChecked) {
this.toDoData.remove(this.toDoItem)
this.achieveData.add(this.toDoItem)
} else {
this.achieveData.remove(this.toDoItem)
this.toDoData.add(this.toDoItem)
}
})
Text(this.toDoItem.name)
.fontSize(@r(sys.float.ohos_id_text_size_body1))
.lineHeight(20)
.maxLines(1)
.textOverflow({overflow: TextOverflow.Ellipsis})
.layoutWeight(1)
}
.height(StyleConfig.LIST_ITEM_HEIGHT)
.width(100.percent)
.padding(StyleConfig.LIST_ITEM_PADDING)
.backgroundColor(Color.WHITE)
}
}
这里使用@Link装饰器实现了父子组件间的双向数据绑定,当用户点击复选框时:
- 更新本地状态isChecked
- 切换待办事项的完成状态
- 在待办列表和已完成列表间迁移数据项
用户交互:打造流畅体验
左滑操作菜单实现
移动应用中,左滑操作是常见的交互模式,用于展示额外操作选项:
// 列表项左滑操作菜单
.swipeAction(end: {=> bind(itemEnd, this)(toDoItem)}, edgeEffect: SwipeEdgeEffect.Spring)
// 操作菜单构建函数
@Builder
func itemEnd(item: ToDo) {
Row(StyleConfig.ICON_GUTTER) {
Image(@r(app.media.pendingitems_ic_public_settings_filled))
.onClick({ event =>
PromptAction.showToast(message: "功能待完善");
})
Image(@r(app.media.pendingitems_ic_public_detail_filled))
.onClick({ event =>
PromptAction.showToast(message: "功能待完善");
})
Image(@r(app.media.pendingitems_ic_public_delete_filled))
.onClick({ event =>
this.deleteTodoItem(item);
})
}
.padding(StyleConfig.OPERATION_BUTTON_PADDING)
.justifyContent(FlexAlign.SpaceEvenly)
}
这段代码实现了类似iOS的左滑操作菜单,用户可以通过滑动列表项来显示设置、详情和删除三个操作按钮。
注意:当前仓颉的swipeAction属性存在一些兼容性问题,实际开发中可能需要根据最新API进行调整。
自定义对话框实现
为了实现待办事项的添加功能,我们需要创建一个自定义对话框:
@CustomDialog
public class MyTextPickerDialog {
var controller: Option<CustomDialogController> = Option.None
var callback: (ToDo) -> Unit
var select: UInt32 = 1
private let availableThings: Array<String> = ['读书', '运动', '旅游', '听音乐', '看电影', '唱歌']
func build() {
Column() {
TextPicker(this.availableThings, selected: this.select)
.width(80.percent)
.margin(top: 16, bottom: 30)
.onChange({result: TextPickerResData =>
this.select = result.index
})
Row(20) {
Button("取消")
.width(40.percent)
.onClick({event =>
controller?.close()
})
Divider().vertical(true)
Button("确定")
.width(40.percent)
.onClick({event =>
let newToDo = ToDo()
newToDo.name = this.availableThings[Int64(this.select)]
callback(newToDo)
controller?.close()
})
}
}
.width(100.percent)
}
}
自定义对话框的使用方式:
// 创建对话框控制器
var dialogController: CustomDialogController = CustomDialogController(
CustomDialogControllerOptions(
builder: MyTextPickerDialog(
callback: { item: ToDo =>
this.toDoData.prepend(item)
}
),
alignment: DialogAlignment.Bottom
)
)
// 点击添加按钮打开对话框
Image(@r(app.media.pendingitems_ic_public_add_filled))
.onClick({ event =>
dialogController.open()
})
这种实现方式的优势在于:
- 代码复用:将对话框封装为独立组件,可在多处复用
- 职责分离:对话框逻辑与主页面逻辑解耦
- 可扩展性:便于后续添加更多自定义功能
状态管理:数据流动的艺术
在现代应用开发中,状态管理是核心挑战之一。仓颉提供了多种状态管理装饰器,帮助开发者构建清晰的数据流动路径。
鸿蒙状态管理装饰器对比
| 装饰器 | 用途 | 适用场景 |
|---|---|---|
| @State | 组件内部状态 | 单个组件的UI状态 |
| @Link | 父子组件双向绑定 | 父子组件间数据同步 |
| @Prop | 父子组件单向绑定 | 子组件使用父组件数据 |
| @Observed | 可观察对象 | 复杂数据模型 |
| @Publish | 可观察属性 | 对象内部需要观察的属性 |
在本案例中,我们主要使用了@State、@Link、@Observed和@Publish四个装饰器,构建了清晰的数据流动路径:
这种状态管理模式的优势:
- 单向数据流:数据流动路径清晰,便于调试
- 响应式更新:数据变化自动触发UI更新
- 组件解耦:组件间通过状态传递而非直接通信
代码优化与最佳实践
样式常量管理
为了提高代码可维护性,将样式相关的常量集中管理:
public class StyleConfig {
// 列表项样式
public static let LIST_ITEM_HEIGHT: Length = 56
public static let LIST_ITEM_GUTTER: Int = 16
public static let LIST_ITEM_PADDING: Padding = Padding(12, 16, 12, 16)
// 图片样式
public static let IMAGE_SIZE: Length = 24
public static let IMAGE_MARGIN: Margin = Margin(12)
// 其他样式常量...
}
错误处理与日志
在企业级应用中,完善的错误处理和日志记录至关重要:
func deleteTodoItem(item: ToDo) {
if (item.isFinished) {
let index = this.achieveData.indexOf(item)
if (index != -1) {
this.achieveData.removeAt(index)
PromptAction.showToast(message: "删除成功")
} else {
AppLog.error("删除失败:未找到项目")
PromptAction.showToast(message: "删除失败")
}
}
// ...
}
性能优化建议
- 使用LazyForEach:对于大数据量列表,使用LazyForEach替代ForEach,实现按需加载
- 避免过度重建:合理设计组件粒度,避免不必要的组件重建
- 图片优化:使用适当分辨率的图片资源,避免内存占用过高
- 状态最小化:只将必要的数据声明为状态变量
项目部署与扩展
编译运行流程
-
环境准备:
# 克隆仓库 git clone https://gitcode.com/Cangjie/HarmonyOS-Cangjie-Cases # 进入项目目录 cd HarmonyOS-Cangjie-Cases/CangjieAppDevelopment # 编译项目 ohos build -
部署到设备:
- 通过DevEco Studio将应用部署到鸿蒙设备或模拟器
- 或使用命令行工具:
ohos install -r app.hap
功能扩展建议
基于现有架构,可以轻松扩展以下功能:
- 数据持久化:使用鸿蒙数据库或偏好设置保存待办事项
- 云同步:集成分布式数据管理,实现多设备同步
- 分类管理:添加标签功能,支持任务分类
- 提醒功能:集成闹钟服务,实现任务提醒
- 统计分析:添加任务完成情况统计和图表展示
总结与展望
通过本文的实战案例,我们深入探讨了鸿蒙仓颉应用开发的核心技术点,从数据建模到UI实现,从状态管理到用户交互,构建了一个功能完备的待办事项管理系统。
回顾本文重点:
- 采用MVVM架构模式,实现数据与UI的分离
- 使用@State、@Link等装饰器实现响应式状态管理
- 封装可复用组件,提高代码复用率
- 实现复杂用户交互,如左滑操作和自定义对话框
- 遵循最佳实践,如样式常量管理和错误处理
随着鸿蒙生态的不断发展,仓颉语言将持续进化,为开发者带来更多便利。未来,我们可以期待更多企业级应用采用仓颉开发,构建更丰富的鸿蒙生态。
作为开发者,掌握鸿蒙开发不仅是技术能力的提升,更是把握未来移动应用发展趋势的关键。希望本文能够帮助你快速入门鸿蒙仓颉开发,开启你的鸿蒙应用开发之旅!
附录:完整代码结构
pendingitems/
├── src/main/cangjie/
│ ├── model/
│ │ ├── ToDo.cj # 待办事项数据模型
│ │ └── StyleConfig.cj # 样式常量配置
│ └── pages/
│ ├── ToDoList.cj # 待办事项主页面
│ ├── ToDoListItem.cj # 列表项组件
│ └── MyTextPickerDialog.cj # 自定义对话框
└── README.md # 模块说明文档
更多推荐



所有评论(0)