
16 — Tabs 组件与 MSVTabs 封装一、引言短视频应用的核心交互是多页签切换首页、朋友、发布、消息、我的。HarmonyOS 提供 Tabs/TabContent 组件承载这一能力但若每个页面各自维护一套 TabBar 样式必然导致视觉漂移与维护成本上升。本项目在 common 层封装了 MSVTabs 公共组件统一手机端底部页签与首页次级页签的样式交互同时为 TV 智慧屏单独实现 TvTabs。本文结合common/multishortvideobase/src/main/ets/components/MSVTabs.ets、products/tv/src/main/ets/components/TvTabs.ets讲解封装思路与多设备差异化。二、Tabs/TabContent 基础用法Tabs 由页签栏 内容区组成TabContent 承载页面内容通过 tabBar 属性自定义页签 UITabs({ index: this.activeIndex }) { TabContent() { HomePage() }.tabBar(this.MyTabBar(0)) TabContent() { MinePage() }.tabBar(this.MyTabBar(1)) } .barPosition(BarPosition.End) // 页签位于底部 .barOverlap(true) // 页签悬浮于内容之上 .onChange((index: number) { this.activeIndex index; })常用属性barPosition 控制页签栏位置本项目底部 End / 顶部 Start、barOverlap 控制页签是否悬浮true、barMode 控制 Fixed/ScrollableBarMode.Scrollable、barHeight 控制高度56vp。三、MSVTabs 公共组件封装MSVTabs 以数据驱动页签配置收敛在 MSVDataModel内容 Builder、文本、图标、是否仅图标export class MSVDataModel { public content?: BuilderCallback | null; // 页签内容 public text?: ResourceStr; // 页签文字 public iconOnly?: boolean; // 是否纯图标 public icon?: Resource; public iconDark?: Resource; // 深色图标 public iconSize?: Length; }组件对外参数MSVTabs.etsComponentV2 export struct MSVTabs { Require Param data: MSVDataModel[]; // 必传 Local activeIndex: number 0; // 内部选中态 Param useFloating: boolean true; // 悬浮 TabBar Param index: number 0; // 初始索引 Param showBarUnderline: boolean false; // 选中下划线 Param barPosition: BarPosition BarPosition.Start; Param onIndexChange: (index: number) void () {}; aboutToAppear(): void { this.activeIndex this.index; } }Require Param强制调用方传 dataLocal activeIndex内部维护选中态onIndexChange 把切换事件抛给外部如切到我的时切换深浅色。四、自定义 TabBar 构建MSVTabs 用 Builder 定义页签样式区分纯图标与图标文字支持选中下划线Builder MSVTabBar(params: MSVDataModel, currentIndex: number) { if (params.iconOnly) { Image(this.isDark ? params.iconDark : params.icon) .width(params.iconSize).aspectRatio(1) } else { Column() { Text(params.text) .fontColor(this.activeIndex currentIndex ? this.selectedLightColor : this.lightColor) } .border({ width: { bottom: this.showBarUnderline (this.activeIndex currentIndex) ? 1 : 0 } }) } }MainTabsViewModel中发布页签即纯图标形态加号this.mainTabsData.push(new MSVDataModel(home, $r(app.string.home_title))); this.mainTabsData.push(new MSVDataModel(add, , true, $r(app.media.ic_plus), $r(app.media.ic_plus_dark)));其余朋友、消息、我的为图文形态仅传文本与内容 Builder。五、HdsTabs 悬浮页签与降级策略大屏启用系统 HDS 悬浮页签小屏或不支持时降级为原生 Tabs用 canIUse 能力探测 useFloating 开关双轨控制if (canIUse(SystemCapability.UIDesign.HDSComponent.Core) this.useFloating) { HdsTabs({ index: this.activeIndex }) { this.MSVTabContent(this.data) } .barPosition(BarPosition.End) .barFloatingStyle({ barSideMargin: 16, barBottomMargin: this.barBottomMargin, ... }) .barHeight(44) } else { Tabs({ index: this.activeIndex }) { this.MSVTabContent(this.data) } .barPosition(this.barPosition) .barBackgroundBlurStyle(BlurStyle.NONE) .barHeight(56).barMode(BarMode.Scrollable, { margin: 8 }) }首页次级页签 SubTabsSubTabsComponent.ets关闭悬浮并开启下划线useFloating: false, index: 4, showBarUnderline: true数据来自 SubTabsViewModel商城、直播、关注、推荐等。六、TV 端 TvTabs 差异化TV 端无触摸TvTabs 用横向 List 构建页签选中态由全局 currentBottomIndex 驱动天然支持遥控器聚焦ComponentV2 export struct TVTabs { Consumer(currentBottomIndex) currentBottomIndex: number 2; Local btnList: ArrayResource [推荐, 关注, 朋友, 加号, 消息, 我的]; // 横向 List 渲染当前项高亮背景与文字色 }与 MSVTabs 的差异对比如下维度MSVTabs手机/平板TvTabs智慧屏容器Tabs / HdsTabs横向 List位置底部悬浮或顶部顶部居中胶囊交互点击、滑动遥控器方向键 点击状态Param LocalConsumer 全局索引七、总结与最佳实践用数据模型驱动页签新增页签只加一条数据内容与图标统一收敛。参数化 Builder 自定义 TabBar一套组件覆盖底部、顶部、纯图标、下划线多种形态。canIUse 探测 useFloating 开关实现 HDS 悬浮与原生 Tabs 双轨降级。设备差异收敛在产品层手机/平板复用 MSVTabsTV 独立实现 TvTabs。onIndexChange 只抛事件不写业务保证公共组件与业务解耦。