鸿蒙纪·梦始卷#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 脑图电子版,让我们一起成长,变得更强。我们下次再见~