【HarmonyOS 7 悬浮页签深度实战】01 HdsTabs、Tabs 与悬浮页签如何选择

文章目录

    • 前言
    • [一、先把 Tabs、HdsTabs 和悬浮页签分清楚](#一、先把 Tabs、HdsTabs 和悬浮页签分清楚)
      • [Tabs 负责常规页签切换](#Tabs 负责常规页签切换)
      • [HdsTabs 属于 UI Design Kit](#HdsTabs 属于 UI Design Kit)
      • [悬浮页签是 HdsTabs 的悬浮形态](#悬浮页签是 HdsTabs 的悬浮形态)
    • [二、现有项目怎么判断要不要换成 HdsTabs](#二、现有项目怎么判断要不要换成 HdsTabs)
      • [普通底部导航可以继续使用 Tabs](#普通底部导航可以继续使用 Tabs)
      • [准备使用悬浮导航时,再进入 HdsTabs](#准备使用悬浮导航时,再进入 HdsTabs)
      • [智感握姿也建立在悬浮 HdsTabs 上](#智感握姿也建立在悬浮 HdsTabs 上)
      • [HdsTabsController 负责控制问题](#HdsTabsController 负责控制问题)
    • [三、先跑通基础 HdsTabs 再增加悬浮配置](#三、先跑通基础 HdsTabs 再增加悬浮配置)
    • 总结
    • 完整代码

前言

现有 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 则负责每一个页签对应的页面内容。

一个普通的底部导航可以写成:

ts 复制代码
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 提供的页签能力。

基础结构可以写成:

ts 复制代码
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。

调用关系可以整理成:

text 复制代码
HdsTabs
↓
barPosition = BarPosition.End
↓
vertical = false
↓
barOverlap = true
↓
barFloatingStyle(...)
↓
底部悬浮页签

所以项目里的几个对象可以这样区分:

对象 主要职责
Tabs ArkUI 常规页签与内容切换
HdsTabs HDS 页签容器
TabContent 每个页签对应的业务内容
BottomTabBarStyle 底部页签的图标、文字等样式
HdsTabsController 页签相关控制
barFloatingStyle 悬浮页签区域配置
悬浮页签 HdsTabs 进入悬浮布局后的页面形态

BottomTabBarStyle 也需要单独理解。

它负责底部页签的视觉内容,和整个 HdsTabs 容器并不是同一个对象。HarmonyOS 的底部页签设计本身就包含图标、文本、选中状态等内容表达。

因此,整个结构更适合按照下面的层次去理解:

text 复制代码
业务内容
TabContent

页签容器
Tabs / HdsTabs

底部页签样式
BottomTabBarStyle

悬浮布局
barFloatingStyle

页签控制
HdsTabsController

把这些层次分开以后,后面看到 MiniBar、页签显隐或者握姿跟随时,就知道应该继续检查哪一层接口。

三者的关系清楚以后,接口选择就可以回到已有项目本身。

二、现有项目怎么判断要不要换成 HdsTabs

页面选择 Tabs 还是 HdsTabs,没有必要只看底栏视觉效果。更实用的判断方式,是把当前需求和接下来准备增加的能力列出来。

普通底部导航可以继续使用 Tabs

假设一个工具应用已经有四个一级页面:

text 复制代码
首页
收藏
历史
设置

业务只要求固定底栏、页面切换和选中状态,底栏样式也已经由项目自己维护。这种情况下,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 的控制入口。

把结构继续拆开以后,可以形成下面这组关系:

text 复制代码
页面放什么
→ TabContent

用哪一种页签容器
→ Tabs / HdsTabs

底部页签长什么样
→ BottomTabBarStyle

页签是否悬浮
→ barFloatingStyle

页签如何被控制
→ HdsTabsController

这套分法在后面处理显隐、主动切换和业务状态时会很有用。

同一个问题只交给一层处理,代码也更容易维护。

三、先跑通基础 HdsTabs 再增加悬浮配置

接口选择最终还是要回到工程里验证。

当前环境已经准备好 UI Design Kit 时,可以先搭一个只有三个页签的 HdsTabs 页面。页面不加入 MiniBar、握姿或者复杂材质,只确认容器、TabContent 和底部页签能够正常工作。

先确认 UIDesignKit 能够正常导入

核心导入只有:

ts 复制代码
import {
  HdsTabs,
  HdsTabsController
} from '@kit.UIDesignKit';

HdsTabs 已经在 6.0.0(20) Beta1 的 UI Design Kit API 中出现,因此使用 HarmonyOS 7 环境时,要避免把 HdsTabs 本身误判为 HarmonyOS 7 才提供的组件。

页面随后创建控制器:

ts 复制代码
private controller: HdsTabsController =
  new HdsTabsController();

再准备三个 TabContent:

ts 复制代码
HdsTabs({
  controller: this.controller
}) {
  TabContent() {
    this.tabPage(
      '首页',
      '当前页面用于检查 HdsTabs 的基础切换。'
    )
  }
  .tabBar('首页')

  TabContent() {
    this.tabPage(
      '任务',
      '业务内容继续由 TabContent 承载。'
    )
  }
  .tabBar('任务')

  TabContent() {
    this.tabPage(
      '我的',
      '页签容器变化不会要求业务内容重写。'
    )
  }
  .tabBar('我的')
}
.barPosition(BarPosition.End)
.vertical(false)

运行以后,先确认三个结果:

text 复制代码
HdsTabs 正常创建
TabContent 正常显示
三个页签可以正常切换

这三个条件成立以后,基础 HDS 页签链路就已经可用。

悬浮配置适合分步加入

基础页面已经能够正常切换以后,再增加:

ts 复制代码
.barOverlap(true)

随后加入:

ts 复制代码
.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

ts 复制代码
/**
 * HarmonyOS 7 悬浮页签深度实战 01
 *
 */

import {
  HdsTabs,
  HdsTabsController
} from '@kit.UIDesignKit';

@Entry
@Component
struct 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%')
  }
}
相关推荐
三掌柜6663 小时前
# ArkWeb 手记 03|把 JSBridge 做成协议
harmonyos
轻口味3 小时前
HarmonyOS 7 新特性5:平行视界——轻视界文集的两栏阅读与窗口状态边界矩阵真机实测
华为·矩阵·harmonyos·鸿蒙·平行视界
动物园猫4 小时前
Compose Multiplatform 三方库 compose-icons(Octicons)的 OpenHarmony 鸿蒙化适配实战
华为·ar·harmonyos
旺仔Sec5 小时前
2026年江西省职业院校技能大赛鸿蒙应用开发赛项竞赛任务书(高职组)样题
华为·harmonyos
曲鸟5 小时前
体验完鸿蒙AI后的几点感受
人工智能·华为·harmonyos
HwJack205 小时前
【HarmonyOS开发小实践】Node-API 的SO 命名规则、多线程限制与调试
华为·harmonyos
LucianaiB5 小时前
用 HarmonyOS 做一张会写诗的月夜明信片:追月的完整开发复盘
华为·ai·harmonyos·skill
蒸鱼Yuzheng6 小时前
HarmonyOS HAP 与调试工件治理:包结构、版本身份与自动化证据链
自动化·性能测试·数据治理·harmonyos·hap
轻口味7 小时前
HarmonyOS 7 新特性3:TiledGSNode——轻带看让 71MB 庭院按视口按需加载:真机实测与零请求降级
华为·harmonyos·鸿蒙·tiledgsnode
李游Leo7 小时前
《HarmonyOS 7 ArkGraphics 3D 空间设计开发实战》01:从空场景到第一个可运行的3D房间【鸿蒙心迹】
3d·华为·harmonyos