鸿蒙原生应用实战二首页与诗词库页面开发——多元布局与交互实现前言在上一章中我们完成了项目初始化和架构设计。本章将正式进入编码阶段集中开发应用的两个核心页面首页Index.ets—— 信息聚合入口诗词库PoemListPage.ets—— 搜索与筛选这两个页面涉及了大量 ArkTS 布局技巧、组件复用和数据绑定模式是鸿蒙开发的核心实战内容。一、首页开发Index.ets1.1 页面布局总览首页从上到下分为五个区域┌──────────────────────┐ │ 标题栏 用户头像 │ ← Row Column 组合 ├──────────────────────┤ │ 每日诗词推荐卡片 │ ← 渐变背景 引用样式 ├──────────────────────┤ │ 6 大分类入口 (Grid) │ ← 2 行 3 列网格 ├──────────────────────┤ │ 热门排行列表 │ ← 带序号和点赞数 ├──────────────────────┤ │ 为你推荐列表 │ ← 与排行相同结构 ├──────────────────────┤ │ 底部导航栏 │ ← 4 个 Tab └──────────────────────┘1.2 数据结构定义在 ArkTS 的严格模式下所有对象字面量必须有显式类型声明// 诗词条目接口interfacePoemItem{id:number;title:string;author:string;dynasty:string;content:string[];// 诗句数组type:string;// 五言绝句 / 词 / 乐府 ...likes:number;}// 每日推荐数据constdailyPoem:DailyPoem{title:定风波,author:苏轼,dynasty:宋,excerpt:竹杖芒鞋轻胜马谁怕一蓑烟雨任平生。};// 热门排行数据consttopPoems:PoemItem[][{id:1,title:静夜思,author:李白,dynasty:唐,content:[床前明月光,疑是地上霜,举头望明月,低头思故乡],type:五言绝句,likes:9852},// ... 更多诗词];1.3 渐变背景卡片每日诗词首页最醒目的每日一首卡片使用了渐变背景效果。在 ArkTS 中可以通过background属性实现Column(){Text(每日一首).fontSize(12).fontColor(rgba(255,255,255,0.7)).width(100%)Text(dailyPoem.title).fontSize(22).fontWeight(FontWeight.Bold).fontColor(Color.White).width(100%).padding({top:8})Text(—— dailyPoem.dynasty·dailyPoem.author).fontSize(13).fontColor(rgba(255,255,255,0.8)).width(100%)// 居中展示经典名句Text(dailyPoem.excerpt).fontSize(17).fontColor(Color.White).lineHeight(28).textAlign(TextAlign.Center).padding({top:16,bottom:8})}.width(100%).padding(20)// 渐变色背景——紫色系渐变.background(linear-gradient(135deg, #667eea, #764ba2)).borderRadius(16)技巧linear-gradient是 ArkTS 支持的背景渐变语法适合做卡片头部装饰。1.4 网格布局6 大分类使用Grid组件实现 2 行 3 列的诗词分类入口Grid(){ForEach(categories,(cat:string){GridItem(){this.createCategoryCard(cat)}},(cat:string)cat)}.columnsTemplate(1fr 1fr 1fr)// 3列等宽.rowsTemplate(1fr 1fr)// 2行.rowsGap(12).columnsGap(12).width(100%)每个分类卡片包含 emoji 图标和文字标签点击后跳转到诗词库页面并自动筛选该分类。1.5 Builder 组件复用在 ArkTS 中Builder是组件复用的核心机制。需要注意一个关键限制Builder 内不能声明变量// ❌ 错误——Builder 内不能有 const/interfaceBuildercreateCategoryCard(name:string){consticons:Recordstring,string{...};// 编译报错// ...}// ✅ 正确——将数据提取为普通方法getCatIcon(name:string):string{consticons:Recordstring,string{唐诗三百:,宋词精选:,元曲:,古诗十九首:,乐府诗集:,诗经:};returnicons[name]||;}BuildercreateCategoryCard(name:string){Column(){Text(this.getCatIcon(name)).fontSize(28)Text(name).fontSize(12).fontColor($r(app.color.text_primary)).margin({top:8}).fontWeight(FontWeight.Medium)}// ...}1.6 图片圆形容器首页右上角的用户头像使用了Circle组件 .overlay()的组合Circle().width(40).height(40).fill($r(app.color.accent_purple)).overlay(this.avatarText())overlay是一个Builder 方法BuilderavatarText(){Text(诗).fontColor(Color.White).fontSize(18).fontWeight(FontWeight.Bold)}注意在早期版本的 ArkTS 中.overlay()不能直接接受Text()组件必须通过Builder方法包装。二、诗词库页面开发PoemListPage.ets2.1 交互功能概览诗词库页面是用户浏览诗词的核心入口包含三个维度交互维度实现方式数据来源搜索TextInput组件用户输入实时过滤朝代筛选标签按钮 Row6 个选项全部/先秦/唐/五代/宋/元类型筛选标签按钮 Row5 个选项全部/五绝/七律/词/乐府2.2 数据过滤逻辑之前我们使用了get filteredPoems()访问器但在运行时发现其在模板中会返回undefined// ❌ 不可行——get 访问器在模板返回 undefinedgetfilteredPoems():PoemItem[]{// ...过滤逻辑returnresult;// 运行时始终 undefined}正确做法使用StateWatch组合StateWatch(onFilterChange)searchText:string;StateWatch(onFilterChange)activeDynasty:stringall;StateWatch(onFilterChange)activeType:stringall;StatefilteredList:PoemItem[]allPoems;// 存储过滤结果onFilterChange():void{letresult:PoemItem[]allPoems;if(this.searchText.length0){constkeyword:stringthis.searchText.toLowerCase();resultresult.filter((p:PoemItem)p.title.includes(keyword)||p.author.includes(keyword));}if(this.activeDynasty!all){resultresult.filter((p:PoemItem)p.dynastythis.activeDynasty);}if(this.activeType!all){resultresult.filter((p:PoemItem)p.typethis.activeType);}this.filteredListresult;// 更新状态触发重新渲染}工作原理当searchText、activeDynasty或activeType任一状态变化时Watch(onFilterChange)自动触发onFilterChange()方法更新filteredListUI 随之刷新。2.3 搜索框实现TextInput是鸿蒙中的文本输入组件Row(){Text().fontSize(16).margin({left:12})TextInput({placeholder:搜索诗词名称或作者...,text:this.searchText}).layoutWeight(1).backgroundColor(Color.Transparent).fontSize(14).placeholderColor($r(app.color.text_secondary)).onChange((val:string){this.searchTextval;})// 搜索框不为空时显示清除按钮if(this.searchText.length0){Text(✕).fontSize(16).fontColor($r(app.color.text_secondary)).margin({right:12}).onClick((){this.searchText;})}}.width(100%).height(44).backgroundColor($r(app.color.bg_card)).borderRadius(22)// 圆角搜索框2.4 筛选标签筛选标签的样式逻辑选中的标签用主题色填充未选中的用白色Text(filter.label).fontSize(13).fontColor(filter.namethis.activeDynasty?Color.White:$r(app.color.text_secondary)).padding({left:14,right:14,top:6,bottom:6}).backgroundColor(filter.namethis.activeDynasty?$r(app.color.accent_purple):$r(app.color.bg_card)).borderRadius(16).onClick((){this.activeDynastyfilter.name;})2.5 结果计数与空状态// 结果计数Row(){Text(共 this.filteredList.length 首).fontSize(12).fontColor($r(app.color.text_secondary))Blank()}// 空状态展示if(this.filteredList.length0){Column(){Text().fontSize(48)Text(没有找到相关诗词).fontSize(16).fontColor($r(app.color.text_secondary)).margin({top:12})}.width(100%).height(200).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}2.6 卡片列表每条诗词卡片显示序号、标题、类型标签、朝代·作者、诗文节选、点赞数BuildercreatePoemCard(poem:PoemItem){Row(){// 序号Text(poem.id.toString()).fontSize(22).fontWeight(FontWeight.Bold).fontColor($r(app.color.accent_purple)).opacity(0.3)Column(){Row(){Text(poem.title).fontSize(17).fontWeight(FontWeight.Bold)Text(poem.type).fontSize(10).fontColor($r(app.color.accent_purple)).padding({left:6,right:6,top:2,bottom:2}).backgroundColor($r(app.color.accent_purple)15).borderRadius(4)}Text(poem.dynasty · poem.author).fontSize(13).fontColor($r(app.color.text_secondary))// 诗文节选最多两行Text(poem.content[0](poem.content.length1?poem.content[1]:)).fontSize(14).fontColor($r(app.color.text_secondary)).maxLines(1).textOverflow({overflow:TextOverflow.Ellipsis})Text(❤ poem.likes.toString()).fontSize(12).fontColor($r(app.color.accent_red))}}.width(100%).padding(14).backgroundColor($r(app.color.bg_card)).borderRadius(12).onClick((){router.pushUrl({url:pages/PoemDetailPage,params:{poemId:poem.id}});})}三、跨页面参数传递3.1 从作者页跳转到诗词库并搜索AuthorPage点击诗人卡片后会跳转到诗词库并自动填入作者名进行搜索// AuthorPage.ets.onClick((){router.pushUrl({url:pages/PoemListPage,params:{searchAuthor:author.name}});})// PoemListPage.ets — 接收参数aboutToAppear():void{constparamsrouter.getParams()asRecordstring,Object;if(paramsparams[searchAuthor]!undefined){this.searchTextparams[searchAuthor]asstring;// Watch 会自动触发 onFilterChange更新 filteredList}}四、Builder 中的 if 条件在 ArkTS 中if条件语句可以直接在build()和Builder中使用BuildercreatePoemCard(poem:PoemItem){Row(){if(this.editMode){Circle()// 编辑模式下的选择框.width(22).height(22).stroke($r(app.color.accent_purple)).strokeWidth(2).fill(Color.Transparent)}// ... 其余内容}}但需要注意if条件内部只能包含 UI 组件语法不能包含变量声明、函数调用赋值等。小结本章完成了首页和诗词库两个核心页面的开发涵盖了渐变背景卡片的设计Grid 网格布局的使用Builder 组件复用技巧搜索 双维度筛选的实现跨页面参数传递数据过滤的最佳实践State Watch在下一章中我们将继续开发诗词详情和作者天地两个页面深入复杂数据展示和交互设计。【系列目录】一项目初始化与架构设计二首页与诗词库页面开发 ← 本文三诗词详情与作者天地页面开发四收藏页面与底部导航实现五编译调试与问题修复经验