鸿蒙原生应用开发实战(二):添加电影与表单交互 — 电影清单App
前言
在上一篇文章中我们搭建了项目框架和首页。今天来开发应用的数据录入功能——添加电影页面。这是用户与App交互的第一步,需要良好的表单设计和用户体验。
本文涵盖:
- 表单输入组件(TextInput/TextArea)
- Grid 分类选择器
- 状态切换 Chip 组件
- 表单校验与数据存储
- 5列 Grid 布局适配
一、页面设计
┌──────────────────────────────────┐ │ < 返回 添加电影 │ ├──────────────────────────────────┤ │ 电影名称 * │ │ ┌────────────────────────────┐ │ │ │ 请输入电影名称 │ │ │ └────────────────────────────┘ │ │ │ │ 上映年份 │ │ ┌────────────────────────────┐ │ │ │ 如: 2024 │ │ │ └────────────────────────────┘ │ │ │ │ 导演 │ │ ┌────────────────────────────┐ │ │ │ 请输入导演名 │ │ │ └────────────────────────────┘ │ │ │ │ 观影状态 │ │ [👀 想看] [▶️ 在看] [✅ 已看] │ │ │ │ 电影分类 │ │ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ │ │ │💥│ │😂│ │🎭│ │🚀│ │👻│ │ │ │动作│ │喜剧│ │剧情│ │科幻│ │恐怖││ │ └──┘ └──┘ └──┘ └──┘ └──┘ │ │ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ │ │ │❤️│ │🐭│ │🔍│ │📽️│ │🪄│ │ │ └──┘ └──┘ └──┘ └──┘ └──┘ │ │ │ │ [ 保存到清单 ] │ └──────────────────────────────────┘二、表单状态定义
@Entry@Componentstruct AddMovie{@Statetitle:string='';@Stateyear:string='';@Statedirector:string='';@StateselectedStatus:MovieStatus=MovieStatus.WANT_TO_WATCH;@StateselectedGenreId:string='';@Stategenres:Genre[]=[];}三、文本输入组件
3.1 电影名称输入
Column(){Text('电影名称 *').fontSize(14).fontColor('#999999').width('100%')TextInput({placeholder:'请输入电影名称',text:this.title}).fontSize(16).layoutWeight(1).height(44).placeholderColor('#CCCCCC').margin({top:4}).onChange((v:string)=>{this.title=v;})}.width('90%').margin({top:20,bottom:16})3.2 年份输入
年份使用数字键盘(InputType.Number),方便用户快速输入:
Column(){Text('上映年份').fontSize(14).fontColor('#999999').width('100%')TextInput({placeholder:'如: 2024',text:this.year}).fontSize(16).layoutWeight(1).height(44).placeholderColor('#CCCCCC').type(InputType.Number).margin({top:4}).onChange((v:string)=>{this.year=v;})}.width('90%').margin({bottom:16})3.3 导演输入
Column(){Text('导演').fontSize(14).fontColor('#999999').width('100%')TextInput({placeholder:'请输入导演名',text:this.director}).fontSize(16).layoutWeight(1).height(44).placeholderColor('#CCCCCC').margin({top:4}).onChange((v:string)=>{this.director=v;})}.width('90%').margin({bottom:20})四、状态切换 Chip
观影状态使用 Chip 标签组实现,选中标签高亮:
@BuilderstatusChip(label:string,status:MovieStatus){Text(label).fontSize(14).fontColor(this.selectedStatus===status?'#FFFFFF':'#666666').backgroundColor(this.selectedStatus===status?'#6C63FF':'#F0F0F0').padding({left:14,right:14,top:6,bottom:6}).borderRadius(16).margin({right:8}).onClick(()=>{this.onStatusClick(status);})}使用方式:
Row(){this.statusChip('👀 想看',MovieStatus.WANT_TO_WATCH)this.statusChip('▶️ 在看',MovieStatus.WATCHING)this.statusChip('✅ 已看',MovieStatus.WATCHED)}.width('90%').margin({bottom:20})五、Grid 分类选择器
5.1 五列 Grid
使用columnsTemplate设置为'1fr 1fr 1fr 1fr 1fr'实现5列布局:
Grid(){ForEach(this.genres,(g:Genre)=>{GridItem(){Column(){Text(g.icon).fontSize(24)Text(g.name).fontSize(11).fontColor(this.selectedGenreId===g.id?'#6C63FF':'#666666').margin({top:2})}.width('100%').padding({top:8,bottom:8}).backgroundColor(this.selectedGenreId===g.id?'#EEEAFF':'#F5F5F5').borderRadius(10).alignItems(HorizontalAlign.Center)}.onClick(()=>{this.onGenreClick(g.id);})},(g:Genre)=>g.id)}.columnsTemplate('1fr 1fr 1fr 1fr 1fr').columnsGap(6).rowsGap(6).width('90%')5.2 选中反馈
- 未选中:灰色背景
#F5F5F5+ 灰色文字#666666 - 选中:浅紫色背景
#EEEAFF+ 紫色文字#6C63FF
视觉反馈让用户清晰知道当前选中哪个分类。
六、表单校验与保存
6.1 校验逻辑
saveMovie():void{// 电影名称为必填项if(this.title.trim()===''){return;}// 年份为空时默认当前年份letyearNum=Number.parseInt(this.year);if(isNaN(yearNum)){yearNum=newDate().getFullYear();}// 构建电影对象letmovie:Movie={id:generateId(),title:this.title.trim(),year:yearNum,director:this.director.trim(),rating:0,status:this.selectedStatus,isFavorite:false,review:'',dateAdded:getToday(),genreId:this.selectedGenreId};// 保存到 AppStorageletstored=AppStorage.get<Movie[]>('movies');letlist:Movie[]=stored?stored:[];list.unshift(movie);AppStorage.set<Movie[]>('movies',list);// 返回上一页router.back();}6.2 AppStorage 数据流
AddMovie (写入) → AppStorage → Index (读取展示) → ListPage (读取筛选) → DetailPage (读取修改) → ProfilePage (读取统计)七、Grid 布局适配技巧
7.1 列数选择
5列 Grid 在手机屏幕上能较好地展示10个分类(2行),兼顾信息密度和触摸面积:
1fr 1fr 1fr 1fr 1fr7.2 间距设置
.columnsGap(6)// 列间距 6vp.rowsGap(6)// 行间距 6vp八、完整数据流
当用户填写完表单点击保存时:
- 表单校验→ 检查名称是否为空
- 构建对象→ 使用
generateId()生成唯一ID - 存储数据→
AppStorage.set('movies', list) - 页面返回→
router.back() - 数据刷新→ 首页
onPageShow中重新加载数据
九、ArkTS 严格模式要点
在添加电影页面中,特别注意:
- @Builder 中不能有
let→statusChip使用三元表达式内联判断 - Grid 的 key 生成→ ForEach 需要唯一 key
- 对象字面量→ Movie 对象使用
let movie: Movie = { ... }显式类型
总结
本文完成了添加电影页面的开发:
- ✅ TextInput 表单(名称/年份/导演)
- ✅ InputType.Number 数字键盘
- ✅ Chip 状态切换组件
- ✅ Grid 五列分类选择器
- ✅ 表单校验与 AppStorage 存储
下一篇,我们将开发电影列表页面,实现多维度筛选和搜索功能!
系列目录:
- ✅ 第一篇:项目搭建与首页概览
- ✅ 第二篇:添加电影与表单交互(本篇)
- 📝 第三篇:电影列表与搜索筛选
- 📝 第四篇:电影详情与评分评价
- 📝 第五篇:个人中心与数据统计