HarmonyOS 实战教程(四):首页布局实现 —— Swiper 轮播、Grid 网格与组件化 —— 以「柚兔自测量表」为例
一、首页整体结构
MeCharts 首页HomeView承载了所有量表入口,整体结构为:顶部导航栏 + 可滚动内容区(轮播图 + 分类网格)。
@Preview@ComponentV2exportstruct HomeView{@LocalglobalInfoModel:GlobalInfoModel=AppStorage.get('GlobalInfoModel')||newGlobalInfoModel()privatepageContext:PageContext=AppStorage.get('pageContext')asPageContext;@LocalimgHeight:number=190@LocalimgWidth:number=264@LocalimgList:Resource[]=newArray(3).fill('').map((item:string,index)=>{return$r(`app.media.banner_0${index+1}`)})privateasdList:AbcModel[]=[newAbcModel($r('app.media.ic_item_abc'),'ABC量表','(自闭症行为评定量表)',1),newAbcModel($r('app.media.ic_item_cars'),'CARS量表','(儿童期孤独症评定量表)',2),newAbcModel($r('app.media.ic_item_move'),'多动症量表','(多动症诊断标准量表)',3),]privatecareerList:AbcModel[]=[newAbcModel($r('app.media.ic_item_mbti'),'MBTI测试','(MBTI职业性格测试完整版)',1),]privateemotionList:AbcModel[]=[newAbcModel($r('app.media.ic_item_depress'),'SDS量表','(抑郁自评量表)',1),newAbcModel($r('app.media.ic_item_anxious'),'SAS量表','(焦虑症自评量表)',2),newAbcModel($r('app.media.ic_item_relationship'),'ECR量表','(亲密关系经历量表)',3),]build(){Column(){this.buildTopBar();this.buildContentView();}.height('100%').width('100%').backgroundColor($r('app.color.color_background'))}}1.1 数据模型设计
所有量表入口使用统一的AbcModel数据模型:
@ObservedexportclassAbcModel{img:Resource|undefined=undefinedtitle:string=''subTitle:string=''id:number=0score?:string=''type?:number=0constructor(img:Resource,title?:string,subTitle?:string,id?:number,score?:string,type?:number){// 构造函数赋值...}}@Observed装饰器使该类的实例可被 ArkUI 框架观测,当属性变化时自动触发 UI 刷新。这在历史记录页面中更新分数显示时非常关键。
二、Swiper 轮播组件
2.1 基本使用
首页顶部使用UISwiper(AGConnect 提供的增强轮播组件)展示推荐量表:
UISwiper({imgWidth:this.imgWidth,imgHeight:this.imgHeight,imgList:this.imgList,isLoop:true,isCovered:false,onImageClick:(index:number)=>{if(index===0){this.pageContext.openPage({param:{title:'MBTI测试'}asResultParams,routerName:'MbtiListPage',},true);}elseif(index===1){this.pageContext.openPage({param:{title:'ABC量表'}asResultParams,routerName:'AbcListPage',},true);}elseif(index===2){this.pageContext.openPage({param:{title:'SAS量表',type:FormType.TYPE_SDS}asResultParams,routerName:'MoveListPage',},true);}}})轮播图的点击事件通过onImageClick回调处理,根据索引跳转到不同量表页面。
2.2 原生 Swiper 的使用方式
如果使用原生 Swiper 组件,基础用法如下:
Swiper(){ForEach(this.imgList,(img:Resource)=>{Image(img).width(this.imgWidth).height(this.imgHeight).borderRadius(12).onClick(()=>{// 点击跳转逻辑})})}.autoPlay(true).interval(3000).indicator(true).loop(true).cachedCount(2)原生 Swiper 的关键属性:
| 属性 | 说明 |
|---|---|
autoPlay | 是否自动播放 |
interval | 自动播放间隔(ms) |
indicator | 是否显示导航指示器 |
loop | 是否循环播放 |
cachedCount | 预缓存页面数 |
三、Grid 网格布局
3.1 分类网格实现
首页使用 Grid 布局展示三大分类的量表入口:
@BuilderbuildAbc(){// 分类标题Row({space:10}){Text().width(3).height(15).backgroundColor($r('app.color.color_default'));Text('自闭症相关').fontWeight(FontWeight.Bold).fontSize(16)}.margin({left:12})// 量表网格Grid(){ForEach(this.asdList,(asd:AbcModel,index)=>{GridItem(){Column({space:8}){Row({space:8}){Image(asd.img).width(24);Text(asd.title).fontWeight(FontWeight.Medium);};Text(asd.subTitle).fontSize(12);}.alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center).borderRadius(15).width('100%').height('100%').backgroundColor($r('app.color.color_card'))}.onClick(()=>{// 跳转到对应量表页面})})}.padding(12).columnsTemplate('1fr 1fr')// 两列等宽.rowsTemplate('1fr 1fr')// 两行等高.columnsGap(10).rowsGap(10).width('100%').height(160)}3.2 Grid 核心属性
columnsTemplate / rowsTemplate是 Grid 最核心的属性,定义了网格的行列结构:
.columnsTemplate('1fr 1fr')// 两列,每列等宽.rowsTemplate('1fr 1fr')// 两行,每行等高fr是分数单位,1fr 1fr表示两列各占 1/2。更复杂的布局如:
.columnsTemplate('1fr 2fr 1fr')// 三列,中间列是两侧的两倍宽.rowsTemplate('auto auto')// 两行,高度由内容决定columnsGap / rowsGap控制行列间距:
.columnsGap(10)// 列间距 10vp.rowsGap(10)// 行间距 10vp3.3 不同分类的布局差异
MeCharts 根据量表数量灵活调整布局:
| 分类 | 量表数量 | 布局方式 | 高度 |
|---|---|---|---|
| 自闭症相关 | 3个 | 2列×2行 | 160 |
| 职业性格相关 | 1个 | 2列×1行 | 80 |
| 情绪相关 | 3个 | 2列×2行 | 160 |
职业性格分类只有一个量表,使用单行布局;其他分类使用 2×2 网格,空位自然留白。
四、TopBar 通用顶部导航栏
4.1 组件设计
TopBar 是全应用复用率最高的组件,支持标题、返回按钮和右侧自定义内容:
@ComponentV2exportstruct TopBar{privateglobalInfoModel:GlobalInfoModel=AppStorage.get('GlobalInfoModel')||newGlobalInfoModel();@Paramtitle:string=''@ParamshowBackButton:boolean=true@ParambgColor:ResourceColor=$r('app.color.color_default')@ParamtitleColor:ResourceColor=Color.White@ParambarHeight:number=80@ParamstatusBarHeight:number=this.globalInfoModel.statusBarHeight@EventonBack?:()=>void|boolean@BuilderParamrightContent?:()=>voidhandleBack(){if(this.onBack){constresult=this.onBack()if(result===false){return// 返回 false 可阻止默认行为}}}build(){RelativeContainer(){if(this.showBackButton){Image($r('app.media.ic_back')).width(20).height(20).id('backBtn').alignRules({left:{anchor:'__container__',align:HorizontalAlign.Start},center:{anchor:'__container__',align:VerticalAlign.Bottom}}).onClick(()=>{this.handleBack()})}Text(this.title).fontSize(18).fontWeight(FontWeight.Bold).fontColor(this.titleColor).id('title').alignRules({center:{anchor:'__container__',align:VerticalAlign.Bottom},middle:{anchor:'__container__',align:HorizontalAlign.Center}})if(this.rightContent){Row(){this.rightContent()}.id('rightContent').alignRules({right:{anchor:'__container__',align:HorizontalAlign.End},center:{anchor:'__container__',align:VerticalAlign.Bottom}})}}.width('100%').height(this.barHeight).backgroundColor(this.bgColor).padding({top:this.statusBarHeight,bottom:20,left:12,right:12})}}4.2 RelativeContainer 相对布局
TopBar 使用RelativeContainer实现左右中三栏布局,这是 HarmonyOS 提供的相对定位容器:
- 返回按钮:锚定容器左侧 + 垂直居中
- 标题:锚定容器水平居中 + 垂直居中
- 右侧内容:锚定容器右侧 + 垂直居中
相比 Flex 布局,RelativeContainer 在处理"左右对齐 + 中间居中"的场景时更简洁。
4.3 使用示例
首页使用 TopBar(无返回按钮 + 自定义右侧):
TopBar({title:'自测量表',showBackButton:false,rightContent:()=>{this.buildRightContent()},})子页面使用 TopBar(有返回按钮):
TopBar({title:this.title,onBack:()=>{this.pageContext.popPage(true)}})五、@ComponentV2 与 @Component 的选择
MeCharts 中两种组件声明方式并存:
@ComponentV2:新版本装饰器,支持@Param、@Event、@Local、@Monitor等,类型安全更好@Component:经典版本装饰器,支持@Prop、@Link、@State等
| 特性 | @Component | @ComponentV2 |
|---|---|---|
| 状态装饰器 | @State, @Prop, @Link | @Local, @Param, @Event |
| 状态观测 | 基于代理 | 基于拦截器,性能更优 |
| 参数传递 | 构造函数传入 | @Param 声明式 |
| 事件回调 | 箭头函数 | @Event 装饰器 |
| 监听变化 | @Watch | @Monitor |
新项目推荐优先使用@ComponentV2。
六、小结
本篇讲解了首页的 Swiper 轮播、Grid 网格布局和 TopBar 通用组件的设计实现。通过组件化封装和统一数据模型,首页的代码结构清晰、复用性强。下一篇将深入量表答题页面的实现,讲解自定义 Radio 组件与表单交互。
