鸿蒙纪·梦始卷#15 | 应用界面整合

《鸿蒙纪元》张风捷特烈 计划打造的一套 HarmonyOS 开发系列教程合集。致力于创作优质的鸿蒙原生学习资源,帮助开发者进入纯血鸿蒙的开发之中。本系列的所有代码将开源在 HarmonyUnit 项目中:

github: github.com/toly1994328…
gitee: gitee.com/toly1994328…

鸿蒙纪元 系列文章列表可在《文章总集》 或 【github 项目首页】 查看。

在前面 14 篇中,我们实现了 计数器猜数字电子木鱼画板绘制 四个小功能。其中每个功能都是以一个独立的界面呈现的。本篇我们将结合导航,将四个功能页整合到一个应用程序中。


1. 界面效果

界面效果如下所示:底部有导航栏,点击时中间的内容切换到对应的功能,并且导航栏激活对应的条目。

计数器界面 绘制界面

我们可以使用 Navigation 组件的 NavigationMode.Stack 模式来实现当前的需求。代码如下所示:

  build() {
    Navigation(this.pageStack) {
        ///TODO 构建主界面
    }
    .hideTitleBar(true)
    .mode(NavigationMode.Stack)
    .padding({ bottom: px2vp(this.bottomRectHeight) })
  }

界面构建方面主要有两块内容:

  • 中间的主体功能区,支持切换界面内容,可以使用 Tab 组件。构造入参中传入激活的索引,内容通过 TabContent 包裹对应的界面组件:
@State currentIndex: number = 0;

@Builder tabview(){
  Tabs({ index: this.currentIndex }) {
    TabContent() {CounterPage()}
    TabContent() {GuessingPage()}
    TabContent() {MuYuPage()}
    TabContent() {Painter()}
  }.layoutWeight(1)
  .barHeight(0)
  .scrollable(false)
}
  • 底部导航栏,只能选择一个的菜单组,想要灵活定制的话,这里自定义 BottomAppBar 组件:
Navigation(this.pageStack) {
  Column() {
    this.tabview()
    BottomAppBar({
      data: kTabs,
      activeIndex: this.currentIndex,
      onSelected: (index) => this.onPageChange(index)
    })
  }
  .width('100%').height('100%')
}

2. 底部导航栏

底部导航栏需要传入激活索引、菜单数据、以及点击回调事件:菜单数据这里定义 MenuMeta 类维护,通过 interface 定义类型,可以简化对象构造:

export interface MenuMeta {
  title: ResourceStr;
  icon: ResourceStr;
}

这里定义 kTabs 记录菜单数组数据:

export const kTabs: MenuMeta[] = [
  {
    title: '计数器',
    icon: $r('app.media.bar_counter'),
  },
  {
    title: '猜数字',
    icon: $r('app.media.bar_guess'),
  },
  {
    title: '电子木鱼',
    icon: $r('app.media.bar_muyu'),
  },
  {
    title: '画板',
    icon: $r('app.media.bar_painter'),
  },
]

BottomAppBar 组件如下,通过 Flex 横向排列菜单列表,通过 ForEach 遍历 data 构建菜单项:

@Component
export struct BottomAppBar {
  @Prop data: MenuMeta[] = [];
  @Prop activeIndex: number = 0;
  private onSelected?: IndexSelector;

  build() {
    Column(){
      Divider()
      Flex({ alignItems: ItemAlign.Center }) {
        ForEach(this.data, (item: MenuMeta, index: number) => {
          TabItem({
            item: item, selected: this.activeIndex == index,
            onTap: ()=>this.onSelected!(index)
          });
        })
      }.layoutWeight(1);
    }.height(58).width('100%').backgroundColor(Color.White)
    }

}

菜单项这里也独立出一个 TabItem 单独维护,其中传入菜单数据 MenuMeta、是否激活、以及点击回调。构建过程通过 Column 包裹图片和文字,它们的颜色通过 selected 决定是否激活。

@Component
struct TabItem {
  @Prop item: MenuMeta;
  @Prop selected: boolean = false;
  private onTap = () => {};

  build() {
    Column() {
      Image(this.item.icon).fillColor(this.selected ? $r('app.color.theme_color') : Color.Grey).width(24).height(24)
      Shape().height(4)
      Text(this.item.title).fontSize(12).fontColor(this.selected ? $r('app.color.theme_color') : Color.Grey)
    }.onClick(() => this.onTap()).layoutWeight(1)
    .justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
  }
}

这里只是简单封装了一下底部菜单栏的表现,支持基本的功能。如果有更强的自定义需求,你也可以优化自己的 BottomAppBar 版本,比如增加动画、支持更多属性样式的配置等。


3.关于切换时界面状态

到这里,四个界面就整合到一个项目中,当前代码位置提交一个小里程碑 v22-项目整合
这里用一下可以发现,使用 Tab 组件切换界面,界面的活性会一直保持。也就是切换界面,之前的数据不会丢失。这也表示,界面切走后并没有被销毁,使用进入时也没有重新被初始化。至于界面组件生命周期的细节,将在后期专门介绍。

- -

尾声:待优化点

虽然现在程序运行期间,可以保证各界面状态类的活性,但是当应用关闭之后,内存中的数据会被清空。再次进入应用时还是无法恢复到之前的状态。想要永久记住用户的信息,就必须对数据进行持久化的存储。比如存储为本地文件、数据库、网络数据等,在程序启动时进行加载,恢复状态数据。这是应用程序非常重要的一个部分,后面将介绍数据的持久化存储。

更多文章和视频知识资讯,大家可以关注我的公众号、掘金和 B 站 。关注 公众号 并回复 鸿蒙纪元 可领取最新的 xmind 脑图电子版,让我们一起成长,变得更强。我们下次再见~

注册登录 后评论
    // 作者
    张风捷特烈 发布于 掘金
    • 0
    // 本帖子
    分类
    // 相关帖子
    Coming soon...
    • 0
    鸿蒙纪·梦始卷#15 | 应用界面整合张风捷特烈 发布于 掘金