[Harmony]自定义导航栏
1.方案一
CustomNavigationBar
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.Whiteprivate context = getContext(this) as common.UIAbilityContext;onHeightChange?: (height: number) => voidaboutToAppear() {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
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.Whiteprivate context = getContext(this) as common.UIAbilityContext;onHeightChange?: (height: number) => voidaboutToAppear() {DevicesUtil.getStatusBarHeight(this.context).then(height => {this.statusBarHeight = heightthis.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.工具
获取状态栏高度
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.使用示例
跳转协议
/// 用户隐私协议
private userPrivacyPolicy() {router.pushUrl({url: 'pages/features/protocol/MFProtocolView',params: {webUrl: 'https://www.baidu.com',title: '隐私协议'}})
}
协议页面
注意:方案二会导致页面底部超出屏幕,所以需要设置.margin({bottom: this.navHeight})
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(()=>{})}
}