【鸿蒙原生应用开发--ArkUI--014】Expense-tracker 记账应用开发教程 Expense-tracker 记账应用开发教程项目介绍项目背景记账应用是一个帮助用户记录日常收入和支出的个人财务管理工具。在现代生活中管理个人财务变得越来越重要。一个好的记账应用可以帮助用户了解自己的消费习惯控制支出实现财务目标。财务管理是一项重要的生活技能它影响着我们的生活质量、未来规划和财务安全。如果没有适当的跟踪很容易失去对资金流向的了解导致超支和财务压力。本应用提供了一个简单而有效的解决方案用于跟踪每一笔交易。应用场景日常记账实时记录每一笔收入和支出交易。无论是一杯咖啡、一顿餐厅用餐还是一笔工资存款每笔交易都可以记录详细信息包括金额、分类、描述和日期。预算管理通过提供清晰的收支概览帮助用户控制支出。用户可以设定月度预算并跟踪进度。财务分析了解消费模式找出可以减少支出的领域。通过将交易分类用户可以看到哪些类别消耗了最多的资源。目标追踪设定储蓄目标并追踪进度。应用可以帮助用户可视化他们的财务决策如何影响长期目标。功能特性交易记录记录每笔交易的金额、分类、描述和日期。分类管理支持多种消费分类餐饮、交通、购物等。余额统计实时显示总收入、总支出和当前余额。交易列表按时间顺序显示所有交易记录。删除功能允许用户删除错误的交易记录。收支切换轻松切换记录收入和支出。最终效果应用采用清新的绿色主题象征着财务增长和财富。主界面包含顶部导航栏显示应用标题和添加按钮余额统计卡片显示总收入、总支出和净余额交易列表显示所有已记录的交易包含分类图标技术栈开发框架HarmonyOS NEXT (API 20)编程语言ArkTSUI框架ArkUI 声明式 UI核心组件Column, Row, List, Button, TextInput, Toggle知识点讲解1. State 状态管理State是 ArkUI 中最常用的装饰器用于声明组件的内部状态。当状态变量的值发生变化时框架会自动重新渲染使用该状态的 UI 部分。Componentstruct ExpenseTracker{// 声明状态变量Statebalance:number0StatetotalIncome:number0StatetotalExpense:number0StateshowAddForm:booleanfalseStatetransactions:Transaction[][]build(){Column(){// 在 UI 中使用状态变量Text(余额: ¥${this.balance}).fontSize(24).fontWeight(FontWeight.Bold)// 修改状态会触发 UI 更新Button(显示表单).onClick((){this.showAddFormtrue// 状态变化UI 自动更新})}}}State 的关键点State变量只能在声明它的组件内使用修改State变量会触发组件重新渲染建议将相关的状态放在一起管理状态变化应该是不可变操作特别是对于复杂对象2. 接口定义 (interface)接口用于定义数据结构确保类型安全提高代码可维护性。// 定义交易记录的数据结构interfaceTransaction{id:number// 唯一标识amount:number// 交易金额category:string// 分类名称description:string// 交易描述date:string// 交易日期type:income|expense// 交易类型}// 使用接口创建数据constnewTransaction:Transaction{id:Date.now(),amount:100,category:餐饮,description:午餐,date:2024-01-20,type:expense}接口的优势类型检查编译时检查数据结构的正确性代码提示IDE 可以提供属性和方法的自动补全文档作用清晰地定义数据的结构3. List 列表组件List组件用于显示可滚动的列表是 ArkUI 中最常用的组件之一。List(){// 使用 ForEach 循环渲染列表项ForEach(this.transactions,(transaction:Transaction){ListItem(){Row(){// 分类图标Column(){Text(transaction.category.charAt(0)).fontSize(20).fontColor(#ffffff)}.width(44).height(44).borderRadius(22).backgroundColor(transaction.typeincome?#10b981:#ef4444).justifyContent(FlexAlign.Center)// 交易信息Column(){Text(transaction.description||transaction.category).fontSize(16).fontColor(#1e293b)Text(${transaction.category}·${transaction.date}).fontSize(12).fontColor(#64748b).margin({top:4})}.width(60%).padding({left:12})// 金额Column(){Text(${transaction.typeincome?:-}¥${transaction.amount.toFixed(2)}).fontSize(16).fontWeight(FontWeight.Medium).fontColor(transaction.typeincome?#10b981:#ef4444)}.width(20%).alignItems(HorizontalAlign.End)}.width(100%).padding(16).backgroundColor(#ffffff).borderRadius(8).margin({bottom:8})}})}.width(100%).layoutWeight(1)// 占据剩余空间List 常用属性.width()/.height()设置宽高.layoutWeight(1)占据父容器的剩余空间.divider()设置列表项之间的分割线.scrollBar()设置滚动条显示方式4. 数组操作方法JavaScript/TypeScript 提供了丰富的数组操作方法在记账应用中广泛使用。// filter: 过滤数组元素constincomeTransactionsthis.transactions.filter(tt.typeincome)// reduce: 累加计算consttotalIncomethis.transactions.filter(tt.typeincome).reduce((sum,t)sumt.amount,0)// unshift: 在数组开头添加元素this.transactions.unshift(newTransaction)// findIndex: 查找元素索引constindexthis.transactions.findIndex(tt.idid)// splice: 删除指定位置的元素this.transactions.splice(index,1)// map: 映射数组元素constcategoriesthis.transactions.map(tt.category)// find: 查找第一个匹配的元素consttransactionthis.transactions.find(tt.idid)// some: 检查是否有任何元素匹配consthasIncomethis.transactions.some(tt.typeincome)// every: 检查是否所有元素匹配constallExpensesthis.transactions.every(tt.typeexpense)5. 条件渲染使用if/else或三元运算符根据条件显示不同的 UI。// if/else 方式if(this.transactions.length0){Column(){Text(暂无交易记录).fontSize(16).fontColor(#64748b)Text(点击右上角 添加第一笔记录).fontSize(14).fontColor(#94a3b8)}.width(100%).height(200).justifyContent(FlexAlign.Center)}else{List(){ForEach(this.transactions,(transaction:Transaction){ListItem(){// 列表项内容}})}}// 三元运算符方式Text(transaction.typeincome?:-).fontSize(16).fontColor(transaction.typeincome?#10b981:#ef4444)6. 事件处理使用onClick、onChange等方法处理用户交互。// 点击事件Button(添加).onClick((){this.addTransaction()})// 输入变化事件TextInput({placeholder:请输入金额,text:this.newAmount}).onChange((value:string){this.newAmountvalue})7. 样式设置使用链式调用设置组件的各种样式属性。Text(记账应用).fontSize(24)// 字体大小.fontWeight(FontWeight.Bold)// 字体粗细.fontColor(#1e293b)// 字体颜色.margin({top:16})// 外边距.padding({left:20})// 内边距.width(100%)// 宽度.textAlign(TextAlign.Center)// 文本对齐8. 布局组件ArkUI 提供了多种布局组件用于构建复杂的界面。// Column: 垂直布局Column(){Text(第一行)Text(第二行)Text(第三行)}.width(100%).height(100%).justifyContent(FlexAlign.Center)// 垂直居中.alignItems(HorizontalAlign.Center)// 水平居中// Row: 水平布局Row(){Text(左侧)Blank()// 占据剩余空间Text(右侧)}.width(100%).justifyContent(FlexAlign.SpaceBetween)// 两端对齐9. 组件生命周期Componentstruct MyComponent{// 组件即将出现时调用aboutToAppear(){console.log(组件即将出现)// 适合进行初始化操作}// 组件即将消失时调用aboutToDisappear(){console.log(组件即将消失)// 适合进行清理操作}build(){// 构建 UI}}10. 字符串模板使用模板字符串进行格式化输出。// 基本模板constmessage当前余额: ¥${this.balance.toFixed(2)}// 格式化数字constamount¥${transaction.amount.toFixed(2)}// 组合字符串constdisplayText${transaction.typeincome?收入:支出}: ¥${transaction.amount}完整代码解析页面结构设计┌─────────────────────────────────┐ │ 顶部导航栏 │ │ [记账应用] [] │ ├─────────────────────────────────┤ │ 余额统计卡片 │ │ ┌───────────────────────────┐ │ │ │ 总余额: ¥1,234.56 │ │ │ │ │ │ │ │ 收入: ¥5,000 │ │ │ │ 支出: ¥3,765 │ │ │ └───────────────────────────┘ │ ├─────────────────────────────────┤ │ 交易记录标题 │ │ [交易记录] [共 5 笔] │ ├─────────────────────────────────┤ │ 交易记录列表 │ │ ┌───────────────────────────┐ │ │ │ 午餐 -¥25.00 │ │ │ │ 餐饮 · 2024-01-20 │ │ │ └───────────────────────────┘ │ │ ┌───────────────────────────┐ │ │ │ 工资 ¥5,000.00│ │ │ │ 收入 · 2024-01-15 │ │ │ └───────────────────────────┘ │ │ ... │ └─────────────────────────────────┘核心方法实现1. 添加交易记录privateaddTransaction(){// 验证输入if(this.newAmount||isNaN(Number(this.newAmount))){return}constamountNumber(this.newAmount)// 创建交易记录对象consttransaction:Transaction{id:Date.now(),// 使用时间戳作为唯一IDamount:amount,category:this.newCategory,description:this.newDescription,date:newDate().toLocaleDateString(),type:this.newType}// 添加到列表开头this.transactions.unshift(transaction)// 更新余额统计this.updateBalance()// 清空表单this.clearForm()// 关闭表单this.showAddFormfalse}2. 更新余额统计privateupdateBalance(){// 计算总收入this.totalIncomethis.transactions.filter(tt.typeincome).reduce((sum,t)sumt.amount,0)// 计算总支出this.totalExpensethis.transactions.filter(tt.typeexpense).reduce((sum,t)sumt.amount,0)// 计算余额this.balancethis.totalIncome-this.totalExpense}3. 删除交易记录privatedeleteTransaction(id:number){// 过滤掉指定ID的记录this.transactionsthis.transactions.filter(tt.id!id)// 更新余额统计this.updateBalance()}4. 格式化金额privateformatAmount(amount:number):string{// 保留两位小数returnamount.toFixed(2)}常见问题与解决方案问题1添加记录后列表不更新现象点击添加按钮后列表没有显示新记录。原因直接修改数组元素不会触发 UI 更新。解决方案// 错误方式直接修改数组this.transactions[0].amount100// 不会触发更新// 正确方式创建新数组this.transactions[...this.transactions]// 或者使用 unshift/push 等方法this.transactions.unshift(newTransaction)问题2金额输入验证现象输入非数字字符时程序出错。解决方案privateaddTransaction(){// 验证输入是否为有效数字if(this.newAmount||isNaN(Number(this.newAmount))){// 显示错误提示return}constamountNumber(this.newAmount)// 验证金额是否为正数if(amount0){return}// 继续添加记录...}问题3日期格式不一致现象不同设备显示的日期格式不同。解决方案// 使用固定的日期格式privateformatDate(date:Date):string{constyeardate.getFullYear()constmonth(date.getMonth()1).toString().padStart(2,0)constdaydate.getDate().toString().padStart(2,0)return${year}-${month}-${day}}问题4列表滚动性能现象记录很多时列表滚动卡顿。解决方案// 使用 LazyForEach 替代 ForEachList(){LazyForEach(this.dataSource,(transaction:Transaction){ListItem(){// 列表项内容}},(transaction:Transaction)transaction.id.toString())}扩展学习可添加功能数据持久化使用Preferences存储用户设置使用RDB关系型数据库存储交易记录图表统计使用 Canvas 绘制饼图显示消费分类使用折线图显示收支趋势多币种支持支持人民币、美元、欧元等多种货币实时汇率转换预算管理设置每月预算超支提醒导出功能导出为 CSV 文件生成月度报告优化建议性能优化使用LazyForEach处理长列表避免在build方法中进行复杂计算使用Watch监听状态变化用户体验添加加载动画提供操作反馈震动、声音支持手势操作左滑删除代码质量提取公共组件使用常量管理颜色和尺寸编写单元测试总结通过本教程您学会了State 状态管理如何声明和使用状态变量接口定义如何使用 interface 定义数据结构List 组件如何创建可滚动的列表数组操作如何使用 filter、reduce 等方法处理数据条件渲染如何根据条件显示不同的 UI事件处理如何处理用户交互样式设置如何设置组件的样式这些知识点是 HarmonyOS NEXT 开发的基础掌握它们后您可以轻松构建更复杂的应用。