[Harmony]自定义导航栏

1.方案一

CustomNavigationBar

TypeScript 复制代码
import { router } from '@kit.ArkUI';
import { DevicesUtil } from '../utils/DevicesUtil';
import { common } from '@kit.AbilityKit';

@Component
export struct CustomNavigationBar {
  @State private navHeight: number = 44
  @State parTitle: string = ''
  @State parBGColor: Color = Color.White
  private context = getContext(this) as common.UIAbilityContext;
  onHeightChange?: (height: number) => void

  aboutToAppear() {
    DevicesUtil.getStatusBarHeight(this.context).then(height => {
      this.onHeightChange?.(height + this.navHeight) // 触发回调
    })
  }

  build() {
    Column({ space:0 }) {
      Stack(){
        Row({ space: 0 }) {
          Image($r('app.media.icon_base_back'))
            .objectFit(ImageFit.Contain)
            .width(44)
            .height(44)
            .padding(12)
            .onClick(() => router.back())

          Image($r('app.media.icon_nav_logo'))
            .objectFit(ImageFit.Contain)
            .width(64)
            .height(44)
            .padding({ right: 10 })
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceBetween)
        .backgroundColor(Color.Transparent)

        Row({ space: 0 }) {
          Text(this.parTitle)
            .fontSize(17)
            .fontColor($r('app.color.mf_base_333333'))
            .fontWeight(FontWeight.Bold)
        }
        .justifyContent(FlexAlign.Center)
        .backgroundColor(Color.Transparent)
      }
      .width('100%')
      .height(this.navHeight)
    }
    .width('100%')
    .backgroundColor(this.parBGColor)
    .expandSafeArea( [SafeAreaType.SYSTEM], [SafeAreaEdge.TOP] )
  }

}

示意图

2.方案二

CustomNavigationBar

TypeScript 复制代码
import { router, window } from '@kit.ArkUI';
import { DevicesUtil } from '../utils/DevicesUtil';
import { common } from '@kit.AbilityKit';

@Component
export struct CustomNavigationBar {
  @State private statusBarHeight: number = 24 // 默认值(vp)
  @State private navHeight: number = 44
  @State parTitle: string = ''
  @State parBGColor: Color = Color.White
  private context = getContext(this) as common.UIAbilityContext;
  onHeightChange?: (height: number) => void

  aboutToAppear() {
    DevicesUtil.getStatusBarHeight(this.context).then(height => {
      this.statusBarHeight = height
      this.onHeightChange?.(height + this.navHeight) // 触发回调
    })
  }

  build() {
    Column({ space:0 }) {
      // 状态栏占位区域
      Row()
      .width('100%')
      .height(this.statusBarHeight)
      .backgroundColor(Color.Transparent)

      // 导航内容区域
      Stack(){
        Row({ space: 0 }) {
          Image($r('app.media.icon_base_back'))
            .objectFit(ImageFit.Contain)
            .width(44)
            .height(44)
            .padding(12)
            .onClick(() => router.back())

          Image($r('app.media.icon_nav_logo'))
            .objectFit(ImageFit.Contain)
            .width(64)
            .height(44)
            .padding({ right: 10 })
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceBetween)
        .backgroundColor(Color.Transparent)

        Row({ space: 0 }) {
          Text(this.parTitle)
            .fontSize(17)
            .fontColor($r('app.color.mf_base_333333'))
            .fontWeight(FontWeight.Bold)
        }
        .justifyContent(FlexAlign.Center)
        .backgroundColor(Color.Transparent)
      }
      .width('100%')
      .height(this.navHeight)
    }
    .width('100%')
    .backgroundColor(this.parBGColor)
    .expandSafeArea( [SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM] )
    .onAppear(async ()=>{
      window.getLastWindow(this.context).then(win => {
        win.setWindowLayoutFullScreen(true) // 隐藏系统栏
        // win.setWindowSystemBarEnable(['navigation']) // 可选控制导航栏显示//这一句不设置 不然第二次进入是就没状态栏那一行了
      })
    })
  }

}

示意图

3.工具

获取状态栏高度

TypeScript 复制代码
import window from '@ohos.window';
import { common } from '@kit.AbilityKit';

export class DevicesUtil {
  /// 状态栏高度
  static async getStatusBarHeight(context: common.UIAbilityContext, isVP: boolean = true): Promise<number> {
    try {
      const win = await window.getLastWindow(context);
      /*
      getWindowAvoidArea返回的物理像素,需要转换为虚拟像素返回,便于布局
      TYPE_SYSTEM:获取状态栏区域(推荐使用)
      TYPE_NAVIGATION_INDICATOR:获取导航栏区域
      TYPE_CUTOUT:获取刘海屏区域
      */
      const avoidArea = await win.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
      if (isVP) {
        return DevicesUtil.pxToVp(avoidArea.topRect.height);
      } else {
        return avoidArea.topRect.height;
      }
    } catch {
      return isVP ? 24 : 96; // 默认安全高度 // 96 假设480dpi设备
    }
  }
}

4.使用示例

跳转协议

TypeScript 复制代码
/// 用户隐私协议
private userPrivacyPolicy() {
  router.pushUrl({
    url: 'pages/features/protocol/MFProtocolView',
    params: {
      webUrl: 'https://www.baidu.com',
      title: '隐私协议'
    }
  })
}

协议页面

注意:方案二会导致页面底部超出屏幕,所以需要设置.margin({bottom: this.navHeight})

TypeScript 复制代码
import { router } from '@kit.ArkUI';
import { webview } from '@kit.ArkWeb';
import { CustomNavigationBar } from '../../../support/custom/CustomNavigationBar';
import ConsoleLog from '../../../support/extension/ConsoleLog';

interface MFProtocolParams {
  webUrl: string;
  title?: string;
}

@Entry
@Component
struct MFProtocolView {
  @State webUrl: string = ''; // 接收的网页地址
  @State title: string = '详情'; // 导航栏标题
  private controller: webview.WebviewController = new webview.WebviewController();
  @State private navHeight: number = 68 // 默认值(vp)

  aboutToAppear() {
    // 在aboutToAppear生命周期中读取路由参数
    const params: MFProtocolParams = router.getParams() as MFProtocolParams;
    if (params) {
      this.webUrl = params.webUrl || 'about:blank'; // 默认为空页面
      this.title = params.title || this.title; // 默认为'协议详情'
    }
  }

  build() {
    Column() {
      // 使用自定义导航栏组件
      CustomNavigationBar({ parTitle: this.title, parBGColor: Color.Red, onHeightChange: (height)=>{
        this.navHeight = height
      }})
      // 必须传递完整WebOptions参数
      Web({
        src: this.webUrl,
        controller: this.controller
      })
        .flexGrow(1) // 自动填充剩余空间
        .margin({bottom: this.navHeight}) // 方案一不用这行代码,方案二需要这行
        .onPageBegin((event) => {
          ConsoleLog.log('开始加载:' + event.url);
        })
    }
    .width('100%')
    .height('100%')
    .backgroundColor(Color.Green)
    .onAppear(()=>{

    })
  }
}
相关推荐
猫林老师4 小时前
HarmonyOS分布式硬件共享:调用手机摄像头的手表应用
华为·交互·harmonyos
前端世界8 小时前
HarmonyOS应用开发指南:Toast无法显示的完整排查流程与实战案例
华为·harmonyos
安卓开发者10 小时前
鸿蒙NEXT Wear Engine穿戴侧应用开发完全指南
ubuntu·华为·harmonyos
安卓开发者10 小时前
鸿蒙Next振动开发指南:打造沉浸式触觉反馈体验
华为·harmonyos
Devil枫10 小时前
HarmonyOS屏幕方向适配指南
华为·harmonyos
li理13 小时前
鸿蒙Image Kit深度解析:从图片解码到高级特效处理
harmonyos
li理13 小时前
鸿蒙相机开发中篇:自定义 UI 与拍摄控制
harmonyos
鸿蒙小白龙18 小时前
OpenHarmony 与 HarmonyOS 的 NAPI 开发实战对比:自上而下与自下而上的差异解析
harmonyos·鸿蒙·鸿蒙系统·open harmony
喵手19 小时前
【参赛心得】从“碰一碰”到“服务流转”:HarmonyOS创新赛金奖作品“智游文博”全流程复盘!
华为·harmonyos·鸿蒙应用开发·1024征文
鸿蒙小白龙19 小时前
OpenHarmony平台大语言模型本地推理:llama深度适配与部署技术详解
人工智能·语言模型·harmonyos·鸿蒙·鸿蒙系统·llama·open harmony