鸿蒙原生应用开发实战(三):电影列表与搜索筛选 — 电影清单App
2026/6/11 17:02:00 网站建设 项目流程

鸿蒙原生应用开发实战(三):电影列表与搜索筛选 — 电影清单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()

总结

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

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

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


系列目录

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询