鸿蒙原生应用从0到1备忘录模块 —— 多视图切换与搜索实战系列第四篇深入「备忘录」页面开发重点讲解分类筛选 关键词搜索、详情视图、编辑模式、多视图切换等核心功能。一、功能概览备忘录是生活助手 App 中功能最丰富的页面因为它需要在有限空间内承载多种视图状态列表浏览、详情查看、新增编辑。┌─────────────────────────────────┐ │ ← 返回 备忘录 共6篇 │ ├─────────────────────────────────┤ │ 搜索笔记... │ ← 搜索栏 ├─────────────────────────────────┤ │ [全部] [工作] [学习] [生活] ... │ ← 分类标签 ├─────────────────────────────────┤ │ ┌─────────────────────────────┐ │ │ │ 工作 2025-01-15 │ │ │ │ 项目管理要点 │ │ │ │ 1. 明确项目目标和范围... │ │ ← 笔记卡片 │ │ 查看全文 → │ │ │ └─────────────────────────────┘ │ │ ┌─────────────────────────────┐ │ │ │ 学习 2025-01-14 │ │ │ │ ArkTS学习笔记 │ │ │ │ ArkTS是鸿蒙原生开发语言... │ │ │ │ 查看全文 → │ │ │ └─────────────────────────────┘ │ ├─────────────────────────────────┤ │ [ 写笔记] │ └─────────────────────────────────┘二、数据模型interfaceNoteItem{id:number;// 唯一标识title:string;// 标题content:string;// 内容支持多行category:string;// 分类date:string;// 日期}interfaceNoteCategory{name:string;// 筛选标识label:string;// 显示文字color:string;// 颜色}与待办页面不同笔记的content是多行文本内容可能较长因此在列表显示时需要做截断处理。三、复杂状态管理备忘录页面有多种视图状态需要 5 个状态变量来协调Componentstruct NotePage{Statenotes:NoteItem[][];StateactiveCategory:string全部;StateshowAddDialog:booleanfalse;StateshowDetail:booleanfalse;// 详情视图StatedetailNote:NoteItem|nullnull;// 当前查看的笔记StateeditingNote:NoteItem|nullnull;// 编辑中的笔记StatenewTitle:string;StatenewContent:string;StatenewCategory:string生活;StatesearchText:string;}3.1 视图状态转移列表浏览 ──点击卡片──→ 详情查看 ──点击编辑──→ 编辑模式 ↑ │ │ └──── 返回列表 ──────┘ │ └──────────────── 保存/取消 ──────────────────┘ 列表浏览 ──点击 ──→ 新增模式3.2 核心状态管理原则showAddDialog控制弹窗显隐新增和编辑共用同一个弹窗editingNote区分新增/编辑为null表示新增有值表示编辑showDetaildetailNote控制详情视图两者配合使用四、数据过滤——分类 搜索双过滤这是本节最重要的知识点getfilteredNotes():NoteItem[]{letresult:NoteItem[]this.notes;// 第一层过滤分类if(this.activeCategory!全部){resultresult.filter((item:NoteItem)item.categorythis.activeCategory);}// 第二层过滤关键字搜索if(this.searchText.trim()){constkeyword:stringthis.searchText.trim().toLowerCase();resultresult.filter((item:NoteItem)item.title.toLowerCase().includes(keyword)||item.content.toLowerCase().includes(keyword));}returnresult;}设计亮点链式过滤先分类后搜索逻辑清晰大小写无关toLowerCase()让搜索对大小写不敏感标题内容双字段匹配用户可以在笔记全文搜索getter写法使用get filteredNotes()而非方法让模板中直接使用this.filteredNotes调用更简洁五、UI 构建详解5.1 搜索栏TextInput({placeholder:搜索笔记...,text:this.searchText}).width(90%).height(40).borderRadius(20)// 圆角效果.backgroundColor($r(app.color.bg_card)).padding({left:16}).onChange((value:string){this.searchTextvalue;})细节borderRadius(20)让搜索栏呈现胶囊形状onChange回调实时更新searchText实现即时搜索5.2 笔记卡片BuildernoteCard(note:NoteItem):void{Column(){// 顶部分类标签 日期Row(){Text(note.category).fontSize(10).fontColor(Color.White).padding({left:10,right:10,top:3,bottom:3}).backgroundColor(this.getCategoryColor(note.category)).borderRadius(8)Blank()Text(note.date).fontSize($r(app.float.tiny_font_size)).fontColor($r(app.color.text_secondary))}.width(100%)// 标题Text(note.title).fontSize($r(app.float.body_font_size)).fontWeight(FontWeight.Medium).fontColor($r(app.color.text_primary)).width(100%).margin({top:8})// 内容预览截断Text(this.getPreviewText(note.content)).fontSize($r(app.float.small_font_size)).fontColor($r(app.color.text_secondary)).lineHeight(20).width(100%).margin({top:4})// 查看全文入口Row(){Blank()Text(查看全文 →).fontSize($r(app.float.tiny_font_size)).fontColor($r(app.color.primary))}.width(100%).margin({top:8})}.width(100%).padding(16).backgroundColor($r(app.color.bg_card)).borderRadius($r(app.float.card_radius)).margin({bottom:10}).shadow({radius:4,color:$r(app.color.shadow),offsetY:1}).onClick((){this.viewDetail(note);})}5.3 内容截断getPreviewText(content:string):string{returncontent.length40?content.substring(0,40)...:content;}为什么截断笔记内容可能很长在列表卡片中完整显示会导致卡片高度不一致、信息密度降低。截断到 40 个字符并加...是移动端常见做法。六、多视图切换——详情模式6.1 视图切换逻辑列表浏览和详情查看是两种完全不同的视图我通过条件渲染实现切换// build() 中if(this.showDetailthis.detailNote){// 详情视图this.detailView()}else{// 列表视图包含搜索、分类、卡片列表this.listView()}6.2 详情视图BuilderdetailView():void{Column(){// 顶部操作栏Row(){Text(← 返回列表).onClick((){this.showDetailfalse;this.detailNotenull;})Blank()Text(✏️)// 编辑.onClick((){this.startEdit(this.detailNote!);})Text(️)// 删除.onClick((){this.deleteNote(this.detailNote!.id);})}// 笔记标题Text(this.detailNote.title).fontSize($r(app.float.title_font_size)).fontWeight(FontWeight.Bold)// 元信息Row(){Text(this.detailNote.category).backgroundColor(this.getCategoryColor(this.detailNote.category))Text(this.detailNote.date)}// 正文内容Text(this.detailNote.content).fontSize($r(app.float.body_font_size)).lineHeight(26).width(100%)}.padding(20)}6.3 进入详情viewDetail(note:NoteItem):void{this.detailNotenote;this.showDetailtrue;}注意detailNote赋值为原始对象的引用。如果在详情页修改笔记内容会影响原数组中的对象。这里因为实现了编辑功能后同步更新所以没问题。如果只是只读详情建议深拷贝。七、编辑功能7.1 进入编辑模式startEdit(note:NoteItem):void{this.editingNotenote;this.newTitlenote.title;this.newContentnote.content;this.newCategorynote.category;this.showAddDialogtrue;// 复用新增弹窗this.showDetailfalse;// 关闭详情}复用设计新增和编辑共用同一个弹窗通过editingNote区分editingNote null→ 新增模式按钮文字为创建editingNote ! null→ 编辑模式按钮文字为保存修改7.2 保存编辑updateNote():void{if(!this.editingNote||!this.newTitle.trim()){return;}constindex:numberthis.notes.findIndex((item:NoteItem)item.idthis.editingNote!.id);if(index!-1){constupdatedNote:NoteItem{id:this.notes[index].id,// 保持 ID 不变title:this.newTitle.trim(),content:this.newContent.trim(),category:this.newCategory,date:this.notes[index].date// 保留原日期};this.notes[index]updatedNote;this.notesthis.notes.slice();// 触发 UI 刷新this.detailNotethis.notes[index];// 同步更新详情视图}this.resetForm();this.editingNotenull;this.showAddDialogfalse;}7.3 新增与编辑的按钮逻辑Button(this.editingNote?保存修改:创建).onClick((){if(this.editingNote){this.updateNote();}else{this.addNote();}})八、CRUD 完整流程8.1 Create —— 新增addNote():void{if(!this.newTitle.trim())return;constnewId:numberthis.notes.length0?this.notes[this.notes.length-1].id1:1;constdateStr:stringthis.getTodayString();constnewNote:NoteItem{id:newId,title:this.newTitle.trim(),content:this.newContent.trim(),category:this.newCategory,date:dateStr};this.notes.push(newNote);this.resetForm();this.showAddDialogfalse;}8.2 Delete —— 删除deleteNote(id:number):void{this.notesthis.notes.filter((item:NoteItem)item.id!id);// 如果当前在详情页且删除的就是正在查看的笔记关闭详情if(this.detailNotethis.detailNote.idid){this.detailNotenull;this.showDetailfalse;}}边界处理删除时检查当前详情视图是否展示的就是被删除的笔记如果是则关闭详情返回列表。8.3 重置表单resetForm():void{this.newTitle;this.newContent;this.newCategory生活;}九、与 ToDo 页面的对比分析备忘录和待办看起来很相似但实际差异不小维度待办页面备忘录页面数据字段title category prioritytitle content category核心操作切换完成状态查看详情 编辑内容搜索功能❌ 无✅ 双字段搜索详情视图❌ 无列表即全部✅ 独立详情页编辑功能❌ 无只能删除✅ 支持编辑内容展示单行标题标题 内容预览视图复杂度单一列表列表/详情双视图备忘录比待办多了一层详情视图这是 CRUD 中的 “Read/Update” 更完整的体现。十、技术难点与解决方案10.1 难点一多视图状态协调问题新增、编辑、详情、列表四种状态如何切换而不冲突方案使用showAddDialog、showDetail、editingNote三个变量组合控制showAddDialog true, editingNote null→ 新增弹窗showAddDialog true, editingNote ! null→ 编辑弹窗showDetail true→ 详情视图全部 false/null → 列表视图10.2 难点二刷新不丢失编辑状态问题ArkTS 的State只追踪顶层引用变化修改对象属性不会触发渲染。方案使用this.notes this.notes.slice()或[...this.notes]创建新数组引用。10.3 难点三搜索性能问题每次输入都实时过滤大数据量下会不会卡方案当前数据量6条示例数据毫无压力。如果未来数据量上万可以加上防抖debounce// 防抖示例仅当需要时添加privatesearchTimer:number-1;onSearch(value:string):void{clearTimeout(this.searchTimer);this.searchTimersetTimeout((){this.searchTextvalue;},300);// 300ms 防抖}十一、本篇总结核心知识点知识点实战应用多视图切换列表/详情/编辑三种视图状态管理双过滤系统分类筛选 关键词搜索CRUD 完整流程新增、读取、编辑、删除弹窗复用新增和编辑共享同一弹窗内容截断列表预览截断 详情完整展示条件渲染if/else控制不同视图完整用户操作路径浏览进入页面 → 查看所有笔记列表搜索输入关键词 → 实时过滤笔记筛选点击分类标签 → 只看某分类查看详情点击卡片 → 进入详情视图 → 查看完整内容编辑点击 ✏️ → 弹窗编辑 → 保存修改 → 返回详情删除点击 ️ → 从列表移除 → 自动返回列表新增点击 “” → 弹窗输入 → 创建 → 列表新增卡片十二、下篇预告最后一篇将开发**「心情日记」页面**这是最具视觉趣味性的页面涵盖日历网格的纯算法实现情绪记录的 Emoji 可视化月度统计与情绪分析个人中心页面开发敬请期待最终篇