小雨同学 2026-09-09 10:52:35 发布前言
现有 HarmonyOS 项目已经有底部导航时,准备改成悬浮页签,最先遇到的问题通常发生在接口选择这里。
项目里原来可能使用 Tabs,页面切换、选中状态和业务数据都已经稳定。开始接触悬浮导航以后,又会碰到 HdsTabs、HdsTabsController、BottomTabBarStyle 和 barFloatingStyle。几个名称放到一起以后,很容易把它们理解成几套可以互相替换的页签组件。
实际关系要简单一些。
Tabs 属于 ArkUI,用来组织多个 TabContent 并完成页签内容切换。HdsTabs 位于 UI Design Kit,UI Design Kit 本身是符合 HarmonyOS Design System 的界面开发套件集合;HdsTabs 已经在 6.0.0(20) Beta1 的 UI Design Kit API 中出现。
悬浮页签继续使用 HdsTabs。页面需要同时设置底部页签、横向排列、内容与页签叠加,再通过 barFloatingStyle 配置悬浮区域。当前悬浮页签的基本条件可以归纳为 barPosition=BarPosition.End、vertical=false、barOverlap=true 和 barFloatingStyle。
所以,已有项目准备改底部导航时,可以先回答两个问题。
当前页面只需要普通页签切换,还是已经准备使用悬浮导航?悬浮导航后面是否还会继续处理 MiniBar、握姿跟随或者不同窗口宽度?
这两个问题确认以后,Tabs 和 HdsTabs 的选择会清楚很多。
目前我的测试环境仍然以 HarmonyOS 7 模拟器为主。基础组件、页签切换和悬浮布局可以先在模拟器中检查;左右手握姿、真实折叠状态以及最终操作手感仍然需要支持相关能力的真机。
一、先把 Tabs、HdsTabs 和悬浮页签分清楚
已有项目最容易混淆的地方,是把三者都理解成底部导航。它们所在的层级其实不同。
Tabs 负责常规页签切换
Tabs 是 ArkUI 中的页签容器,内部通过多个 TabContent 承载不同内容。barPosition 可以决定页签栏的位置,TabContent 则负责每一个页签对应的页面内容。
一个普通的底部导航可以写成:
Tabs({ barPosition: BarPosition.End}) { TabContent() { Text('首页内容') } .tabBar('首页') TabContent() { Text('任务内容') } .tabBar('任务') TabContent() { Text('我的内容') } .tabBar('我的')}这种结构已经能够覆盖大量业务页面。
例如首页、任务、个人中心三个一级入口,只需要固定底栏和内容切换,现有 Tabs 已经运行稳定,项目可以继续保持原来的导航结构。
此时没有必要因为悬浮页签出现,就先修改整个导航容器。真正需要重新判断的时候,是需求开始进入 HDS 页签范围。
HdsTabs 属于 UI Design Kit
HdsTabs 位于 UI Design Kit。它依然使用 TabContent 承载页面,因此已有业务内容通常可以继续保留。改变的主要是外层页签容器,以及 HDS 提供的页签能力。
基础结构可以写成:
import { HdsTabs, HdsTabsController} from '@kit.UIDesignKit';private controller: HdsTabsController = new HdsTabsController();HdsTabs({ controller: this.controller}) { TabContent() { Text('首页内容') } .tabBar('首页') TabContent() { Text('任务内容') } .tabBar('任务') TabContent() { Text('我的内容') } .tabBar('我的')}.barPosition(BarPosition.End).vertical(false)这里有一个对已有项目很友好的地方。
原来的首页、任务页、个人中心仍然可以放在 TabContent 里。迁移时可以先处理导航容器,业务页面暂时保持原状。
这样出现问题时,排查范围主要集中在页签结构,不会同时把业务状态、接口请求和页面布局全部卷进来。
悬浮页签是 HdsTabs 的悬浮形态
大家很容易继续寻找一个单独的悬浮页签组件。
当前悬浮形态依然建立在 HdsTabs 上。页面需要把页签放到底部、保持横向排列,让 TabBar 覆盖到 TabContent 上,再配置 barFloatingStyle。
调用关系可以整理成:
HdsTabs↓barPosition = BarPosition.End↓vertical = false↓barOverlap = true↓barFloatingStyle(...)↓底部悬浮页签所以项目里的几个对象可以这样区分:
| 对象 | 主要职责 |
|---|---|
Tabs | ArkUI 常规页签与内容切换 |
HdsTabs | HDS 页签容器 |
TabContent | 每个页签对应的业务内容 |
BottomTabBarStyle | 底部页签的图标、文字等样式 |
HdsTabsController | 页签相关控制 |
barFloatingStyle | 悬浮页签区域配置 |
| 悬浮页签 | HdsTabs 进入悬浮布局后的页面形态 |
BottomTabBarStyle 也需要单独理解。
它负责底部页签的视觉内容,和整个 HdsTabs 容器并不是同一个对象。HarmonyOS 的底部页签设计本身就包含图标、文本、选中状态等内容表达。
因此,整个结构更适合按照下面的层次去理解:
业务内容TabContent页签容器Tabs / HdsTabs底部页签样式BottomTabBarStyle悬浮布局barFloatingStyle页签控制HdsTabsController把这些层次分开以后,后面看到 MiniBar、页签显隐或者握姿跟随时,就知道应该继续检查哪一层接口。
三者的关系清楚以后,接口选择就可以回到已有项目本身。
二、现有项目怎么判断要不要换成 HdsTabs
页面选择 Tabs 还是 HdsTabs,没有必要只看底栏视觉效果。更实用的判断方式,是把当前需求和接下来准备增加的能力列出来。
普通底部导航可以继续使用 Tabs
假设一个工具应用已经有四个一级页面:
首页收藏历史设置业务只要求固定底栏、页面切换和选中状态,底栏样式也已经由项目自己维护。这种情况下,Tabs 已经能够承担当前任务。
保留现有组件可以减少改动范围,也不会因为导航改造去碰已经稳定的页面逻辑。
准备使用悬浮导航时,再进入 HdsTabs
当页面开始需要胶囊形悬浮导航,或者内容要延伸到底部导航后方时,HdsTabs 就进入了选择范围。
悬浮形态下,barOverlap=true 会让 TabBar 与 TabContent 形成叠加关系,因此内容区和底部导航之间的空间处理也会跟普通固定底栏不同。
项目可以先按照需求做一轮判断:
| 当前需求 | 可以优先考虑 |
|---|---|
| 普通顶部或底部页签 | Tabs |
| HDS 页签结构 | HdsTabs |
| 底部悬浮导航 | HdsTabs + barFloatingStyle |
| 内容延伸到页签后方 | HdsTabs + barOverlap |
| MiniBar | HDS 悬浮页签 |
| 左右手握姿适配 | HDS 悬浮页签 |
| 宽窗口继续调整悬浮导航 | HDS 页签结构 |
这里比较容易误判的是,把 HdsTabs 当成一个必须替换 Tabs 的新版本。
项目是否迁移,还是要看现有页面准备使用哪些能力。
如果普通页签已经满足需求,继续维护 Tabs 会更省事。如果页面已经确定要做悬浮导航,并且后面还会增加 MiniBar、握姿跟随或者多窗口处理,那么从 HdsTabs 开始搭结构会更合适。
智感握姿也建立在悬浮 HdsTabs 上
握姿适配可以进一步帮助判断 HdsTabs 的使用边界。
HdsTabs 进入悬浮状态以后,barFloatingStyle 中可以通过 adaptToHandedness 让底部页签栏跟随握持手变化。这个能力要求页签已经处于 barOverlap 开启的悬浮状态。
所以,当产品设计里已经出现单手操作、左右手热区或者悬浮导航跟随需求时,页面从一开始就需要考虑 HdsTabs 的悬浮结构。
当前模拟器可以先检查属性和布局,真实握姿变化仍然要留到支持对应能力的设备上。
HdsTabsController 负责控制问题
另外一个容易和样式混在一起的对象是 HdsTabsController。HdsTabsOptions 提供 controller?: HdsTabsController,因此控制器属于 HdsTabs 的控制入口。
把结构继续拆开以后,可以形成下面这组关系:
页面放什么→ TabContent用哪一种页签容器→ Tabs / HdsTabs底部页签长什么样→ BottomTabBarStyle页签是否悬浮→ barFloatingStyle页签如何被控制→ HdsTabsController这套分法在后面处理显隐、主动切换和业务状态时会很有用。
同一个问题只交给一层处理,代码也更容易维护。
三、先跑通基础 HdsTabs 再增加悬浮配置
接口选择最终还是要回到工程里验证。
当前环境已经准备好 UI Design Kit 时,可以先搭一个只有三个页签的 HdsTabs 页面。页面不加入 MiniBar、握姿或者复杂材质,只确认容器、TabContent 和底部页签能够正常工作。
先确认 UIDesignKit 能够正常导入
核心导入只有:
import { HdsTabs, HdsTabsController} from '@kit.UIDesignKit';HdsTabs 已经在 6.0.0(20) Beta1 的 UI Design Kit API 中出现,因此使用 HarmonyOS 7 环境时,要避免把 HdsTabs 本身误判为 HarmonyOS 7 才提供的组件。
页面随后创建控制器:
private controller: HdsTabsController = new HdsTabsController();再准备三个 TabContent:
HdsTabs({ controller: this.controller}) { TabContent() { this.tabPage( '首页', '当前页面用于检查 HdsTabs 的基础切换。' ) } .tabBar('首页') TabContent() { this.tabPage( '任务', '业务内容继续由 TabContent 承载。' ) } .tabBar('任务') TabContent() { this.tabPage( '我的', '页签容器变化不会要求业务内容重写。' ) } .tabBar('我的')}.barPosition(BarPosition.End).vertical(false)运行以后,先确认三个结果:
HdsTabs 正常创建TabContent 正常显示三个页签可以正常切换这三个条件成立以后,基础 HDS 页签链路就已经可用。
悬浮配置适合分步加入
基础页面已经能够正常切换以后,再增加:
.barOverlap(true)随后加入:
.barFloatingStyle({ // 悬浮页签配置})悬浮页签本身要求 barPosition=BarPosition.End、vertical=false、barOverlap=true,并配置 barFloatingStyle。
分步增加有一个很现实的好处。
页签突然不符合预期时,可以知道问题发生在普通 HdsTabs,还是发生在悬浮布局配置。
如果从第一版代码就同时加入 MiniBar、材质、渐变遮罩和握姿适配,任何一个字段出错都会扩大排查范围。
只确认当前环境已经走通基础调用
模拟器中的这张页面能够帮助确认基础工程链路。
如果 @kit.UIDesignKit 无法导入,问题需要回到 SDK 和工程环境。如果 HdsTabs 已经创建,但页面结构异常,可以继续检查容器和 TabContent。基础结构稳定以后,再进入 barOverlap 和 barFloatingStyle。
总结
现有项目遇到 Tabs、HdsTabs 和悬浮页签时,可以先把它们放到不同层级。
Tabs 负责 ArkUI 常规页签。HdsTabs 属于 UI Design Kit,业务内容仍然通过 TabContent 承载。悬浮页签继续使用 HdsTabs,通过 barPosition、vertical、barOverlap 和 barFloatingStyle 调整成悬浮布局。
项目选择时也可以保持一个比较简单的判断。
普通底栏已经满足需求,现有 Tabs 可以继续维护。页面准备使用 HDS 悬浮导航、MiniBar、智感握姿或者继续处理宽窗口布局时,再评估 HdsTabs。
已有业务页面通常不用跟着导航容器一起重写。首页、任务页、个人中心仍然可以留在 TabContent 中,先完成外层容器验证,再增加悬浮布局。
当前模拟器能够承担 HdsTabs 基础调用、页面切换和悬浮结构的验证。智感握姿、真实折叠状态、最终交互距离和设备性能仍然需要对应真机环境。
完整代码
Main.ets
/** * HarmonyOS 7 悬浮页签深度实战 01 * */import { HdsTabs, HdsTabsController} from '@kit.UIDesignKit';@Entry@Componentstruct Main { /** * HdsTabs 的控制对象。 * 页签状态控制可以继续复用这个对象。 */ private controller: HdsTabsController = new HdsTabsController(); @Builder private tabPage( title: string, description: string, label: string ) { Column({ space: 18 }) { Column({ space: 6 }) { Text('HarmonyOS 7 悬浮页签') .fontSize(28) .fontWeight(FontWeight.Bold) .fontColor('#11182C') .width('100%') Text('HdsTabs 基础模式') .fontSize(16) .fontColor('#68708A') .width('100%') } .width('100%') .alignItems(HorizontalAlign.Start) Column({ space: 8 }) { Text('当前检查') .fontSize(14) .fontWeight(FontWeight.Medium) .fontColor('#5065E8') .width('100%') Text( '当前页面检查 HdsTabs、' + 'TabContent 和底部页签的基础调用。' ) .fontSize(13) .fontColor('#68708A') .lineHeight(20) .width('100%') Text( '基础页面保持普通底栏,' + '方便观察容器和页面切换是否正常。' ) .fontSize(13) .fontColor('#68708A') .lineHeight(20) .width('100%') } .width('100%') .padding(16) .backgroundColor(Color.White) .borderRadius(20) .alignItems(HorizontalAlign.Start) Column({ space: 10 }) { Text(label) .fontSize(12) .fontWeight(FontWeight.Medium) .fontColor('#5065E8') .width('100%') Text(title) .fontSize(24) .fontWeight(FontWeight.Bold) .fontColor('#17203A') .width('100%') Text(description) .fontSize(14) .fontColor('#68708A') .lineHeight(22) .width('100%') } .width('100%') .layoutWeight(1) .padding(20) .justifyContent(FlexAlign.Center) .alignItems(HorizontalAlign.Start) .backgroundColor('#F8F9FD') .borderRadius(24) Column({ space: 6 }) { Text('当前接口关系') .fontSize(14) .fontWeight(FontWeight.Medium) .fontColor('#17203A') .width('100%') Text( 'HdsTabs → TabContent → tabBar' ) .fontSize(13) .fontColor('#68708A') .width('100%') Text( '进入悬浮布局时,' + '还需要配置 barOverlap 和 barFloatingStyle。' ) .fontSize(12) .fontColor('#747C92') .lineHeight(18) .width('100%') } .width('100%') .padding(16) .backgroundColor(Color.White) .borderRadius(20) .alignItems(HorizontalAlign.Start) } .width('100%') .height('100%') .padding({ left: 20, right: 20, top: 24, bottom: 18 }) .backgroundColor('#F4F6FB') } build() { /** * @Entry 的根节点使用 Column。 * HdsTabs 放在根容器内部, * 避免直接把 HdsTabs 作为 Entry 根节点。 */ Column() { HdsTabs({ controller: this.controller }) { TabContent() { this.tabPage( '首页', '基础页面已经放进 HdsTabs,' + '当前可以检查内容和底部页签切换。', 'TAB 01' ) } .tabBar('首页') TabContent() { this.tabPage( '任务', '业务内容继续由 TabContent 承载,' + '页面状态可以保持原来的组织方式。', 'TAB 02' ) } .tabBar('任务') TabContent() { this.tabPage( '我的', '外层使用 HdsTabs 后,' + '业务页面仍然可以保持独立。', 'TAB 03' ) } .tabBar('我的') } .barPosition(BarPosition.End) .vertical(false) .width('100%') .layoutWeight(1) } .width('100%') .height('100%') }}暂无评论数据
发布
相关推荐
小雨同学
产品总监、独立开发者社群主理人、资深全栈工程师,HarmonyOS应用开发者高级认证,PMP认证,CSDN博客专家,鸿蒙极客,Trae Fellow,阿里云社区专家博主、51CTO 博客专家、OpenTiny 优秀布道师、科大讯飞荣誉讲师。
帖子
提问
粉丝
【HarmonyOS 7 沉浸光感深度实战】10 通用组件封装与现有项目接入
2026-09-08 09:49:50 发布【HarmonyOS 7 沉浸光感深度实战】 09 常见冲突、性能边界与降级策略
2026-09-06 10:39:27 发布
0
京公网安备:11010502051901号