news 2026/6/14 16:21:58

鸿蒙原生应用从0到1:备忘录模块 —— 多视图切换与搜索实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
鸿蒙原生应用从0到1:备忘录模块 —— 多视图切换与搜索实战

鸿蒙原生应用从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:boolean=false;@StateshowDetail:boolean=false;// 详情视图@StatedetailNote:NoteItem|null=null;// 当前查看的笔记@StateeditingNote:NoteItem|null=null;// 编辑中的笔记@StatenewTitle:string='';@StatenewContent:string='';@StatenewCategory:string='生活';@StatesearchText:string='';}

3.1 视图状态转移

列表浏览 ──点击卡片──→ 详情查看 ──点击编辑──→ 编辑模式 ↑ │ │ └──── 返回列表 ──────┘ │ └──────────────── 保存/取消 ──────────────────┘ 列表浏览 ──点击 + ──→ 新增模式

3.2 核心状态管理原则

  1. showAddDialog控制弹窗显隐:新增和编辑共用同一个弹窗
  2. editingNote区分新增/编辑:为null表示新增,有值表示编辑
  3. showDetail+detailNote控制详情视图:两者配合使用

四、数据过滤——分类 + 搜索双过滤

这是本节最重要的知识点:

getfilteredNotes():NoteItem[]{letresult:NoteItem[]=this.notes;// 第一层过滤:分类if(this.activeCategory!=='全部'){result=result.filter((item:NoteItem)=>item.category===this.activeCategory);}// 第二层过滤:关键字搜索if(this.searchText.trim()){constkeyword:string=this.searchText.trim().toLowerCase();result=result.filter((item:NoteItem)=>item.title.toLowerCase().includes(keyword)||item.content.toLowerCase().includes(keyword));}returnresult;}

设计亮点

  1. 链式过滤:先分类后搜索,逻辑清晰
  2. 大小写无关toLowerCase()让搜索对大小写不敏感
  3. 标题+内容双字段匹配:用户可以在笔记全文搜索
  4. 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.searchText=value;})

细节

  • 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.length>40?content.substring(0,40)+'...':content;}

为什么截断:笔记内容可能很长,在列表卡片中完整显示会导致卡片高度不一致、信息密度降低。截断到 40 个字符并加...是移动端常见做法。


六、多视图切换——详情模式

6.1 视图切换逻辑

列表浏览和详情查看是两种完全不同的视图,我通过条件渲染实现切换:

// build() 中if(this.showDetail&&this.detailNote){// 详情视图this.detailView()}else{// 列表视图(包含搜索、分类、卡片列表)this.listView()}

6.2 详情视图

@BuilderdetailView():void{Column(){// 顶部操作栏Row(){Text('← 返回列表').onClick(()=>{this.showDetail=false;this.detailNote=null;})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.detailNote=note;this.showDetail=true;}

注意detailNote赋值为原始对象的引用。如果在详情页修改笔记内容,会影响原数组中的对象。这里因为实现了编辑功能后同步更新,所以没问题。如果只是只读详情,建议深拷贝。


七、编辑功能

7.1 进入编辑模式

startEdit(note:NoteItem):void{this.editingNote=note;this.newTitle=note.title;this.newContent=note.content;this.newCategory=note.category;this.showAddDialog=true;// 复用新增弹窗this.showDetail=false;// 关闭详情}

复用设计:新增和编辑共用同一个弹窗,通过editingNote区分:

  • editingNote === null→ 新增模式,按钮文字为"创建"
  • editingNote !== null→ 编辑模式,按钮文字为"保存修改"

7.2 保存编辑

updateNote():void{if(!this.editingNote||!this.newTitle.trim()){return;}constindex:number=this.notes.findIndex((item:NoteItem)=>item.id===this.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.notes=this.notes.slice();// 触发 UI 刷新this.detailNote=this.notes[index];// 同步更新详情视图}this.resetForm();this.editingNote=null;this.showAddDialog=false;}

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:number=this.notes.length>0?this.notes[this.notes.length-1].id+1:1;constdateStr:string=this.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.showAddDialog=false;}

8.2 Delete —— 删除

deleteNote(id:number):void{this.notes=this.notes.filter((item:NoteItem)=>item.id!==id);// 如果当前在详情页且删除的就是正在查看的笔记,关闭详情if(this.detailNote&&this.detailNote.id===id){this.detailNote=null;this.showDetail=false;}}

边界处理:删除时检查当前详情视图是否展示的就是被删除的笔记,如果是则关闭详情返回列表。

8.3 重置表单

resetForm():void{this.newTitle='';this.newContent='';this.newCategory='生活';}

九、与 ToDo 页面的对比分析

备忘录和待办看起来很相似,但实际差异不小:

维度待办页面备忘录页面
数据字段title + category + prioritytitle + content + category
核心操作切换完成状态查看详情 + 编辑内容
搜索功能❌ 无✅ 双字段搜索
详情视图❌ 无(列表即全部)✅ 独立详情页
编辑功能❌ 无(只能删除)✅ 支持编辑
内容展示单行标题标题 + 内容预览
视图复杂度单一列表列表/详情双视图

备忘录比待办多了一层详情视图,这是 CRUD 中的 “Read/Update” 更完整的体现。


十、技术难点与解决方案

10.1 难点一:多视图状态协调

问题:新增、编辑、详情、列表四种状态如何切换而不冲突?

方案:使用showAddDialogshowDetaileditingNote三个变量组合控制:

  • 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.searchTimer=setTimeout(()=>{this.searchText=value;},300);// 300ms 防抖}

十一、本篇总结

核心知识点

知识点实战应用
多视图切换列表/详情/编辑三种视图状态管理
双过滤系统分类筛选 + 关键词搜索
CRUD 完整流程新增、读取、编辑、删除
弹窗复用新增和编辑共享同一弹窗
内容截断列表预览截断 + 详情完整展示
条件渲染if/else控制不同视图

完整用户操作路径

  1. 浏览:进入页面 → 查看所有笔记列表
  2. 搜索:输入关键词 → 实时过滤笔记
  3. 筛选:点击分类标签 → 只看某分类
  4. 查看详情:点击卡片 → 进入详情视图 → 查看完整内容
  5. 编辑:点击 ✏️ → 弹窗编辑 → 保存修改 → 返回详情
  6. 删除:点击 🗑️ → 从列表移除 → 自动返回列表
  7. 新增:点击 “+” → 弹窗输入 → 创建 → 列表新增卡片

十二、下篇预告

最后一篇将开发**「心情日记」页面**,这是最具视觉趣味性的页面,涵盖:

  • 日历网格的纯算法实现
  • 情绪记录的 Emoji 可视化
  • 月度统计与情绪分析
  • 个人中心页面开发

敬请期待最终篇!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/14 16:18:03

技术人转型 AI:从后端工程到 AI 应用的能力迁移路径

技术人转型 AI:从后端工程到 AI 应用的能力迁移路径一、转型焦虑的本质:技能树的断层感 后端工程师转型 AI 时,最大的障碍不是数学公式,而是技能树的断层感。后端工程师擅长的是系统设计、性能优化和工程交付,但 AI 领…

作者头像 李华
网站建设 2026/6/14 16:18:02

SillyTavern性能优化指南:3大技巧实现AI聊天响应速度提升60%

SillyTavern性能优化指南:3大技巧实现AI聊天响应速度提升60% 【免费下载链接】SillyTavern LLM Frontend for Power Users. 项目地址: https://gitcode.com/GitHub_Trending/si/SillyTavern 还在为SillyTavern的对话延迟和界面卡顿而烦恼吗?作为面…

作者头像 李华
网站建设 2026/6/14 16:18:02

告别单调界面:用foobox-cn打造你的专业级音乐播放器

告别单调界面:用foobox-cn打造你的专业级音乐播放器 【免费下载链接】foobox-cn DUI 配置 for foobar2000 项目地址: https://gitcode.com/GitHub_Trending/fo/foobox-cn 还在为foobar2000那单调枯燥的界面而烦恼吗?你是否曾经想过,为…

作者头像 李华
网站建设 2026/6/14 16:15:55

【信息科学与工程学】【通信工程】第二百零二篇 交换机设备中的学科知识01

高性能数据中心交换机设备(RoCEv2 无损以太网)— 全学科知识体系表 覆盖范围:交换ASIC/SerDes → PCB/封装互连 → 光模块 → 协议栈(RoCEv2/PFC/DCQCN/ECN) → 热-电-流-场耦合 → 机箱结构与制造工艺。所有方程式标注物理意义、参数定义域与边界条件。 总览:模块拓扑结构…

作者头像 李华
网站建设 2026/6/14 16:15:54

3步彻底解决Cursor自动更新问题:永久保持编辑器稳定运行

3步彻底解决Cursor自动更新问题:永久保持编辑器稳定运行 【免费下载链接】go-cursor-help 解决Cursor在免费订阅期间出现以下提示的问题: Your request has been blocked as our system has detected suspicious activity / Youve reached your trial request limit…

作者头像 李华
网站建设 2026/6/14 16:14:54

3个核心技巧:用Pandas快速构建学生成绩管理系统

3个核心技巧:用Pandas快速构建学生成绩管理系统 【免费下载链接】materials Bonus materials, exercises, and example projects for our Python tutorials 项目地址: https://gitcode.com/gh_mirrors/ma/materials 在当今数据驱动的教育领域,Pyt…

作者头像 李华