HarmonyOS NEXT - Navigation组件封装BaseNavigation

demo 地址: https://github.com/iotjin/JhHarmonyDemo
代码不定时更新,请前往github查看最新代码
在demo中这些组件和工具类都通过module实现了,具体可以参考HarmonyOS NEXT - 通过 module 模块化引用公共组件和utils

官方介绍

组件导航 (Navigation)(推荐)

Navigation是路由容器组件,一般作为首页的根容器,包括单栏(Stack)、分栏(Split)和自适应(Auto)三种显示模式。Navigation组件适用于模块内和跨模块的路由切换,一次开发,多端部署场景。通过组件级路由能力实现更加自然流畅的转场体验,并提供多种标题栏样式来呈现更好的标题和内容联动效果。在不同尺寸的设备上,Navigation组件能够自适应显示大小,自动切换分栏展示效果。
Navigation组件主要包含​导航页(NavBar)和子页(NavDestination)。导航页由标题栏(Titlebar,包含菜单栏menu)、内容区(Navigation子组件)和工具栏(Toolbar)组成,其中导航页可以通过hideNavBar属性进行隐藏,导航页不存在页面栈中,导航页和子页,以及子页之间可以通过路由操作进行切换。
在API Version 9上,需要配合NavRouter组件实现页面路由,从API Version 10开始,推荐使用NavPathStack实现页面路由。

这里里封装的只是组件,路由跳转通过 Router切换Navigation 或者 @ohos.route 跳转
demo中都是通过@ohos.router实现的跳转,因为Router需要Navigation 包着页面的内容,多一层嵌套

页面路由 (@ohos.router)(不推荐)
Router切换Navigation

BaseNavigation效果图

js 复制代码
@Entry
@Component
struct TestNav {
  // menus(value: Array<NavigationMenuItem> | CustomBuilder): NavigationAttribute

  aboutToAppear() {
    console.log('CustomComponent: aboutToAppear called. Component is about to appear.')
    // 在这里执行初始化数据的操作
  }

  build() {
    Column() {
      this.NavigationTest()
    }
  }

  TooTmp: NavigationMenuItem = {
    'value': "1",
    'action': () => {
    },
    'icon': "resources/rawfile/tab/nav_tab_1.png",
  }
  TooBar: ToolbarItem[] = [this.TooTmp, this.TooTmp, this.TooTmp]

  @Builder
  NavigationTest() {
    Navigation() {
      RelativeContainer() {
        Text('Content')
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .alignRules({
            center: { anchor: '__container__', align: VerticalAlign.Center },
            middle: { anchor: '__container__', align: HorizontalAlign.Center }
          })
      }
      .height('100%')
      .width('100%')
    }
    .titleMode(NavigationTitleMode.Mini)
    .title("标题栏") // 设置title,此时不支持修改文字大小,颜色等样式
    .hideBackButton(false)
    .menus([this.TooTmp, this.TooTmp, this.TooTmp])
    .toolbarConfiguration(this.TooBar)

    // .size({ width: '100%', height: '100%' })
    // .backgroundColor(Color.Orange)
    // .toolbarConfiguration(this.TooBar)
  }
}

BaseNavigation使用

demo里的弹框在这里 HarmonyOS NEXT - Toast和Loading使用

  • 左侧返回,右侧文字
js 复制代码
BaseNavigation({
   title: '标题标题标题标题标题标题标题标题标题标题',
   rightText: '设置',
   rightItemCallBack: () => {
     console.log("点击了设置")
     JhProgressHUD.showText("点击了设置")
   }
 })
  • 左侧返回(拦截点击事件),右侧图片
js 复制代码
  BaseNavigation({
    title: '标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题',
    titleTextAlign: TextAlign.Start,
    // rightImgPath: 'resources/rawfile/images/ic_more_white.png',
    rightImgPath: $rawfile("images/ic_nav_add.png"),
    rightItemCallBack: () => {
      JhProgressHUD.showText("点击了右侧图标")
    },
    leftItemCallBack: () => {
      JhProgressHUD.showText("点击左侧")
    }
  })
  • 左侧自定义右侧文字
js 复制代码
  BaseNavigation({
    title: '标题',
    titleTextAlign: TextAlign.Start,
    leftItem: {
      text: '设置1',
      icon: $rawfile("images/ic_nav_add.png"),
      itemCallBack: () => {
        JhProgressHUD.showText("点击了左侧")
      }
    },
    rightText: '发布',
    rightItemCallBack: () => {
      console.log("点击了设置")
      JhProgressHUD.showText("点击了发布")
    }
  })
  • 渐变导航条
js 复制代码
BaseNavigation({
   isGradient: true,
   title: '标题',
   // rightImgPath: 'resources/rawfile/images/ic_more_white.png',
   rightImgPath: $rawfile("images/ic_nav_add.png"),
   rightItemCallBack: () => {
     JhProgressHUD.showText("点击了右侧图标")
   }
 })
  • 右侧自定义1

       BaseNavigation({
         title: '标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题',
         titleTextAlign: TextAlign.Start,
         rightItems: [
           {
             text: '设置1',
             itemCallBack: () => {
               JhProgressHUD.showText("点击了设置1")
             }
           },
           {
             text: '设置2',
             itemCallBack: () => {
               JhProgressHUD.showText("点击了设置2")
             }
           }
         ]
       })
    
  • 内部包裹child内容

js 复制代码
 BaseNavigation({
    title: '自定义child',
    child: () => {
      this.childBuilder()
    }
  })

  @Builder
  childBuilder() {
    Scroll() {
      Column() {
        Text('111111111')
          .width('100%')
          .height(600)
          .backgroundColor(Color.Orange)
        Text('222222222')
          .width('100%')
          .height(500)
          .backgroundColor(Color.Yellow)
      }
    }
  }

JhProgressHud.ets 完整代码

js 复制代码
///  BaseNavigation.ets
///
///  Created by iotjin on 2024/08/01.
///  description: 导航条

import { router } from '@kit.ArkUI';
import { KColors } from '../configs/Colors';

type imageType = string | Resource | PixelMap

interface ItemType {
  icon?: imageType // icon优先级高于text
  text?: string
  itemCallBack?: () => void
}

const _titleFontSize = 18.0
const _textFontSize = 16.0
const _itemSpace = 15.0 // 右侧item内间距
const _imgWH = 22.0 // 右侧图片宽高
const _rate = 16 // 左右item占比

const _bgColor: ResourceColor = KColors.kThemeColor
const _bgDarkColor: ResourceColor = KColors.kNavBgDarkColor
const _titleColor: ResourceColor = KColors.kNavTitleColor

const _appbarStartColor = KColors.kGradientStartColor // 默认appBar 渐变开始色
const _appbarEndColor = KColors.kGradientEndColor // 默认appBar 渐变结束色

@Preview
@Component
export struct BaseNavigation {
  @Prop public title: string = '' // 标题文字
  private isCenterTitle: boolean = true
  @Prop public titleFontSize: number = _titleFontSize
  @Prop public titleTextAlign: TextAlign = this.isCenterTitle ? TextAlign.Center : TextAlign.Start
  @Prop public bgColor: ResourceColor = _bgColor
  @Prop public leftItem?: ItemType | null = null // 左侧Widget,为空显示返回按钮
  public leftItemCallBack?: () => void
  @Prop public rightText: string = '' // 右侧按钮文字
  @Prop public rightImgPath: imageType = '' // 右侧按钮图片路径,优先级高于text ,eg: $rawfile("images/ic_nav_add.png") | 'resources/rawfile/images/ic_nav_add.png'
  public rightItemCallBack?: () => void
  @Prop public rightItems: [] | [ItemType] | [ItemType, ItemType] = [] // 优先级高于rightText和rightImgPath
  @Prop public isGradient: boolean = false // 是否渐变背景色
  @Prop public navHeight: number = 56 // 传入child为100%,否则为navHeight
  @BuilderParam child?: () => void // 子组件
  //
  private backImage: imageType = $rawfile("common/ic_nav_back_white.png")
  private titleAndIconColor: ResourceColor = _titleColor

  aboutToAppear() {
    // 如果背景透明或者是白色,设置字体和图标、状态栏字体为黑色
    let isTransparent = this.bgColor == Color.Transparent || this.bgColor == Color.White || this.bgColor == KColors.kNavWhiteBgColor
    if (isTransparent) {
      this.titleAndIconColor = Color.Black
      this.backImage = $rawfile("common/ic_nav_back_black.png")
    }
  }

  build() {
    Navigation() {
      if (this.child) {
        this.child()
      }
    }
    .titleMode(NavigationTitleMode.Mini)
    .title(this.NavigationTitle())
    .hideBackButton(true)
    .height(this.child ? '100%' : this.navHeight)
  }

  @Builder
  NavigationTitle() {
    Row() {
      this.navBuilder()
    }
    .width('100%')
    .height('100%')
    .backgroundColor(this.bgColor)
    .linearGradient(this.isGradient ? {
      direction: GradientDirection.Right, // 从左向右
      colors: [[_appbarStartColor, 0.0], [_appbarEndColor, 1.0]]
    } : null)
  }

  @Builder
  navBuilder() {
    Row() {
      if (this.leftItem) {
        Row() {
          this.itemBuilder({ icon: this.leftItem.icon, text: this.leftItem.text })
        }.onClick(this.leftItem.itemCallBack)
      } else {
        this.backBuilder()
      }
    }
    .layoutWeight(_rate)
    .width('100%')

    Row() {
      this.titleBuilder()
    }
    .layoutWeight(this.rightItems.length ? (100 - _rate - this.rightItems.length * _rate) : (100 - _rate * 2))
    .width('100%')

    if (this.rightItems.length) {
      Row() {
        ForEach(this.rightItems, (item: ItemType) => {
          Row() {
            this.itemBuilder({ icon: item.icon, text: item.text })
          }.onClick(item.itemCallBack)
          .layoutWeight(1)
          .width('100%')
        })
      }
      .margin({ left: this.rightItems.length == 2 ? 15 : 0, right: this.rightItems.length == 2 ? 5 : 0 })
      .layoutWeight(_rate * this.rightItems.length)
      .width('100%')
    } else {
      Row() {
        this.itemBuilder({ icon: this.rightImgPath, text: this.rightText })
      }
      .onClick(this.rightItemCallBack)
      .layoutWeight(_rate)
      .width('100%')
    }
  }

  @Builder
  backBuilder() {
    Row() {
      Image(this.backImage)
        .width(18)
        .height(18)
        .margin({ left: 15 })
    }
    .size({ width: '100%', height: '100%' })
    .onClick(() => {
      if (this.leftItemCallBack) {
        this.leftItemCallBack()
      } else {
        this.goBack()
      }
    })
  }

  @Builder
  titleBuilder() {
    Row() {
      Text(this.title)
        .padding({
          left: 10,
          top: 5,
          right: 10,
          bottom: 5
        })
        .fontSize(this.titleFontSize)
        .fontColor(this.titleAndIconColor)
        .textAlign(this.titleTextAlign)
        .maxLines(2)
        .textOverflow({ overflow: TextOverflow.Ellipsis })// 文本超长显示省略号
        .wordBreak(WordBreak.BREAK_WORD)
    }
    .justifyContent(FlexAlign.Center)
    .alignItems(VerticalAlign.Center)
    .size({ width: '100%', height: '100%' })
  }

  @Builder
  itemBuilder(item: ItemType) {
    Row() {
      if (item.icon) {
        Image(item.icon)
          .width(_imgWH)
          .height(_imgWH)
          .margin({ left: _itemSpace, right: _itemSpace })
      } else if (item.text) {
        Text(item.text)
          .fontSize(_textFontSize)
          .fontColor(this.titleAndIconColor)
      }
    }
    .size({ width: '100%', height: '100%' })
    .justifyContent(FlexAlign.Center)
    .alignItems(VerticalAlign.Center)
  }

  goBack() {
    router.back()
  }
}

里面用到的颜色

js 复制代码
export class KColors {
  // 主题色(导航条背景、提交按钮背景、弹框确认文字、表单图标录入光标)
  // 暗黑模式高亮显示颜色按kThemeColor设置,如tabBar选中文字图标、提交按钮背景色、指示器选中下划线、光标等
  static readonly kThemeColor: ResourceColor = '#3BB815'
  static readonly kThemeDarkColor: ResourceColor = '#0A0A0A' // (10, 10, 10)
  static readonly kThemeBlueColor: ResourceColor = '#4688FA'
  static readonly kThemePurpleColor: ResourceColor = '#9C27B0'
  // 渐变色(appBar和按钮)
  static readonly kGradientStartColor: ResourceColor = '#2683BE' // 渐变开始色
  static readonly kGradientEndColor: ResourceColor = '#34CABE' // 渐变结束色
  static readonly kNavTitleColor: Color = Color.White
}
相关推荐
冯志浩4 小时前
Harmony NEXT:如何给数据库添加自定义分词
harmonyos·掘金·金石计划
爱桥代码的程序媛6 小时前
鸿蒙OpenHarmony【轻量系统芯片移植案例】标准系统方案之瑞芯微RK3568移植案例
嵌入式硬件·harmonyos·鸿蒙·鸿蒙系统·移植·openharmony·鸿蒙开发
AORO_BEIDOU6 小时前
防爆手机+鸿蒙系统,遨游通讯筑牢工业安全基石
5g·安全·智能手机·信息与通信·harmonyos
逢生博客18 小时前
Mac 搭建仓颉语言开发环境(Cangjie SDK)
macos·华为·鸿蒙
青柠_项目管理18 小时前
PMP证书持有者,在华为、腾讯一般能拿多少薪资?
华为·pmp
小强在此21 小时前
【基于开源鸿蒙(OpenHarmony)的智慧农业综合应用系统】
华为·开源·团队开发·智慧农业·harmonyos·开源鸿蒙
Reuuse1 天前
【HCIA-Datacom】华为VRP系统
服务器·网络·华为
PlumCarefree1 天前
基于鸿蒙API10的RTSP播放器(四:沉浸式播放窗口)
华为·harmonyos
中关村科金1 天前
中关村科金推出得助音视频鸿蒙SDK,助力金融业务系统鸿蒙化提速
华为·音视频·harmonyos