当前位置: 首页 > 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.zskr.cn/news/1505199.html

相关文章:

  • P89LPC938微控制器I2C、SPI与ADC模块实战配置与深度调试指南
  • 5分钟上手 markItUp! 1.x:让你的网站秒变专业标记编辑平台 [特殊字符]
  • D2UNet:双解码器协同与纹理变形模块,如何重塑地震图像超分辨率重建?
  • 华硕笔记本性能调校神器:G-Helper终极指南,5分钟告别臃肿控制软件
  • 2026年兰州断桥铝门窗怎么选?本地工厂vs全国品牌实测对比 - 优质企业观察收录
  • 从视觉问答(VQA)实战出发:用CoTAttention提升你的PyTorch模型性能
  • 考研互助交流平台毕设
  • Edge.js 与 Electron 集成:构建跨平台桌面应用的技术方案
  • Edge.js 容器化部署:使用 Docker 打包 .NET-Node.js 混合应用
  • 鸿蒙原生应用开发实战(五):个人中心与数据统计 — 电影清单App
  • 【原创绿化】二维码生成[特殊字符]多类内容[特殊字符]专属二维码制作神器[特殊字符]
  • 如何快速实现Figma到After Effects的无缝动效转换:AEUX终极指南
  • 湖南CPVC电力管品牌哪家可靠 - 资讯速览
  • 杭州西装定制专业评测:5 家顶级店铺深度对比,第一名实至名归! - 西装爱好者
  • MC9S12NE64以太网模块深度解析:EMAC与EPHY配置、调试与实战指南
  • MCprep:让Blender中的Minecraft创作从繁琐到高效的革命性工具
  • RuoYi-Vue-Pro工作流审批系统:从零构建企业级流程自动化的3小时实战指南
  • 工贸企业指南:预算有限优先 SaaS 还是一步到位私有化部署?实在Agent深度解析
  • IOPaint:3分钟搞定图像修复,让AI成为你的智能修图助手
  • 2026 年 6 月最新 | 岩棉净化板厂家盘点 洁净车间项目采购参考厂家榜单 - 商业新知
  • MC9S12NE64 BDM与DBG模块:嵌入式调试的底层原理与实战应用
  • ComfyUI-LTXVideo终极指南:从零开始掌握AI视频生成
  • nnUNet 实战解析(一):自适应框架的设计哲学与核心策略
  • 从‘信息学奥赛一本通’1209题出发,手把手教你用C++写一个通用的分数计算器类
  • 2026东莞工厂厂房拆除废旧物资回收公司专业上门高价收购热线咨询 - 广东再生资源回收
  • Modbus RTU协议详解:从帧格式到功能码示例,一篇就够了
  • I2C总线开关PCA9548A应用与焊接工艺全解析
  • 【实战指南】供应链准时交付预测 —— 基于Amazon SageMaker Canvas的端到端建模
  • Resistor Scanner:3步教你用手机摄像头识别电阻值,从此告别色环记忆烦恼
  • 如何快速获得专业级鼠标指针:Bibata_Cursor完全定制指南