鸿蒙原生应用开发实战二添加电影与表单交互 — 电影清单App前言在上一篇文章中我们搭建了项目框架和首页。今天来开发应用的数据录入功能——添加电影页面。这是用户与App交互的第一步需要良好的表单设计和用户体验。本文涵盖表单输入组件TextInput/TextAreaGrid 分类选择器状态切换 Chip 组件表单校验与数据存储5列 Grid 布局适配一、页面设计┌──────────────────────────────────┐ │ 返回 添加电影 │ ├──────────────────────────────────┤ │ 电影名称 * │ │ ┌────────────────────────────┐ │ │ │ 请输入电影名称 │ │ │ └────────────────────────────┘ │ │ │ │ 上映年份 │ │ ┌────────────────────────────┐ │ │ │ 如: 2024 │ │ │ └────────────────────────────┘ │ │ │ │ 导演 │ │ ┌────────────────────────────┐ │ │ │ 请输入导演名 │ │ │ └────────────────────────────┘ │ │ │ │ 观影状态 │ │ [ 想看] [▶️ 在看] [✅ 已看] │ │ │ │ 电影分类 │ │ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ │ │ ││ ││ ││ ││ ││ │ │ │动作│ │喜剧│ │剧情│ │科幻│ │恐怖││ │ └──┘ └──┘ └──┘ └──┘ └──┘ │ │ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ │ │ │❤️│ ││ ││ │️│ ││ │ │ └──┘ └──┘ └──┘ └──┘ └──┘ │ │ │ │ [ 保存到清单 ] │ └──────────────────────────────────┘二、表单状态定义EntryComponentstruct AddMovie{Statetitle:string;Stateyear:string;Statedirector:string;StateselectedStatus:MovieStatusMovieStatus.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.titlev;})}.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.yearv;})}.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.directorv;})}.width(90%).margin({bottom:20})四、状态切换 Chip观影状态使用 Chip 标签组实现选中标签高亮BuilderstatusChip(label:string,status:MovieStatus){Text(label).fontSize(14).fontColor(this.selectedStatusstatus?#FFFFFF:#666666).backgroundColor(this.selectedStatusstatus?#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.selectedGenreIdg.id?#6C63FF:#666666).margin({top:2})}.width(100%).padding({top:8,bottom:8}).backgroundColor(this.selectedGenreIdg.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;}// 年份为空时默认当前年份letyearNumNumber.parseInt(this.year);if(isNaN(yearNum)){yearNumnewDate().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};// 保存到 AppStorageletstoredAppStorage.getMovie[](movies);letlist:Movie[]stored?stored:[];list.unshift(movie);AppStorage.setMovie[](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 存储下一篇我们将开发电影列表页面实现多维度筛选和搜索功能系列目录✅ 第一篇项目搭建与首页概览✅ 第二篇添加电影与表单交互本篇 第三篇电影列表与搜索筛选 第四篇电影详情与评分评价 第五篇个人中心与数据统计
鸿蒙原生应用开发实战(二):添加电影与表单交互 — 电影清单App
鸿蒙原生应用开发实战二添加电影与表单交互 — 电影清单App前言在上一篇文章中我们搭建了项目框架和首页。今天来开发应用的数据录入功能——添加电影页面。这是用户与App交互的第一步需要良好的表单设计和用户体验。本文涵盖表单输入组件TextInput/TextAreaGrid 分类选择器状态切换 Chip 组件表单校验与数据存储5列 Grid 布局适配一、页面设计┌──────────────────────────────────┐ │ 返回 添加电影 │ ├──────────────────────────────────┤ │ 电影名称 * │ │ ┌────────────────────────────┐ │ │ │ 请输入电影名称 │ │ │ └────────────────────────────┘ │ │ │ │ 上映年份 │ │ ┌────────────────────────────┐ │ │ │ 如: 2024 │ │ │ └────────────────────────────┘ │ │ │ │ 导演 │ │ ┌────────────────────────────┐ │ │ │ 请输入导演名 │ │ │ └────────────────────────────┘ │ │ │ │ 观影状态 │ │ [ 想看] [▶️ 在看] [✅ 已看] │ │ │ │ 电影分类 │ │ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ │ │ ││ ││ ││ ││ ││ │ │ │动作│ │喜剧│ │剧情│ │科幻│ │恐怖││ │ └──┘ └──┘ └──┘ └──┘ └──┘ │ │ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ │ │ │❤️│ ││ ││ │️│ ││ │ │ └──┘ └──┘ └──┘ └──┘ └──┘ │ │ │ │ [ 保存到清单 ] │ └──────────────────────────────────┘二、表单状态定义EntryComponentstruct AddMovie{Statetitle:string;Stateyear:string;Statedirector:string;StateselectedStatus:MovieStatusMovieStatus.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.titlev;})}.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.yearv;})}.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.directorv;})}.width(90%).margin({bottom:20})四、状态切换 Chip观影状态使用 Chip 标签组实现选中标签高亮BuilderstatusChip(label:string,status:MovieStatus){Text(label).fontSize(14).fontColor(this.selectedStatusstatus?#FFFFFF:#666666).backgroundColor(this.selectedStatusstatus?#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.selectedGenreIdg.id?#6C63FF:#666666).margin({top:2})}.width(100%).padding({top:8,bottom:8}).backgroundColor(this.selectedGenreIdg.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;}// 年份为空时默认当前年份letyearNumNumber.parseInt(this.year);if(isNaN(yearNum)){yearNumnewDate().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};// 保存到 AppStorageletstoredAppStorage.getMovie[](movies);letlist:Movie[]stored?stored:[];list.unshift(movie);AppStorage.setMovie[](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 存储下一篇我们将开发电影列表页面实现多维度筛选和搜索功能系列目录✅ 第一篇项目搭建与首页概览✅ 第二篇添加电影与表单交互本篇 第三篇电影列表与搜索筛选 第四篇电影详情与评分评价 第五篇个人中心与数据统计