当前位置: 首页 > news >正文

鸿蒙原生应用开发实战(三):电影列表与搜索筛选 — 电影清单App

鸿蒙原生应用开发实战(三):电影列表与搜索筛选 — 电影清单App

前言

随着用户添加的电影越来越多,需要有一个功能完善的列表页面来管理和查找。这篇文章将开发电影列表页,支持多维度状态筛选、关键词搜索和滑动删除。

本文将覆盖:

  1. 卡片式电影列表
  2. 五标签状态筛选(全部/已看/想看/在看/收藏)
  3. 关键词实时搜索
  4. 滑动删除操作
  5. 空状态设计

一、列表页面设计

┌──────────────────────────────────┐ │ < 返回 全部电影 🔍 │ ├──────────────────────────────────┤ │ 🔍 [搜索片名、导演或分类...] │ ← 可展开搜索栏 │ 全部 ✅已看 👀想看 ▶️在看 ⭐收藏│ ← 筛选标签 ├──────────────────────────────────┤ │ 8 部电影 │ ← 计数 ├──────────────────────────────────┤ │ ┌──────────────────────────┐ │ │ │ 🐭 疯狂动物城 ▶️在看 >│ │ ← 电影卡片 │ │ 2016 │ │ (支持滑动删除) │ └──────────────────────────┘ │ │ ┌──────────────────────────┐ │ │ │ 🎭 楚门的世界 ✅已看 >│ │ │ │ ⭐ 1998 │ │ │ └──────────────────────────┘ │ │ ┌──────────────────────────┐ │ │ │ ❤️ 泰坦尼克号 👀想看 >│ │ │ │ 1997 │ │ │ └──────────────────────────┘ │ └──────────────────────────────────┘

二、状态定义

@Entry@Componentstruct ListPage{@Statemovies:Movie[]=[];@StatefilteredList:Movie[]=[];@StatefilterStatus:string='all';@StatesearchText:string='';@StateshowSearch:boolean=false;}

三、多维度筛选引擎

3.1 筛选逻辑

筛选引擎支持3个维度的组合过滤:状态 + 关键词。

applyFilter():void{letresult:Movie[]=[];for(leti=0;i<this.movies.length;i++){letm=this.movies[i];letmatch=true;// 维度1:状态筛选if(this.filterStatus==='watched'&&m.status!==MovieStatus.WATCHED){match=false;}if(this.filterStatus==='want'&&m.status!==MovieStatus.WANT_TO_WATCH){match=false;}if(this.filterStatus==='watching'&&m.status!==MovieStatus.WATCHING){match=false;}if(this.filterStatus==='fav'&&!m.isFavorite){match=false;}// 维度2:关键词搜索if(this.searchText!==''){letkw=this.searchText.toLowerCase();lettitleMatch=m.title.toLowerCase().indexOf(kw)!==-1;letdirMatch=m.director.toLowerCase().indexOf(kw)!==-1;letgenre=getGenreById(m.genreId);letgenreMatch=genre?genre.name.indexOf(kw)!==-1:false;if(!titleMatch&&!dirMatch&&!genreMatch){match=false;}}if(match){result.push(m);}}// 按日期倒序result.sort((a,b)=>b.dateAdded>a.dateAdded?1:-1);this.filteredList=result;}

3.2 搜索范围

搜索不仅匹配电影名称,还匹配导演和分类名称,提高搜索命中率:

  • 片名m.title.toLowerCase().indexOf(kw)
  • 导演m.director.toLowerCase().indexOf(kw)
  • 分类genre.name.indexOf(kw)

四、可展开搜索栏

4.1 搜索开关

点击标题栏右侧 🔍 按钮展开/收起搜索栏:

// 标题栏Row(){Text('< 返回').fontSize(16).fontColor('#6C63FF').onClick(()=>{router.back();})Blank()Text('全部电影').fontSize(18).fontWeight(FontWeight.Bold)Blank()Text('🔍').fontSize(18).onClick(()=>{this.showSearch=!this.showSearch;if(!this.showSearch){this.searchText='';// 收起时清空搜索this.applyFilter();}})}

4.2 条件渲染搜索栏

if(this.showSearch){Row(){TextInput({placeholder:'搜索片名、导演或分类...',text:this.searchText}).fontSize(14).layoutWeight(1).height(36).placeholderColor('#CCCCCC').onChange((v:string)=>{this.onSearchChange(v);})}.width('94%').padding(8).backgroundColor('#FFFFFF')}

五、筛选标签组

5.1 五标签筛选

使用五个 Chip 标签对应全部/已看/想看/在看/收藏:

@BuilderfilterChip(label:string,status:string){Text(label).fontSize(13).fontColor(this.filterStatus===status?'#FFFFFF':'#666666').backgroundColor(this.filterStatus===status?'#6C63FF':'#F0F0F0').padding({left:10,right:10,top:4,bottom:4}).borderRadius(12).margin({right:6}).onClick(()=>{this.onFilterClick(status);})}

使用方式:

Row(){this.filterChip('全部','all')this.filterChip('✅ 已看','watched')this.filterChip('👀 想看','want')this.filterChip('▶️ 在看','watching')this.filterChip('⭐ 收藏','fav')}

5.2 选中状态

状态筛选值图标
全部all
已看watched
想看want👀
在看watching▶️
收藏fav

六、电影卡片

6.1 卡片设计

每部电影使用卡片展示,包含分类图标、片名、状态标签、年份:

@BuildermovieCard(item:Movie){Row(){// 左侧:分类图标Text(getGenreById(item.genreId)?.icon??'🎬').fontSize(28).width(46).height(46).textAlign(TextAlign.Center).backgroundColor('#F5F5F5').borderRadius(23)// 中间:片名 + 状态 + 年份Column(){Row(){Text(item.title).fontSize(16).fontWeight(FontWeight.Bold)if(item.isFavorite){Text(' ⭐').fontSize(14)}}.width('100%')Row(){// 状态标签(带颜色)Text(getStatusLabel(item.status)).fontSize(11).fontColor('#FFFFFF').backgroundColor(getStatusColor(item.status)).padding({left:6,right:6,top:2,bottom:2}).borderRadius(6)Text(' '+item.year).fontSize(12).fontColor('#BBBBBB').margin({left:6})}.width('100%').margin({top:3})}.layoutWeight(1).alignItems(HorizontalAlign.Start).margin({left:12})// 右侧箭头Text('>').fontSize(16).fontColor('#CCCCCC')}.width('100%').padding(12).backgroundColor('#FFFFFF').borderRadius(10).margin({bottom:6}).onClick(()=>{router.pushUrl({url:'pages/DetailPage',params:{movieId:item.id}});})}

6.2 状态颜色编码

状态颜色色值
想看🟠 橙色#FFA502
在看🟢 绿色#2ED573
已看🟣 紫色#6C63FF

七、滑动删除

7.1 SwipeAction 实现

ListItem(){this.movieCard(item)}.swipeAction({end:{builder:():void=>{this.deleteSwipeBtn(item.id)},onAction:():void=>{this.deleteMovie(item.id)}}})

7.2 删除按钮

@BuilderdeleteSwipeBtn(id:string){Column(){Text('删除').fontSize(14).fontColor('#FFFFFF').padding(16)}.backgroundColor('#FF4757').height('100%').justifyContent(FlexAlign.Center).onClick(()=>{this.deleteMovie(id);})}

7.3 删除逻辑

deleteMovie(id:string):void{letnewList:Movie[]=[];for(leti=0;i<this.movies.length;i++){if(this.movies[i].id!==id){newList.push(this.movies[i]);}}this.movies=newList;AppStorage.set<Movie[]>('movies',newList);this.applyFilter();}

八、空状态设计

if(this.filteredList.length===0){Column(){Text('🎬').fontSize(48).margin({bottom:8})Text('没有找到匹配的电影').fontSize(16).fontColor('#CCCCCC')}.width('100%').height(200).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)}

九、ArkTS 注意事项

9.1 可选链操作符

ArkTS 支持?.可选链操作符,可以安全地访问嵌套属性:

// 安全访问getGenreById(item.genreId)?.icon??'🎬'

9.2 滑动删除 API

在 API 23 中,swipeAction 的正确格式是:

.swipeAction({end:{builder:():void=>{/* 自定义内容 */},onAction:():void=>{/* 触发回调 */}}})

不能使用build()方法或.bind()

总结

本文完成了电影列表页面的开发:

  • ✅ 卡片式电影列表展示
  • ✅ 五标签状态筛选
  • ✅ 关键词实时搜索(片名/导演/分类)
  • ✅ 滑动删除操作
  • ✅ 空状态友好提示

下一篇,我们将开发电影详情页面,实现评分、收藏和影评功能!


系列目录

  • ✅ 第一篇:项目搭建与首页概览
  • ✅ 第二篇:添加电影与表单交互
  • ✅ 第三篇:电影列表与搜索筛选(本篇)
  • 📝 第四篇:电影详情与评分评价
  • 📝 第五篇:个人中心与数据统计
http://www.jsqmd.com/news/993442/

相关文章:

  • 3分钟搭建Windows C/C++开发环境:w64devkit完全免费解决方案
  • 5分钟上手 markItUp! 1.x:让你的网站秒变专业标记编辑平台 [特殊字符]
  • 终极指南:如何用开源3D建模软件从照片创建专业级三维模型
  • 卡梅德生物科普:C5(补体蛋白C5)靶点功能与应用深度解析
  • 2026博尔塔拉本地土壤检测农田土壤检测哪家强?TOP 正规机构榜单 + 联系方式 - 鉴安检测
  • 2026年6月青岛婚纱照品牌推荐:TOP10口碑严选+全攻略 - 江湖评测
  • D2UNet:双解码器协同与纹理变形模块,如何重塑地震图像超分辨率重建?
  • 3大核心技术深度解析:cim系统如何实现高可用分布式即时通讯
  • 2026港大本科直申中介怎么挑?专业口碑佳、录取实力强的香港本科留学机构盘点 - 品牌2026
  • 构建千万级分布式即时通讯系统的3大核心策略:ZooKeeper服务发现架构实战
  • PowerPC EC603e嵌入式处理器硬件设计实战:从架构解析到PCB布局与调试
  • LavinMQ性能基准测试:如何快速评估你的消息队列系统性能
  • PCA9622 LED驱动器:两级PWM控制、I2C通信与热管理设计详解
  • 实测CH32V305的USB-CDC串口:用Python脚本跑出30MB/s+,附完整代码与避坑点
  • 5分钟快速上手Umi-OCR:免费离线OCR软件的完整使用指南
  • 别再死记硬背网络结构了!手把手带你用PyTorch复现GoogLeNet(附完整代码与调试技巧)
  • 华硕笔记本性能调校神器:G-Helper终极指南,5分钟告别臃肿控制软件
  • 2026年兰州断桥铝门窗怎么选?本地工厂vs全国品牌实测对比 - 优质企业观察收录
  • 如何安全备份微信聊天记录?WeChatExporter帮你实现本地数据永久保存
  • 深入解析NXP PCA85262 LCD驱动芯片:低复用率原理与I2C配置实战
  • 从视觉问答(VQA)实战出发:用CoTAttention提升你的PyTorch模型性能
  • phpClickHouse监控与诊断:如何使用系统表和查询日志进行性能分析
  • Mermaid Live Editor:5分钟掌握终极在线图表编辑器
  • 手机摄像头如何3秒完成电阻色环识别:ResistorScanner完整指南
  • abap2xlsx安装教程:使用abapGit快速部署Excel处理库到SAP系统
  • 2026 内江厨卫屋面地下室漏水瓷砖空鼓测评:吉修匠 99.8 分五星榜首 - 吉修匠
  • 深入解析PCA9538A I2C GPIO扩展芯片:时序、焊接与PCB设计实战
  • 2026达州企业业主高频选择的 5 家危房检测房屋结构安全鉴定机构实地测评整理 - 科信检测
  • Windows 11终极优化指南:一键清理系统冗余的完整解决方案
  • 深入解析MPC875/870通信处理器:架构、硬件设计与实战优化