鸿蒙原生应用开发实战(三):电影列表与搜索筛选 — 电影清单App
·
鸿蒙原生应用开发实战(三):电影列表与搜索筛选 — 电影清单App
前言
随着用户添加的电影越来越多,需要有一个功能完善的列表页面来管理和查找。这篇文章将开发电影列表页,支持多维度状态筛选、关键词搜索和滑动删除。
本文将覆盖:
- 卡片式电影列表
- 五标签状态筛选(全部/已看/想看/在看/收藏)
- 关键词实时搜索
- 滑动删除操作
- 空状态设计
一、列表页面设计
┌──────────────────────────────────┐
│ < 返回 全部电影 🔍 │
├──────────────────────────────────┤
│ 🔍 [搜索片名、导演或分类...] │ ← 可展开搜索栏
│ 全部 ✅已看 👀想看 ▶️在看 ⭐收藏│ ← 筛选标签
├──────────────────────────────────┤
│ 8 部电影 │ ← 计数
├──────────────────────────────────┤
│ ┌──────────────────────────┐ │
│ │ 🐭 疯狂动物城 ▶️在看 >│ │ ← 电影卡片
│ │ 2016 │ │ (支持滑动删除)
│ └──────────────────────────┘ │
│ ┌──────────────────────────┐ │
│ │ 🎭 楚门的世界 ✅已看 >│ │
│ │ ⭐ 1998 │ │
│ └──────────────────────────┘ │
│ ┌──────────────────────────┐ │
│ │ ❤️ 泰坦尼克号 👀想看 >│ │
│ │ 1997 │ │
│ └──────────────────────────┘ │
└──────────────────────────────────┘
二、状态定义
@Entry
@Component
struct ListPage {
@State movies: Movie[] = [];
@State filteredList: Movie[] = [];
@State filterStatus: string = 'all';
@State searchText: string = '';
@State showSearch: boolean = false;
}
三、多维度筛选引擎
3.1 筛选逻辑
筛选引擎支持3个维度的组合过滤:状态 + 关键词。
applyFilter(): void {
let result: Movie[] = [];
for (let i = 0; i < this.movies.length; i++) {
let m = this.movies[i];
let match = 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 !== '') {
let kw = this.searchText.toLowerCase();
let titleMatch = m.title.toLowerCase().indexOf(kw) !== -1;
let dirMatch = m.director.toLowerCase().indexOf(kw) !== -1;
let genre = getGenreById(m.genreId);
let genreMatch = 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 标签对应全部/已看/想看/在看/收藏:
@Builder filterChip(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 卡片设计
每部电影使用卡片展示,包含分类图标、片名、状态标签、年份:
@Builder movieCard(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 删除按钮
@Builder deleteSwipeBtn(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 {
let newList: Movie[] = [];
for (let i = 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()。
总结
本文完成了电影列表页面的开发:
- ✅ 卡片式电影列表展示
- ✅ 五标签状态筛选
- ✅ 关键词实时搜索(片名/导演/分类)
- ✅ 滑动删除操作
- ✅ 空状态友好提示
下一篇,我们将开发电影详情页面,实现评分、收藏和影评功能!

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



所有评论(0)