【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,页面切换、选中状态和业务数据都已经稳定。开始接触悬浮导航以后,又会碰到 HdsTabsHdsTabsControllerBottomTabBarStylebarFloatingStyle。几个名称放到一起以后,很容易把它们理解成几套可以互相替换的页签组件。

实际关系要简单一些。

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.Endvertical=falsebarOverlap=truebarFloatingStyle

所以,已有项目准备改底部导航时,可以先回答两个问题。

当前页面只需要普通页签切换,还是已经准备使用悬浮导航?悬浮导航后面是否还会继续处理 MiniBar、握姿跟随或者不同窗口宽度?

这两个问题确认以后,TabsHdsTabs 的选择会清楚很多。

目前我的测试环境仍然以 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 负责控制问题

另外一个容易和样式混在一起的对象是 HdsTabsControllerHdsTabsOptions 提供 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.Endvertical=falsebarOverlap=true,并配置 barFloatingStyle

分步增加有一个很现实的好处。

页签突然不符合预期时,可以知道问题发生在普通 HdsTabs,还是发生在悬浮布局配置。

如果从第一版代码就同时加入 MiniBar、材质、渐变遮罩和握姿适配,任何一个字段出错都会扩大排查范围。

只确认当前环境已经走通基础调用

模拟器中的这张页面能够帮助确认基础工程链路。

如果 @kit.UIDesignKit 无法导入,问题需要回到 SDK 和工程环境。如果 HdsTabs 已经创建,但页面结构异常,可以继续检查容器和 TabContent。基础结构稳定以后,再进入 barOverlapbarFloatingStyle

总结

现有项目遇到 TabsHdsTabs 和悬浮页签时,可以先把它们放到不同层级。

Tabs 负责 ArkUI 常规页签。HdsTabs 属于 UI Design Kit,业务内容仍然通过 TabContent 承载。悬浮页签继续使用 HdsTabs,通过 barPositionverticalbarOverlapbarFloatingStyle 调整成悬浮布局。

项目选择时也可以保持一个比较简单的判断。

普通底栏已经满足需求,现有 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%')
  }
}
相关推荐
kiros_wang6 小时前
@Observed/@ObjectLink深层监听:嵌套对象更新失效根源与修复
华为·harmonyos
智塑未来12 小时前
鸿蒙6.1隐私安全升级——加密分享限时查看+水印+应用锁+防诈一键开启
安全·华为·harmonyos
OH_TPC13 小时前
HarmonyOS APP开发---“股动力“实时行情App,需要用到这个库
华为·音视频·harmonyos·鸿蒙
core51213 小时前
如何从多架构 Docker 镜像中导出指定的 ARM64 版本
华为·docker·架构·镜像·导出·多架构
砚凝霜15 小时前
软考网络工程师|案例分析:华为盒式交换机开局一本通核心考点总结
网络·华为
小雨青年15 小时前
【HarmonyOS 7 沉浸光感深度实战】10 通用组件封装与现有项目接入
华为·harmonyos
YM52e16 小时前
蒙特卡洛模拟器:鸿蒙 Canvas 上的大数定律与中心极限定理实证
华为·harmonyos
Dovis(誓平步青云)17 小时前
DevEco Studio 6.1.1 Windows 安装实录:从下载校验到首次启动
android·开发语言·数据库·人工智能·windows·harmonyos
2501_9197490317 小时前
华为鸿蒙免费排班APP—小羊排班
华为·harmonyos·鸿蒙