[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(()=>{

    })
  }
}
相关推荐
autumn20052 小时前
Flutter 框架跨平台鸿蒙开发 - 虚拟纪念馆
flutter·华为·harmonyos
2301_822703202 小时前
渐变壁纸生成:基于鸿蒙Flutter的跨平台壁纸创建工具
flutter·华为·harmonyos·鸿蒙
人间打气筒(Ada)3 小时前
「码动四季·开源同行」HarmonyOS应用开发:常见组件
华为·开源·harmonyos·组件·布局·鸿蒙开发
三声三视3 小时前
ArkTS 自定义组件完全指南:@Builder、@Extend、@Styles 实战解析
华为·harmonyos
Utopia^5 小时前
Flutter 框架跨平台鸿蒙开发 - 旅行预算管家
flutter·华为·harmonyos
李李李勃谦6 小时前
Flutter 框架跨平台鸿蒙开发 - 星空识别助手
flutter·华为·harmonyos
李李李勃谦6 小时前
Flutter 框架跨平台鸿蒙开发 - 本地生活服务预约
flutter·华为·生活·harmonyos
我的世界洛天依6 小时前
胡桃讲编程:早期华为手机(比如畅享等)可以升级鸿蒙吗?
华为·harmonyos
2301_822703206 小时前
开源鸿蒙跨平台Flutter开发:幼儿疫苗全生命周期追踪系统:基于 Flutter 的免疫接种档案与状态机设计
算法·flutter·华为·开源·harmonyos·鸿蒙
2301_822703207 小时前
鸿蒙flutter三方库实战——教育与学习平台:Flutter Markdown
学习·算法·flutter·华为·harmonyos·鸿蒙