HarmonyOS鸿蒙Next中一级标题栏的上边距如何控制,使其与系统应用效果一致?

HarmonyOS鸿蒙Next中一级标题栏的上边距如何控制,使其与系统应用效果一致? 一级标题栏的上边距如何控制,使其与系统应用效果一致?

问题描述

import {
  HdsNavigation,
  HdsNavigationTitleMode,
} from '@kit.UIDesignKit';

import window from '@ohos.window';

@Entry
@Component
export struct HomePage {
  // @State topSafeHeight: number = 0;   // 存储系统导航栏高度(单位:vp)
  // async aboutToAppear() {
  //   // 获取当前窗口
  //   const windowClass = await window.getLastWindow(getContext(this));
  //   // 获取底部导航指示器区域(即系统导航栏)
  //   const avoidArea = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
  //   // 将 px 转换为 vp(因为在布局中使用的长度单位是 vp)
  //   this.topSafeHeight = px2vp(avoidArea.topRect.height);
  // }
  build() {
    HdsNavigation() {}
    // 标题栏配置
    .titleBar({
      content: {
        title: { mainTitle: '浏览' },
      },
    })
    .titleMode(HdsNavigationTitleMode.MINI)
    .safeAreaPadding({top:36})
    .hideBackButton(true)
  }
}

以上是我实现标题栏的方法
我按照标题栏-导航类-控件 - 华为HarmonyOS开发者

上的方法实现了标题栏,但是通过截图对比发现系统应用的标题栏会比我的标题栏更低一下,这是为什么,我应该怎么正确控制标题栏的高度

API版本号HarmonyOS 6.1.0(23)


更多关于HarmonyOS鸿蒙Next中一级标题栏的上边距如何控制,使其与系统应用效果一致?的实战教程也可以访问 https://www.itying.com/category-93-b0.html

10 回复
build(): void {
    Stack() {
      HdsNavigation(this.pageInfo) {
        HdsTabs({ controller: this.controller }) {
          ForEach(this.tabs, (tab: TabConfig) => {
            TabContent() {
              this.buildTabContent(tab)
            }
            .tabBar(new BottomTabBarStyle({
              normal: new SymbolGlyphModifier(tab.normalIcon)
                .renderingStrategy(SymbolRenderingStrategy.MULTIPLE_COLOR)
                .fontColor([
                  $r('sys.color.ohos_id_color_bottom_tab_icon_off'),
                  $r('sys.color.ohos_id_color_bottom_tab_icon_auxcolor_off02')
                ]),
              selected: new SymbolGlyphModifier(tab.selectedIcon)
                .renderingStrategy(SymbolRenderingStrategy.MULTIPLE_COLOR)
                .fontColor([
                  $r('sys.color.ohos_id_color_activated'),
                  $r('sys.color.ohos_id_color_primary_contrary')
                ])
            }, tab.label))
          })
        }
        .scrollable(false)
        .barOverlap(!this.shouldSideNav)
        .vertical(this.shouldSideNav)
        .barPosition(this.shouldSideNav ? BarPosition.Start : BarPosition.End)
        .barFloatingStyle(!this.shouldSideNav ? {
          barBottomMargin: 24,
          systemMaterialEffect: {
            materialType: hdsMaterial.MaterialType.ADAPTIVE,
            materialLevel: hdsMaterial.MaterialLevel.EXQUISITE
          }
        } : undefined)
      }
      .mode(NavigationMode.Stack)
      .titleBar({
        content: {
          title: {
            mainTitle: 'XXX',
          },
          menu: this.menus,
        },
        style: {
          scrollEffectOpts: {
            enableScrollEffect: true,
            scrollEffectType: ScrollEffectType.GRADIENT_BLUR,
          },
          systemMaterialEffect: {
            materialType: hdsMaterial.MaterialType.ADAPTIVE,
            materialLevel: hdsMaterial.MaterialLevel.EXQUISITE
          },
        },
        avoidLayoutSafeArea: false,
        enableComponentSafeArea: false
      })
      .bindToScrollable([this.scrollerHome, this.scrollerAR, this.scrollerMe])
      .hideBackButton(true)
      .titleMode(HdsNavigationTitleMode.MINI)
      .ignoreLayoutSafeArea([LayoutSafeAreaType.SYSTEM], [LayoutSafeAreaEdge.TOP, LayoutSafeAreaEdge.BOTTOM])
    }
    .width('100%')
    .height('100%')
    .clip(false)
  }

可以参考一下我这段代码,重点是留出安全区域.ignoreLayoutSafeArea([LayoutSafeAreaType.SYSTEM], [LayoutSafeAreaEdge.TOP, LayoutSafeAreaEdge.BOTTOM])

更多关于HarmonyOS鸿蒙Next中一级标题栏的上边距如何控制,使其与系统应用效果一致?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


Ignore layout safe area.这个不是忽视安全区吗?,

// ⚠️ 这行是关键!允许内容侵入系统区域,并自动识别到状态栏的高度
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])

那可以用expandSafeArea**,原理解释**:.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])会告诉 HarmonyOS:“让最外层的 Column 充满屏幕,并且从顶部(包括状态栏区域)开始布局”。然后HdsNavigation会由于设计原因(它自身自带一定的顶部安全区行为),自动避开状态栏,从而和系统应用的布局完全对齐。

你好,不要写死 safeAreaPadding({top:36}) ,而是通过 getWindowAvoidArea() 接口获取当前布局遮挡区域。打开注释代码,使用 this.topSafeHeight 替换 36. 实际 topSafeHeight 的值小于36.

import {
  HdsNavigation, HdsNavigationTitleMode,
} from '@kit.UIDesignKit';

import window from '@ohos.window';

@Entry
@Component
export struct HomePage {
  @State topSafeHeight: number = 0; // 存储系统导航栏高度(单位:vp)

  async aboutToAppear() {
    // 获取当前窗口
    const windowClass = await window.getLastWindow(this.getUIContext().getHostContext()!);
    // 获取底部导航指示器区域(即系统导航栏)
    const avoidArea = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
    // 将 px 转换为 vp(因为在布局中使用的长度单位是 vp)
    this.topSafeHeight = this.getUIContext().px2vp(avoidArea.topRect.height);
  }

  build() {
    HdsNavigation() {
    }
    // 标题栏配置
    .titleBar({
      content: {
        title: { mainTitle: '浏览' },
      },
    })
    .titleMode(HdsNavigationTitleMode.MINI)
    .safeAreaPadding({ top: this.topSafeHeight })
    .hideBackButton(true)
  }
}

你这里的问题,本质上不是 safeAreaPadding({top:36}) 设置错了,而是:

你手动写死了顶部间距,导致和系统一级标题栏规范不一致。

HdsNavigation 本身已经内置了:

  • 状态栏避让
  • 一级标题栏高度
  • 安全区处理
  • 不同设备适配

所以正常情况下:

不要再额外手动加 top padding。

你现在:

.safeAreaPadding({top:36})

等于:

“系统已经帮你避让了一次,你又额外往下推了 36vp”。

因此视觉上会比系统应用更低。

正确做法:

直接删除:

.safeAreaPadding({top:36})

即可。

改成:

HdsNavigation() {}
.titleBar({
  content: {
    title: { mainTitle: '浏览' },
  },
})
.titleMode(HdsNavigationTitleMode.MINI)
.hideBackButton(true)

这才是官方推荐用法。

另外还有一个关键点:

你使用的是:

HdsNavigationTitleMode.MINI

MINI 模式本身就是:

“小标题栏模式”。

它会比:

  • 系统一级大标题
  • LARGE模式

更紧凑。

所以如果你对比的是:

系统应用首页那种“大标题效果”,

实际上应该使用:

HdsNavigationTitleMode.LARGE

而不是 MINI。

例如:

.titleMode(HdsNavigationTitleMode.LARGE)

系统应用里:

  • 首页
  • 一级页面

通常都是 LARGE。

而:

  • 二级页
  • 详情页

才更像 MINI。

这个差异非常明显。

另外:

你注释掉的:

getWindowAvoidArea()

这种方式一般用于:

  • 自定义沉浸式页面
  • 自绘标题栏
  • 全屏布局

不建议和 HdsNavigation 混用。

因为:

HdsNavigation 已经自己处理了安全区。

你再手动计算状态栏高度:

会出现“双重避让”。

这也是很多人觉得:

“为什么我的标题栏越来越低”

的原因。

再补充一个容易忽略的点:

系统应用的标题栏并不完全是固定高度。

HarmonyOS 6 开始:

系统应用很多标题栏用了:

  • 动态沉浸
  • 滚动渐变
  • 光感
  • 大标题折叠

所以你截图对比时:

如果系统应用处于:

  • 可滚动状态
  • 折叠状态
  • 沉浸状态

视觉上也会不一样。

总结一下:

你当前的问题主要是:

  1. 不应该手动:
.safeAreaPadding({top:36})
  1. HdsNavigation 已自动处理状态栏安全区
  2. 一级页面建议:
HdsNavigationTitleMode.LARGE
  1. 不要同时混用:
  • getWindowAvoidArea
  • safeAreaPadding
  • HdsNavigation

否则容易重复避让。

你现在最接近系统应用效果的写法:

HdsNavigation() {}
.titleBar({
  content: {
    title: {
      mainTitle: '浏览'
    },
  },
})
.titleMode(HdsNavigationTitleMode.LARGE)
.hideBackButton(true)

这样基本就会和系统一级页面效果一致了。

谢谢回答,已经解决了,因为我使用了全屏模式,用avoidlayoutsafearea:true实现了避让,

如果不使用全屏模式,是不是要用expandsafearea来实现全屏沉浸效果呢,

要控制一级标题栏的上边距,使其与系统应用效果一致,关键在于正确适配设备的顶部安全区域(包括状态栏、刘海等系统UI区域)。系统应用会根据动态获取的安全区域高度自动调整标题栏位置,而硬编码的尺寸值(如你代码中的36vp)可能无法在所有设备上匹配系统效果。

问题分析

你代码中设置的.safeAreaPadding({top:36})将顶部安全区内边距固定为36vp。这个值可能在某些设备上接近安全区域高度,但不同设备(尤其是不同屏幕形态、不同系统版本)的顶部安全区域高度可能不同,导致标题栏位置偏高或偏低,从而与系统应用产生视觉差异。

正确步骤

动态获取顶部安全区域高度

在组件的aboutToAppear生命周期中,通过window.getWindowAvoidArea方法获取系统定义的避免区域(AvoidArea)。通常应使用TYPE_SYSTEM(包含状态栏、导航栏等系统栏)或TYPE_CUTOUT(仅包含刘海、挖孔等切割区域),根据你的设计需求选择。获取到的topRect.height单位为像素(px),需通过px2vp转换为虚拟像素(vp),以便在声明式UI中使用。

将安全区域高度应用到标题栏

将转换后的vp值设置为HdsNavigation的safeAreaPadding的top属性,例如.safeAreaPadding({top: 计算出的vp高度})。这样标题栏的上边距会精确匹配当前设备的顶部安全区域起始位置。

检查是否需要额外偏移

部分系统应用可能在安全区域基础上增加了细微的间距以达到特定的视觉平衡。你可以通过截图对比工具测量系统应用标题栏上边缘与屏幕顶部的实际距离,与你计算出的安全区域高度进行对比。如果存在固定差值,可以在设置top值时加上这个偏移量(例如安全区域高度 + 4vp)。

利用组件默认行为

HdsNavigation组件本身可能已内置安全区域适配逻辑。建议先注释掉.safeAreaPadding设置,运行应用观察标题栏默认位置。如果默认位置已与系统应用基本一致,则无需手动设置;如果仍有偏差,再采用动态计算的方式微调。

注意几点问题:

避免在代码中硬编码尺寸值(如36vp),除非你明确知道该值在所有目标设备上都符合设计规范。设计规范中标题栏的总高度(内容区域+边距)可能有推荐值,但上边距应优先跟随安全区域变化。获取窗口避免区域时,请确保在窗口已创建完成后调用(例如在aboutToAppear中),否则可能获取到不准确的值。通过动态获取并应用顶部安全区域高度,你的标题栏上边距就能自动适应不同设备,与系统应用保持一致的布局效果。

在HarmonyOS Next中,一级标题栏上边距控制需通过titleBar组件的padding属性调整,或使用expandSafeArea([SafeAreaType.SYSTEM])自动适配状态栏高度。系统效果采用@Entry组件的useSafeArea属性设为true,确保标题栏与系统状态栏无缝衔接。

移除手动设置的 .safeAreaPadding({top: 36}) 即可。

HdsNavigation 组件默认会自动处理顶部安全区(即状态栏高度),无需额外指定具体数值。你当前设置的固定值 36vp 未必等于设备真实的状态栏高度,导致位置与系统应用不一致。直接去掉该属性,让组件自适应,标题栏便会与系统应用对齐。如果确有自定义需求,可通过 window.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM) 获取真实高度(需转换为 vp)后赋值。

回到顶部