【鸿蒙原生应用开发实战】第二篇首页开发——宠物卡片快捷入口动态信息流上一篇我们搭好了项目架子这一篇开始写真实代码。首页是一个 App 的门面我们的萌宠日记首页包含了宠物切换、快速入口、动态信息流三大模块麻雀虽小五脏俱全。本文手把手带你实现全部 UI并讲解 ArkTS 的核心语法。一、首页整体布局先看页面结构Index.ets ├── 顶部标题栏 (buildHeader) ← 萌宠日记 通知按钮 ├── 宠物卡片区 (buildPetCards) ← 横向滚动 选中卡片信息面板 ├── 快捷操作栏 (buildQuickActions) ← 添加宠物/相册/提醒/成长记录 └── 萌宠动态 (buildMoments) ← 动态列表 Feeds每个模块都是一个Builder装饰的方法在build()中按顺序组装。这种组件化拆分的手段是管理 ArkTS 复杂页面的核心技巧。二、数据类型定义在 Index.ets 顶部我们定义了两个接口interfacePet{id:number;name:string;type:string;// 表情符号如 breed:string;// 品种如 英短蓝猫age:string;// 年龄如 2岁3个月avatar:string;weight:string;// 体重如 4.5kgvaccineDate:string;// 最近疫苗日期dewormDate:string;// 最近驱虫日期}interfaceMoment{id:number;petName:string;// 关联宠物名content:string;// 动态内容time:string;// 发布时间描述likes:number;// 点赞数imageCount:number;// 配图数量}在 ArkTS 严格模式下所有对象字面量必须有显式类型声明arkts-no-untyped-obj-literals规则。所以我们在initPets()中初始化数据时this.pets [...]的数组元素必须符合Pet接口。三、问题首页开发——宠物卡片横向滚动3.1 宠物头像选择器这是首页最核心的交互组件——用户通过横向滑动选择宠物选中后下方显示该宠物的详细信息BuilderbuildPetCards(){Column(){Scroll(){Row(){ForEach(this.pets,(pet:Pet,index?:number){Column(){Stack(){Column().width(68).height(68).borderRadius(34).backgroundColor(this.selectedPetIndex(indexasnumber)?#FF6B35:#F0F0F0)Text(pet.type).fontSize(30)// 显示 //}.width(68).height(68)Text(pet.name).fontSize(13).fontWeight(FontWeight.Medium).fontColor(#333333)Text(pet.breed).fontSize(10).fontColor(#999999)}.margin({right:16}).onClick((){this.selectedPetIndexindexasnumber;})},(pet:Pet)pet.id.toString())}.padding({left:16})}.scrollable(ScrollDirection.Horizontal).height(120)// ... 下方信息面板}}关键知识点ScrollScrollDirection.Horizontal实现横向滚动的标准组合。Scroll包裹Row设置scrollable(ScrollDirection.Horizontal)当内容超出屏幕宽度时自动可滑动。Stack层叠布局Stack用于将头像圆形背景和 Emoji 叠加在一起。这比用RowColumn更简洁且支持绝对定位。选中状态切换通过selectedPetIndex状态变量和三元表达式动态切换背景色.backgroundColor(this.selectedPetIndexindex?#FF6B35:#F0F0F0)3.2 选中宠物的信息面板点击宠物头像后下方显示三列信息 详情入口Row(){Column(){Text(this.pets[this.selectedPetIndex].weight).fontSize(16).fontWeight(FontWeight.Bold).fontColor(#FF6B35)Text(体重).fontSize(10).fontColor(#999999)}.layoutWeight(1).alignItems(HorizontalAlign.Center)Column(){Text(this.pets[this.selectedPetIndex].age).fontSize(16).fontWeight(FontWeight.Bold).fontColor(#3498DB)Text(年龄)}.layoutWeight(1).alignItems(HorizontalAlign.Center)// ... 品种列Column(){Text(详情).fontSize(12).fontColor(#FF6B35)}.layoutWeight(1).alignItems(HorizontalAlign.Center).onClick((){router.pushUrl({url:pages/PetDetailPage,params:{petId:this.pets[this.selectedPetIndex].id}});})}.width(100%).padding(12).backgroundColor(#FFFFFF).borderRadius(10)知识点layoutWeight(1)实现等分layoutWeight(1)让四个 Column 平分 Row 的宽度比写死百分比更灵活。这是 ArkUI 中实现等分布局的推荐做法。四、快捷操作栏四个功能入口整齐排列BuilderbuildQuickActions(){Row(){Column(){Text(➕).fontSize(22)Text(添加宠物).fontSize(11).fontColor(#666666)}.layoutWeight(1).alignItems(HorizontalAlign.Center).onClick(()router.pushUrl({url:pages/AddPetPage}))Column(){Text().fontSize(22)Text(相册).fontSize(11).fontColor(#666666)}.layoutWeight(1).alignItems(HorizontalAlign.Center).onClick(()router.pushUrl({url:pages/AlbumPage}))Column(){Text().fontSize(22)Text(提醒).fontSize(11).fontColor(#666666)}.layoutWeight(1).alignItems(HorizontalAlign.Center).onClick(()router.pushUrl({url:pages/ReminderPage}))Column(){Text().fontSize(22)Text(成长记录).fontSize(11).fontColor(#666666)}.layoutWeight(1).alignItems(HorizontalAlign.Center).onClick(()router.pushUrl({url:pages/PetDetailPage,params:{petId:...}}))}.width(100%).padding(14).backgroundColor(#FFFFFF).margin({top:12,left:16,right:16}).borderRadius(12)}这里有一个有意思的设计细节四个入口中添加宠物和成长记录跳转到不同页面但成长记录复用了PetDetailPage通过 params 传参区分。这种页面复用的策略可以减少页面数量保持路由简洁。五、萌宠动态信息流5.1 动态列表渲染BuilderbuildMoments(){Column(){Row(){Text( 萌宠动态).fontSize(16).fontWeight(FontWeight.Bold).fontColor(#1A1A2E)Blank()Text(共${this.moments.length}条).fontSize(12).fontColor(#999999)}.width(100%).padding({left:16,right:16,top:16})ForEach(this.moments,(moment:Moment){Column(){// 头部头像 名字 时间Row(){Stack(){Column().width(36).height(36).borderRadius(18).backgroundColor(#FFE0D0)Text(moment.petName团子?:moment.petName豆豆?:).fontSize(18)}Column(){Text(moment.petName).fontSize(14).fontWeight(FontWeight.Medium)Text(moment.time).fontSize(10).fontColor(#BBBBBB)}.margin({left:8}).layoutWeight(1)Text(···).fontSize(14).fontColor(#CCCCCC)}.width(100%)// 正文Text(moment.content).fontSize(13).fontColor(#666666).lineHeight(20).width(100%).margin({top:8})// 配图占位有图才显示if(moment.imageCount0){Row(){ForEach([1,2,3],(i:number){if(imoment.imageCount){Column().width(80).height(80).backgroundColor(#F0F0F0).borderRadius(6).margin({right:6})}},(i:number)i.toString())}.width(100%).margin({top:6}).height(80)}// 底部互动点赞 评论Row(){Blank()Text(❤️${moment.likes}).fontSize(12).fontColor(#999999).margin({left:12})Text( 评论).fontSize(12).fontColor(#999999).margin({left:12})}.width(100%).margin({top:6})}.width(100%).padding(14).backgroundColor(#FFFFFF).borderRadius(10).margin({top:8,left:16,right:16}).alignItems(HorizontalAlign.Start)},(moment:Moment)moment.id.toString())}.width(100%)}5.2 ForEach 的关键参数ForEach的第三个参数是键值生成器用于追踪列表项的增删改ForEach(this.moments,// 数据源(moment:Moment){...},// UI 模板(moment:Moment)moment.id.toString()// key 生成器)为什么要传 key提升 diff 更新性能避免列表项重排时 UI 闪烁符合 ArkTS 的渲染优化要求5.3 条件渲染的优雅写法配图区域只在moment.imageCount 0时才渲染if(moment.imageCount0){Row(){/* 图片占位 */}}这是 ArkTS 中条件渲染的标准写法。不需要v-if或*ngIf直接用if语句。六、State 装饰器与响应式编程整个首页的核心状态只有三个变量Statepets:Pet[][];// 宠物列表Statemoments:Moment[][];// 动态列表StateselectedPetIndex:number0;// 选中的宠物索引State是 ArkTS 响应式系统的基石特性说明自动追踪State变量被读取时自动记录依赖精准更新变量变化时只重新渲染依赖它的组件不可变替换数组/对象需要整体替换而非修改属性注意State只能装饰属于组件自身的状态如果状态需要跨组件共享需要使用Prop、Link或Provide/Consume。七、UI 设计技巧总结7.1 颜色系统我们定义了一套隐式的色彩规范用途色值使用场景主色调#FF6B35选中状态、按钮、链接深色文字#333333标题、正文灰色文字#999999辅助信息、说明浅灰文字#BBBBBB时间戳、次要信息页面背景#F5F5F5整体背景色卡片背景#FFFFFF所有卡片7.2 卡片阴影与圆角所有卡片统一风格.backgroundColor(#FFFFFF).borderRadius(10)// 圆角.margin({top:8,left:16,right:16})虽然没有显式加阴影但白色卡片在浅灰背景上已经产生视觉层次感。需要阴影时可以用.shadow()方法。7.3 Emoji 的妙用全篇大量使用了 Emoji 作为图标场景Emoji宠物类型 快捷入口➕ 动态互动❤️ 通知按钮这样做的好处是零资源开销——不需要引入任何图标库或图片资源一个 Unicode 字符搞定。对小型应用来说这是性价比最高的 UI 方案。八、完整 build() 组装build():void{Column(){this.buildHeader()// 顶部标题栏Scroll(){Column(){this.buildPetCards()// 宠物卡片this.buildQuickActions()// 快捷操作this.buildMoments()// 动态信息流}.width(100%).padding({bottom:20})}.scrollable(ScrollDirection.Vertical).layoutWeight(1).width(100%)}.width(100%).height(100%).backgroundColor(#F5F5F5)}整个页面被包裹在一个Column中其中Scroll占满剩余空间layoutWeight(1)内部再嵌套Column按顺序排列三个模块。这种垂直滚动 横向子滚动的嵌套结构是移动端首页最经典的布局模式。九、知识点串讲知识点本页示例Builder方法拆分4个 Builder 分别对应4个 UI 区块State响应式变量pets, moments, selectedPetIndexScroll双向滚动纵向整页 横向宠物卡片ForEach列表渲染宠物列表 动态列表Stack层叠布局圆形头像背景 EmojilayoutWeight等分快捷入口四等分if条件渲染配图区域的显示/隐藏router.pushUrl跳转到详情/添加/相册/提醒页十、下篇预告下一篇我们做添加宠物页面 宠物详情页TextInput 表单的完整实现标签选择器宠物类型/性别三个 Tab 页签切换健康档案/体重记录/疫苗记录柱状图式体重趋势展示敬请期待系列导航:第一篇项目搭建与架构第二篇首页开发本文第三篇表单与详情页第四篇相册与提醒功能第五篇总结与最佳实践
【鸿蒙原生应用开发实战】第二篇:首页开发——宠物卡片+快捷入口+动态信息流
发布时间:2026/6/13 2:48:05
【鸿蒙原生应用开发实战】第二篇首页开发——宠物卡片快捷入口动态信息流上一篇我们搭好了项目架子这一篇开始写真实代码。首页是一个 App 的门面我们的萌宠日记首页包含了宠物切换、快速入口、动态信息流三大模块麻雀虽小五脏俱全。本文手把手带你实现全部 UI并讲解 ArkTS 的核心语法。一、首页整体布局先看页面结构Index.ets ├── 顶部标题栏 (buildHeader) ← 萌宠日记 通知按钮 ├── 宠物卡片区 (buildPetCards) ← 横向滚动 选中卡片信息面板 ├── 快捷操作栏 (buildQuickActions) ← 添加宠物/相册/提醒/成长记录 └── 萌宠动态 (buildMoments) ← 动态列表 Feeds每个模块都是一个Builder装饰的方法在build()中按顺序组装。这种组件化拆分的手段是管理 ArkTS 复杂页面的核心技巧。二、数据类型定义在 Index.ets 顶部我们定义了两个接口interfacePet{id:number;name:string;type:string;// 表情符号如 breed:string;// 品种如 英短蓝猫age:string;// 年龄如 2岁3个月avatar:string;weight:string;// 体重如 4.5kgvaccineDate:string;// 最近疫苗日期dewormDate:string;// 最近驱虫日期}interfaceMoment{id:number;petName:string;// 关联宠物名content:string;// 动态内容time:string;// 发布时间描述likes:number;// 点赞数imageCount:number;// 配图数量}在 ArkTS 严格模式下所有对象字面量必须有显式类型声明arkts-no-untyped-obj-literals规则。所以我们在initPets()中初始化数据时this.pets [...]的数组元素必须符合Pet接口。三、问题首页开发——宠物卡片横向滚动3.1 宠物头像选择器这是首页最核心的交互组件——用户通过横向滑动选择宠物选中后下方显示该宠物的详细信息BuilderbuildPetCards(){Column(){Scroll(){Row(){ForEach(this.pets,(pet:Pet,index?:number){Column(){Stack(){Column().width(68).height(68).borderRadius(34).backgroundColor(this.selectedPetIndex(indexasnumber)?#FF6B35:#F0F0F0)Text(pet.type).fontSize(30)// 显示 //}.width(68).height(68)Text(pet.name).fontSize(13).fontWeight(FontWeight.Medium).fontColor(#333333)Text(pet.breed).fontSize(10).fontColor(#999999)}.margin({right:16}).onClick((){this.selectedPetIndexindexasnumber;})},(pet:Pet)pet.id.toString())}.padding({left:16})}.scrollable(ScrollDirection.Horizontal).height(120)// ... 下方信息面板}}关键知识点ScrollScrollDirection.Horizontal实现横向滚动的标准组合。Scroll包裹Row设置scrollable(ScrollDirection.Horizontal)当内容超出屏幕宽度时自动可滑动。Stack层叠布局Stack用于将头像圆形背景和 Emoji 叠加在一起。这比用RowColumn更简洁且支持绝对定位。选中状态切换通过selectedPetIndex状态变量和三元表达式动态切换背景色.backgroundColor(this.selectedPetIndexindex?#FF6B35:#F0F0F0)3.2 选中宠物的信息面板点击宠物头像后下方显示三列信息 详情入口Row(){Column(){Text(this.pets[this.selectedPetIndex].weight).fontSize(16).fontWeight(FontWeight.Bold).fontColor(#FF6B35)Text(体重).fontSize(10).fontColor(#999999)}.layoutWeight(1).alignItems(HorizontalAlign.Center)Column(){Text(this.pets[this.selectedPetIndex].age).fontSize(16).fontWeight(FontWeight.Bold).fontColor(#3498DB)Text(年龄)}.layoutWeight(1).alignItems(HorizontalAlign.Center)// ... 品种列Column(){Text(详情).fontSize(12).fontColor(#FF6B35)}.layoutWeight(1).alignItems(HorizontalAlign.Center).onClick((){router.pushUrl({url:pages/PetDetailPage,params:{petId:this.pets[this.selectedPetIndex].id}});})}.width(100%).padding(12).backgroundColor(#FFFFFF).borderRadius(10)知识点layoutWeight(1)实现等分layoutWeight(1)让四个 Column 平分 Row 的宽度比写死百分比更灵活。这是 ArkUI 中实现等分布局的推荐做法。四、快捷操作栏四个功能入口整齐排列BuilderbuildQuickActions(){Row(){Column(){Text(➕).fontSize(22)Text(添加宠物).fontSize(11).fontColor(#666666)}.layoutWeight(1).alignItems(HorizontalAlign.Center).onClick(()router.pushUrl({url:pages/AddPetPage}))Column(){Text().fontSize(22)Text(相册).fontSize(11).fontColor(#666666)}.layoutWeight(1).alignItems(HorizontalAlign.Center).onClick(()router.pushUrl({url:pages/AlbumPage}))Column(){Text().fontSize(22)Text(提醒).fontSize(11).fontColor(#666666)}.layoutWeight(1).alignItems(HorizontalAlign.Center).onClick(()router.pushUrl({url:pages/ReminderPage}))Column(){Text().fontSize(22)Text(成长记录).fontSize(11).fontColor(#666666)}.layoutWeight(1).alignItems(HorizontalAlign.Center).onClick(()router.pushUrl({url:pages/PetDetailPage,params:{petId:...}}))}.width(100%).padding(14).backgroundColor(#FFFFFF).margin({top:12,left:16,right:16}).borderRadius(12)}这里有一个有意思的设计细节四个入口中添加宠物和成长记录跳转到不同页面但成长记录复用了PetDetailPage通过 params 传参区分。这种页面复用的策略可以减少页面数量保持路由简洁。五、萌宠动态信息流5.1 动态列表渲染BuilderbuildMoments(){Column(){Row(){Text( 萌宠动态).fontSize(16).fontWeight(FontWeight.Bold).fontColor(#1A1A2E)Blank()Text(共${this.moments.length}条).fontSize(12).fontColor(#999999)}.width(100%).padding({left:16,right:16,top:16})ForEach(this.moments,(moment:Moment){Column(){// 头部头像 名字 时间Row(){Stack(){Column().width(36).height(36).borderRadius(18).backgroundColor(#FFE0D0)Text(moment.petName团子?:moment.petName豆豆?:).fontSize(18)}Column(){Text(moment.petName).fontSize(14).fontWeight(FontWeight.Medium)Text(moment.time).fontSize(10).fontColor(#BBBBBB)}.margin({left:8}).layoutWeight(1)Text(···).fontSize(14).fontColor(#CCCCCC)}.width(100%)// 正文Text(moment.content).fontSize(13).fontColor(#666666).lineHeight(20).width(100%).margin({top:8})// 配图占位有图才显示if(moment.imageCount0){Row(){ForEach([1,2,3],(i:number){if(imoment.imageCount){Column().width(80).height(80).backgroundColor(#F0F0F0).borderRadius(6).margin({right:6})}},(i:number)i.toString())}.width(100%).margin({top:6}).height(80)}// 底部互动点赞 评论Row(){Blank()Text(❤️${moment.likes}).fontSize(12).fontColor(#999999).margin({left:12})Text( 评论).fontSize(12).fontColor(#999999).margin({left:12})}.width(100%).margin({top:6})}.width(100%).padding(14).backgroundColor(#FFFFFF).borderRadius(10).margin({top:8,left:16,right:16}).alignItems(HorizontalAlign.Start)},(moment:Moment)moment.id.toString())}.width(100%)}5.2 ForEach 的关键参数ForEach的第三个参数是键值生成器用于追踪列表项的增删改ForEach(this.moments,// 数据源(moment:Moment){...},// UI 模板(moment:Moment)moment.id.toString()// key 生成器)为什么要传 key提升 diff 更新性能避免列表项重排时 UI 闪烁符合 ArkTS 的渲染优化要求5.3 条件渲染的优雅写法配图区域只在moment.imageCount 0时才渲染if(moment.imageCount0){Row(){/* 图片占位 */}}这是 ArkTS 中条件渲染的标准写法。不需要v-if或*ngIf直接用if语句。六、State 装饰器与响应式编程整个首页的核心状态只有三个变量Statepets:Pet[][];// 宠物列表Statemoments:Moment[][];// 动态列表StateselectedPetIndex:number0;// 选中的宠物索引State是 ArkTS 响应式系统的基石特性说明自动追踪State变量被读取时自动记录依赖精准更新变量变化时只重新渲染依赖它的组件不可变替换数组/对象需要整体替换而非修改属性注意State只能装饰属于组件自身的状态如果状态需要跨组件共享需要使用Prop、Link或Provide/Consume。七、UI 设计技巧总结7.1 颜色系统我们定义了一套隐式的色彩规范用途色值使用场景主色调#FF6B35选中状态、按钮、链接深色文字#333333标题、正文灰色文字#999999辅助信息、说明浅灰文字#BBBBBB时间戳、次要信息页面背景#F5F5F5整体背景色卡片背景#FFFFFF所有卡片7.2 卡片阴影与圆角所有卡片统一风格.backgroundColor(#FFFFFF).borderRadius(10)// 圆角.margin({top:8,left:16,right:16})虽然没有显式加阴影但白色卡片在浅灰背景上已经产生视觉层次感。需要阴影时可以用.shadow()方法。7.3 Emoji 的妙用全篇大量使用了 Emoji 作为图标场景Emoji宠物类型 快捷入口➕ 动态互动❤️ 通知按钮这样做的好处是零资源开销——不需要引入任何图标库或图片资源一个 Unicode 字符搞定。对小型应用来说这是性价比最高的 UI 方案。八、完整 build() 组装build():void{Column(){this.buildHeader()// 顶部标题栏Scroll(){Column(){this.buildPetCards()// 宠物卡片this.buildQuickActions()// 快捷操作this.buildMoments()// 动态信息流}.width(100%).padding({bottom:20})}.scrollable(ScrollDirection.Vertical).layoutWeight(1).width(100%)}.width(100%).height(100%).backgroundColor(#F5F5F5)}整个页面被包裹在一个Column中其中Scroll占满剩余空间layoutWeight(1)内部再嵌套Column按顺序排列三个模块。这种垂直滚动 横向子滚动的嵌套结构是移动端首页最经典的布局模式。九、知识点串讲知识点本页示例Builder方法拆分4个 Builder 分别对应4个 UI 区块State响应式变量pets, moments, selectedPetIndexScroll双向滚动纵向整页 横向宠物卡片ForEach列表渲染宠物列表 动态列表Stack层叠布局圆形头像背景 EmojilayoutWeight等分快捷入口四等分if条件渲染配图区域的显示/隐藏router.pushUrl跳转到详情/添加/相册/提醒页十、下篇预告下一篇我们做添加宠物页面 宠物详情页TextInput 表单的完整实现标签选择器宠物类型/性别三个 Tab 页签切换健康档案/体重记录/疫苗记录柱状图式体重趋势展示敬请期待系列导航:第一篇项目搭建与架构第二篇首页开发本文第三篇表单与详情页第四篇相册与提醒功能第五篇总结与最佳实践