HarmonyOS鸿蒙Next应用开发中,如何实现美观的自定义导航栏?

HarmonyOS鸿蒙Next应用开发中,如何实现美观的自定义导航栏? 在鸿蒙应用开发中,如何实现美观的自定义导航栏?

如何让状态栏颜色跟随主题动态变化?

3 回复

技术要点

  • Window API状态栏设置
  • 自定义导航栏组件
  • @Builder组件复用
  • 主题色动态应用
  • 全面屏适配

完整实现代码

/**
 * 自定义导航栏组件示例
 */

import { router } from '@kit.ArkUI';
import { window } from '@kit.ArkUI';
import { common } from '@kit.AbilityKit';
import { ThemeConstants } from '../common/ThemeConstants';

@Component
export struct CustomNavigationBar {
  @Prop title: string = '';
  @Prop showBackButton: boolean = true;
  @Prop backgroundColor: string = '#FA8C16';
  @Prop titleColor: string = '#FFFFFF';
  @Prop rightText?: string;
  @Prop onRightClick?: () => void;

  build() {
    Row() {
      // 返回按钮
      if (this.showBackButton) {
        Image($r('app.media.ic_back'))
          .width(24)
          .height(24)
          .fillColor(this.titleColor)
          .onClick(() => {
            router.back();
          })
          .margin({ right: 16 })
      }

      // 标题
      Text(this.title)
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .fontColor(this.titleColor)
        .maxLines(1)
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .layoutWeight(1)

      // 右侧按钮
      if (this.rightText) {
        Text(this.rightText)
          .fontSize(16)
          .fontColor(this.titleColor)
          .onClick(() => {
            if (this.onRightClick) {
              this.onRightClick();
            }
          })
      }
    }
    .width('100%')
    .height(60)
    .padding({ left: 20, right: 20 })
    .backgroundColor(this.backgroundColor)
  }
}

/**
 * 页面示例 - 使用自定义导航栏
 */
@Entry
@Component
struct ExamplePage {
  @State primaryColor: string = '#FA8C16';

  aboutToAppear() {
    this.loadThemeColor();
    this.setStatusBarColor();
  }

  onPageShow() {
    this.loadThemeColor();
    this.setStatusBarColor();
  }

  /**
   * 加载主题颜色
   */
  private loadThemeColor() {
    ThemeConstants.refreshTheme();
    this.primaryColor = ThemeConstants.getPrimaryColor();
  }

  /**
   * 设置状态栏颜色
   */
  private async setStatusBarColor() {
    try {
      const context = getContext(this) as common.UIAbilityContext;
      const windowClass = await window.getLastWindow(context);
      
      if (windowClass) {
        // 设置状态栏属性
        await windowClass.setWindowSystemBarProperties({
          statusBarContentColor: '#FFFFFF',  // 状态栏文字颜色(白色)
          statusBarColor: this.primaryColor    // 状态栏背景色(主题色)
        });
      }
    } catch (error) {
      console.error('设置状态栏颜色失败:', JSON.stringify(error));
    }
  }

  build() {
    Column() {
      // 使用自定义导航栏
      CustomNavigationBar({
        title: '示例页面',
        showBackButton: true,
        backgroundColor: this.primaryColor,
        rightText: '保存',
        onRightClick: () => {
          console.info('点击保存');
        }
      })

      // 页面内容
      Column() {
        Text('页面内容')
          .fontSize(16)
      }
      .layoutWeight(1)
      .width('100%')
      .backgroundColor('#F5F5F5')
    }
    .width('100%')
    .height('100%')
  }
}

/**
 * 通用导航栏Builder
 */
[@Builder](/user/Builder)
export function buildNavigationBar(
  title: string,
  backgroundColor: string = '#FA8C16',
  showBack: boolean = true,
  rightText?: string,
  onRightClick?: () => void
) {
  Row() {
    if (showBack) {
      Image($r('app.media.ic_back'))
        .width(24)
        .height(24)
        .fillColor('#FFFFFF')
        .onClick(() => router.back())
        .margin({ right: 16 })
    }

    Text(title)
      .fontSize(18)
      .fontWeight(FontWeight.Bold)
      .fontColor('#FFFFFF')
      .layoutWeight(1)

    if (rightText) {
      Text(rightText)
        .fontSize(16)
        .fontColor('#FFFFFF')
        .onClick(() => {
          if (onRightClick) onRightClick();
        })
    }
  }
  .width('100%')
  .height(60)
  .padding({ left: 20, right: 20 })
  .backgroundColor(backgroundColor)
}

/**
 * 状态栏工具类
 */
export class StatusBarUtil {
  /**
   * 设置状态栏颜色
   */
  public static async setStatusBarColor(
    context: common.UIAbilityContext,
    backgroundColor: string,
    contentColor: string = '#FFFFFF'
  ): Promise<void> {
    try {
      const windowClass = await window.getLastWindow(context);
      
      if (windowClass) {
        await windowClass.setWindowSystemBarProperties({
          statusBarContentColor: contentColor,
          statusBarColor: backgroundColor
        });
      }
    } catch (error) {
      console.error('设置状态栏颜色失败:', JSON.stringify(error));
    }
  }

  /**
   * 设置状态栏为透明
   */
  public static async setTransparentStatusBar(
    context: common.UIAbilityContext
  ): Promise<void> {
    try {
      const windowClass = await window.getLastWindow(context);
      
      if (windowClass) {
        await windowClass.setWindowSystemBarProperties({
          statusBarContentColor: '#000000',
          statusBarColor: '#00000000'  // 透明
        });
      }
    } catch (error) {
      console.error('设置透明状态栏失败:', JSON.stringify(error));
    }
  }

  /**
   * 隐藏状态栏
   */
  public static async hideStatusBar(
    context: common.UIAbilityContext
  ): Promise<void> {
    try {
      const windowClass = await window.getLastWindow(context);
      
      if (windowClass) {
        await windowClass.setWindowSystemBarEnable(['navigation']);
      }
    } catch (error) {
      console.error('隐藏状态栏失败:', JSON.stringify(error));
    }
  }

  /**
   * 显示状态栏
   */
  public static async showStatusBar(
    context: common.UIAbilityContext
  ): Promise<void> {
    try {
      const windowClass = await window.getLastWindow(context);
      
      if (windowClass) {
        await windowClass.setWindowSystemBarEnable(['status', 'navigation']);
      }
    } catch (error) {
      console.error('显示状态栏失败:', JSON.stringify(error));
    }
  }

  /**
   * 设置全屏模式
   */
  public static async setFullScreen(
    context: common.UIAbilityContext,
    isFullScreen: boolean
  ): Promise<void> {
    try {
      const windowClass = await window.getLastWindow(context);
      
      if (windowClass) {
        await windowClass.setWindowLayoutFullScreen(isFullScreen);
      }
    } catch (error) {
      console.error('设置全屏模式失败:', JSON.stringify(error));
    }
  }
}

/**
 * 实际项目中的导航栏示例
 */
@Entry
@Component
struct AddRecordPage {
  @State primaryColor: string = '#FA8C16';

  aboutToAppear() {
    this.loadThemeColor();
    this.setStatusBarColor();
  }

  private loadThemeColor() {
    this.primaryColor = ThemeConstants.getPrimaryColor();
  }

  private async setStatusBarColor() {
    const context = getContext(this) as common.UIAbilityContext;
    await StatusBarUtil.setStatusBarColor(context, this.primaryColor);
  }

  [@Builder](/user/Builder)
  buildHeader() {
    Row() {
      Image($r('app.media.ic_back'))
        .width(24)
        .height(24)
        .fillColor('#FFFFFF')
        .onClick(() => router.back())

      Text('添加人情记录')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .fontColor('#FFFFFF')
        .margin({ left: 16 })

      Row().layoutWeight(1)

      Text('保存')
        .fontSize(16)
        .fontColor('#FFFFFF')
        .onClick(() => this.saveRecord())
    }
    .width('100%')
    .height(60)
    .padding({ left: 20, right: 20 })
    .backgroundColor(this.primaryColor)
    .shadow({
      radius: 12,
      color: 'rgba(0, 0, 0, 0.15)',
      offsetX: 0,
      offsetY: 4
    })
  }

  private saveRecord() {
    // 保存逻辑
  }

  build() {
    Column() {
      this.buildHeader()
      
      // 页面内容
      Scroll() {
        Column() {
          // 表单内容
        }
        .padding(16)
      }
      .layoutWeight(1)
    }
    .width('100%')
    .height('100%')
  }
}

核心API讲解

1. Window API - 状态栏设置

window.setWindowSystemBarProperties({
  statusBarContentColor: '#FFFFFF',  // 状态栏内容颜色
  statusBarColor: '#FA8C16'          // 状态栏背景色
})

参数说明:

  • statusBarContentColor: 状态栏文字和图标颜色
  • statusBarColor: 状态栏背景颜色(支持透明)

2. 全屏模式设置

window.setWindowLayoutFullScreen(true)  // 开启全屏
window.setWindowLayoutFullScreen(false) // 关闭全屏

3. 状态栏显隐控制

// 显示状态栏和导航栏
window.setWindowSystemBarEnable(['status', 'navigation'])

// 只显示导航栏(隐藏状态栏)
window.setWindowSystemBarEnable(['navigation'])

设计要点

1. 导航栏高度

.height(60)  // 推荐高度60vp

2. 内边距设置

.padding({ left: 20, right: 20 })  // 左右留白20vp

3. 阴影效果

.shadow({
  radius: 12,
  color: 'rgba(0, 0, 0, 0.15)',
  offsetX: 0,
  offsetY: 4
})

4. 返回按钮

Image($r('app.media.ic_back'))
  .width(24)
  .height(24)
  .fillColor('#FFFFFF')
  .onClick(() => router.back())

最佳实践

1. 主题色动态应用

aboutToAppear() {
  this.loadThemeColor();
  this.setStatusBarColor();
}

onPageShow() {
  this.loadThemeColor();
  this.setStatusBarColor();
}

2. 使用@Builder复用

[@Builder](/user/Builder)
buildHeader() {
  Row() {
    // 导航栏内容
  }
  .backgroundColor(this.primaryColor)
}

3. 封装工具类

// 统一管理状态栏操作
StatusBarUtil.setStatusBarColor(context, color);

4. 错误处理

try {
  await window.setWindowSystemBarProperties({...});
} catch (error) {
  console.error('设置失败:', error);
}

适配方案

1. 全面屏适配

// 获取安全区域
const avoidArea = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
const topHeight = avoidArea.topRect.height;

// 添加顶部padding
.padding({ top: topHeight })

2. 不同设备适配

// 根据设备类型调整高度
const navBarHeight = deviceType === 'tablet' ? 70 : 60;

3. 横屏适配

// 监听屏幕旋转
windowClass.on('windowSizeChange', (size) => {
  // 调整布局
});

使用场景

  1. 普通页面: 标题+返回按钮
  2. 表单页面: 标题+取消+保存
  3. 详情页面: 标题+返回+更多操作
  4. 搜索页面: 搜索框+取消
  5. 沉浸式页面: 透明状态栏

避坑指南

1. ❌ 忘记在onPageShow中刷新

// 错误: 只在aboutToAppear中设置
aboutToAppear() {
  this.setStatusBarColor();
}

// 正确: 同时在onPageShow中设置
onPageShow() {
  this.setStatusBarColor();
}

2. ❌ 状态栏颜色不跟随主题

// 错误: 硬编码颜色
.backgroundColor('#FA8C16')

// 正确: 使用主题色
.backgroundColor(this.primaryColor)

3. ❌ 异步操作未处理

// 正确: 使用async/await
private async setStatusBarColor() {
  await window.setWindowSystemBarProperties({...});
}

效果展示

  • 状态栏颜色与导航栏一致
  • 支持主题动态切换
  • 返回按钮交互流畅
  • 阴影效果美观
  • 全面屏完美适配

总结

本文提供了完整的导航栏和状态栏解决方案:

  • ✅ 自定义导航栏组件
  • ✅ 状态栏动态设置
  • ✅ 主题色自动适配
  • ✅ 全面屏完美适配
  • ✅ 工具类封装复用

更多关于HarmonyOS鸿蒙Next应用开发中,如何实现美观的自定义导航栏?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


鸿蒙Next自定义导航栏主要通过以下方式实现:

  1. 使用Navigation组件作为基础容器
  2. 通过@CustomDialog装饰器创建自定义导航栏组件
  3. 在Navigation的title属性中嵌入自定义布局
  4. 利用Row/Column容器结合Text、Image等基础组件构建导航栏UI
  5. 通过状态变量管理导航栏的交互状态
  6. 使用资源文件管理颜色、尺寸等样式属性

关键代码结构示例:

Navigation() {
  // 页面内容
}
.title({
  Builder: this.CustomTitleBuilder
})

导航栏高度可通过系统API获取,建议遵循鸿蒙设计规范中的推荐尺寸。

在HarmonyOS Next应用开发中,实现美观且动态的自定义导航栏,核心在于使用WindowUI上下文进行灵活控制。

1. 实现自定义导航栏

关键在于隐藏默认导航栏,并使用自定义组件替代。

  • 隐藏系统导航栏:在EntryAbilityonWindowStageCreate方法中,通过Window对象设置。
    import { window } from '@kit.ArkUI';
    
    onWindowStageCreate(windowStage: window.WindowStage): void {
        // 获取主窗口
        let mainWindow = windowStage.getMainWindow();
        // 隐藏系统导航栏
        mainWindow.setWindowSystemBarEnable(['navigation']); // 传入空数组即可完全隐藏
        // ... 其他初始化代码
    }
    
  • 构建自定义组件:在ArkUI页面中,使用RowColumnTextImage等基础组件在页面顶部自由组合导航栏布局、图标和标题,并为其设置样式和事件。
    @Component
    struct CustomNavBar {
      build() {
        Row() {
          Image($r('app.media.back')) // 返回图标
            .onClick(() => {
              // 处理返回事件
            })
          Text('页面标题')
            .fontSize(20)
            .fontWeight(FontWeight.Medium)
        }
        .width('100%')
        .height(50)
        .padding({ left: 12, right: 12 })
        .backgroundColor(Color.White)
        .justifyContent(FontAlign.SpaceBetween)
      }
    }
    
  • 集成到页面:在页面的build方法中,将自定义导航栏组件置于顶部。
    @Entry
    @Component
    struct Index {
      build() {
        Column() {
          CustomNavBar() // 自定义导航栏
          // 页面主要内容...
          Scroll() {
            // ...
          }
        }
        .width('100%')
        .height('100%')
      }
    }
    

2. 状态栏颜色动态跟随主题

HarmonyOS Next提供了完整的动态主题能力,可通过UI上下文(UIContext)获取并响应主题变化。

  • 获取当前主题色:使用资源管理器和UI上下文获取当前的主题颜色资源,特别是状态栏背景常用的$color('sys.color.ohos_id_color_sub_background')
  • 应用主题色:将获取到的颜色资源设置为自定义导航栏(或状态栏区域)的背景色。
  • 监听主题变化:在自定义组件或页面中监听主题变化事件,并在回调中更新颜色。

示例代码片段

import { common } from '@kit.ArkUI';

@Entry
@Component
struct Index {
  // 通过UI上下文监听主题变化
  private uiContext: common.UIContext = getContext(this) as common.UIContext;

  @State navBarColor: Resource = $color('sys.color.ohos_id_color_sub_background'); // 初始颜色

  aboutToAppear(): void {
    // 监听主题变化
    this.uiContext.on('themeChange', () => {
      // 主题变更时,更新颜色状态
      this.navBarColor = $color('sys.color.ohos_id_color_sub_background');
    });
  }

  build() {
    Column() {
      // 自定义导航栏,背景色绑定到动态状态变量
      Row() {
        // ... 导航栏内容
      }
      .width('100%')
      .height(50)
      .backgroundColor(this.navBarColor) // 动态应用颜色

      // ... 页面其他内容
    }
  }
}

关键点总结

  • 窗口控制:使用Window API管理系统栏显示。
  • 组件化:将导航栏封装为可复用的自定义组件。
  • 主题响应:通过UIContextthemeChange事件和系统颜色资源($color('sys.color...'))实现颜色动态切换。
  • 样式与交互:结合ArkUI的声明式语法和响应式状态管理,为导航栏添加丰富的样式和流畅的交互逻辑。

通过以上步骤,你可以创建出既美观又能无缝适配系统深色/浅色模式的自定义导航栏。

回到顶部