快速入门HarmonyOS应用开发(三) - 教程

news/2025/9/25 14:54:03/文章来源:https://www.cnblogs.com/yfceshi/p/19111200

目录

前言

1、应用沉浸式

2、主页框架搭建

3、实现效果

前言

本系列案例完整代码已上传GitHub:HarmonyDemos

------觉得还行的麻烦动动发财的小手点个小星星啦^_^

今天咱们接着上一篇中的功能来继续实现案例Demo,做完闪屏页之后肯定是要到主页了,那今天咱们就来搭建一下APP的主页框架,实现底部的Tab和对应页面的切换,话不多说,开干!

1、应用沉浸式

首先来给咱们的应用实现沉浸式效果,参考官网中的实现方式:

开发应用沉浸式效果

我们选择的方案是窗口全屏布局方案,整体上分为以下几个步骤:

①、调用setWindowLayoutFullScreen()接口设置窗口全屏

②、使用getWindowAvoidArea()接口获取当前布局遮挡区域

③、注册监听函数,动态获取避让区域的实时数据

④、布局中的UI元素需要避让状态栏和导航区域

实现代码:在EntryAbility的onWindowStageCreate()的loadContent()回调中添加代码

// 使用同步方式获取应用主窗口
let windowClass: window.Window = windowStage.getMainWindowSync()
// 设置状态栏字体颜色
windowClass.setWindowSystemBarProperties({
statusBarContentColor: '#FFFFFF'
})
// 设置窗口全屏
windowClass.setWindowLayoutFullScreen(true)
// 获取布局避让遮挡的区域
let topType = window.AvoidAreaType.TYPE_SYSTEM
let topArea = windowClass.getWindowAvoidArea(topType)
let topRectHeight = topArea.topRect.height
AppStorage.setOrCreate('topRectHeight', topRectHeight)
let bottomType = window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR
let bottomArea = windowClass.getWindowAvoidArea(bottomType)
let bottomRectHeight = bottomArea.bottomRect.height
AppStorage.setOrCreate('bottomRectHeight', bottomRectHeight)
// 注册监听函数,动态获取避让区域的实时数据
windowClass.on('avoidAreaChange', (data) => {
if (data.type === window.AvoidAreaType.TYPE_SYSTEM) {
let topRectHeight = data.area.topRect.height
AppStorage.setOrCreate('topRectHeight', topRectHeight)
} else if (data.type == window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR) {
let bottomRectHeight = data.area.bottomRect.height
AppStorage.setOrCreate('bottomRectHeight', bottomRectHeight)
}
})

关于第4步需要在实际的页面中设置布局的底部Padding:

NavDestination() {
}.hideTitleBar(true)
.padding({bottom:DisplayUtil.getBottomRectHeight(this.getUIContext())})

这里用到了一个工具方法,其实就是对之前存储的数据进行获取之后封装的一个方法,代码如下:

export class DisplayUtil {
static getTopRectHeight(context: UIContext): number {
return context.px2vp(AppStorage.get('topRectHeight') as number)
}
static getBottomRectHeight(context: UIContext): number {
return context.px2vp(AppStorage.get('bottomRectHeight') as number)
}
}

从上面的代码中可以看到咱们有使用AppStorage进行存储和获取数据,关于AppStorage的使用,大家可以参考:

AppStorage:应用全局的UI状态存储

它是HarmonyOS中提供的用于全局内存缓存的机制,与应用进程绑定,需要注意的是它并不是持久化的存储,是内存缓存。

2、主页框架搭建

想要实现底部Tab之间的切换效果,我们需要使用到一个新的系统提供的组件Tabs组件,参考:

选项卡 (Tabs)

打开MainPage页面,我们需要的整体结构是上下结构,上面是每个Tab的页面,下面是Tab按钮。

首先,我们来自定义底部的Tab按钮,这里放置5个按钮,先来定义每个按钮的样式:

@State currentTabIndex: number = CommonConstant.TAB_INDEX
@Builder
TabItem(tabInfo: TabInfo) {
Column() {
Image(this.currentTabIndex === tabInfo.index ? tabInfo.selectImg : tabInfo.img)
.objectFit(ImageFit.Contain)
.width($r('app.float.vp_24'))
.height($r('app.float.vp_24'))
.margin({ bottom: $r('app.float.vp_5') })
Text(tabInfo.title)
.width(CommonConstant.FULL_PERCENT)
.height($r('app.float.vp_16'))
.fontSize($r('app.float.fp_12'))
.textAlign(TextAlign.Center)
.fontColor(this.currentTabIndex === tabInfo.index ? $r("app.color.color_primary") :
$r('app.color.color_unselect'))
}
.width(CommonConstant.FULL_PERCENT)
.height(CommonConstant.FULL_PERCENT)
.justifyContent(FlexAlign.Center)
.width(CommonConstant.PERCENT_20)
.onClick(() => {
this.currentTabIndex = tabInfo.index
})
}

这里我们用到了@Builder这个装饰器,它是自定义的构建函数,关于它的使用,大家可以参考:

@Builder装饰器:自定义构建函数

在点击这个按钮的时候,通过对上面定义的currentTabIndex变量值的改变来切换不同的下标。

接着,通过循环来实现5个Tab按钮的创建:

@Builder
BottomTabView() {
Column() {
Divider().width(CommonConstant.FULL_PERCENT).color(Color.Black)
.opacity(CommonConstant.OPACITY_01).strokeWidth(CommonConstant.WEIGHT_1)
Row() {
ForEach(tabInfos, (button: TabInfo) => {
this.TabItem(button)
})
}
.width(CommonConstant.FULL_PERCENT)
.height($r('app.float.vp_50'))
.alignItems(VerticalAlign.Center)
.backgroundColor(Color.White)
}.width(CommonConstant.FULL_PERCENT)
}

这里我们使用了ForEach来进行循环渲染,关于ForEach的详细使用,大家可以参考:

ForEach:循环渲染

这里ForEach的第一个参数是我们提前准备好的Tabs的数据,我们把它放在了src/main/ets/model下面:

export class TabInfo {
index: number = 0
img: Resource = $r('app.media.tab_index')
selectImg: Resource = $r('app.media.tab_indexed')
title: Resource = $r('app.string.tab_index')
}
const tabInfos: TabInfo[] = [
{
index: 0,
img: $r('app.media.tab_index'),
selectImg: $r('app.media.tab_indexed'),
title: $r('app.string.tab_index')
},
{
index: 1,
img: $r('app.media.tab_financial'),
selectImg: $r('app.media.tab_financialed'),
title: $r('app.string.tab_financial')
},
{
index: 2,
img: $r('app.media.tab_video'),
selectImg: $r('app.media.tab_videoed'),
title: $r('app.string.tab_video')
},
{
index: 3,
img: $r('app.media.tab_message'),
selectImg: $r('app.media.tab_messaged'),
title: $r('app.string.tab_msg')
},
{
index: 4,
img: $r('app.media.tab_mine'),
selectImg: $r('app.media.tab_mined'),
title: $r('app.string.tab_mine')
}
]
export { tabInfos }

做完上面这些之后,我们就可以来实现整个页面的结构了,在MainPage的NavDestination中通过Column组件来完成上下结构的布局,上面使用Tabs组件,下面使用自定义的BottomTabView():

build() {
NavDestination() {
Column() {
Tabs({ index: this.currentTabIndex }) {
TabContent() {
HomePage()
}
TabContent() {
FinancialPage()
}
TabContent() {
VideoPage()
}
TabContent() {
MessagePage()
}
TabContent() {
MinePage()
}
}
.barHeight(CommonConstant.TAB_BAR_HEIGHT)
.scrollable(false)
.layoutWeight(CommonConstant.WEIGHT_1)
.onChange((index) => {
this.currentTabIndex = index
})
this.BottomTabView()
}
}.hideTitleBar(true)
.padding({bottom:DisplayUtil.getBottomRectHeight(this.getUIContext())})
}

可以看到我们一共创建了5个TabContent(),每个里面就是对应Tab承载的内容区域了,简单理解可以称它为页面,实际上它们都是自定义组件,比如,这里第一个TabContent()里面我们创建了HomePage()组件,依此类推我们又创建了余下的四个页面。

下面来看一下HomePage()里面都有些什么吧?由于目前我们只是为了实现页面的整体结构,所以这里咱们就简单放置了一个文本进行展示,代码如下:

@Component
export struct HomePage {
build() {
Column() {
StatusBarComponent()
Column() {
Text($r('app.string.tab_index'))
.fontSize($r('app.float.fp_20'))
.fontColor($r('app.color.color_primary'))
.fontWeight(FontWeight.Bold)
}.layoutWeight(CommonConstant.WEIGHT_1)
.justifyContent(FlexAlign.Center)
}
.width(CommonConstant.FULL_PERCENT)
.height(CommonConstant.FULL_PERCENT)
}
}

细心的大家会发现这不是还有一个StatusBarComponent()吗?它是我们自定义的一个状态栏横条,里面也很简单,只有一个跟顶部状态栏高度一致的View,代码如下:

@Component
export struct StatusBarComponent {
build() {
Blank().height(DisplayUtil.getTopRectHeight(this.getUIContext())).backgroundColor($r('app.color.color_primary'))
}
}

通过上面的步骤我们已经完成了首页整体结构的搭建。

3、实现效果

下面,让我们一起来看一下最终的实现效果吧:

主页面结构录屏

OK,今天的内容就这么多啦,下期再会!

祝:工作顺利,生活愉快!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/917107.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker + IDEA 一键部署! - 实践

Docker + IDEA 一键部署! - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco&q…

使用springboot开发一个宿舍管理系统练习项目 - 实践

使用springboot开发一个宿舍管理系统练习项目 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas&quo…

有做兼职赚钱的网站吗12315可以查询营业执照吗

本文小编给大家分享的是腾讯视频下载的视频怎么导出来_手机腾讯视频怎么缓存视频电影。相比其它的视频客户端,腾讯视频的多维度筛选,大数据比对,更有利于用户发现和推荐自己喜爱的影视剧内容。腾讯视频播放器推荐精准,越用越懂你&…

seo网站制作网站专题报道页面怎么做的

最近开始阅读java底层的源码,是因为发现越到后面越发现读源码的重要性,真的很重要,不阅读源码,你会发现“路”越走越窄。 今天看到了String的这个构造方法, /*** Initializes a newly created {code String} object so…

深圳有做网站公司wordpress onethink

可以放在服务器上,对服务器上的文件进行浏览、上传、下载,可下载文件源码。把下所有代码入在一个文件里即可,文件的后缀要为asp。thedir request("thedir")if thedir "" thenfolderini server.mappath(".")…

云南省城乡住房与建设厅网站教育网站建设情况报告

在官网可编辑表格typescript样例里 const inputRef useRef<InputRef>(null); InputRef项目报错原因是ant design的版本问题! antd 4.19版本重写了input 可通过InputRef来使用input组件的ref

CF1542

简单题/简单题/简单计数题/中等计数题/优化计数题CF1542E2 Abnormal Permutation Pairs 既然要求了字典序,那么我们可以枚举两个排列的最长公共前缀长度 \(L\) 并钦定 \(p_{L+1}<q_{L+1}\),此时 \(L+1\) 之后的位…

Manim实现涟漪扩散特效

在视频制作和数据可视化领域,涟漪扩散特效是一种常见且富有视觉吸引力的动画效果。 本文将详细介绍如何使用Manim数学动画引擎来实现这一效果,包括其实现原理、使用示例以及应用场景。 1. 实现原理 涟漪扩散特效主要…

CRMEB标准版PHP移动订单功能深度解析:多端同步方案

添加的客服进移动端个人中心统计管理里面就可以管理订单 Git仓库:https://gitee.com/ZhongBangKeJi/CRMEB

PolarFire SOC Auto Update 和 IAP 文档阅读(四) IAP

PolarFire SOC Auto Update 和 IAP 文档阅读(四) IAP 文档来自:PolarFire FPGA and PolarFire SoC FPGA Programming User GuidePolarFire SOC Auto Update 和 IAP 文档阅读一 - 所长 - 博客园 PolarFire SOC Auto Up…

CICD流程建设之持续测试实践指南

本文来自腾讯蓝鲸智云社区用户: CanWay持续测试(CT)是在软件开发周期(SDLC)期间持续检查软件质量的过程。具体指每次更改代码时定期执行的自动化测试。持续测试可及早发现并修复问题,确保软件始终准备就绪并运行良…

wordpress文章添加按钮seo检查工具

目录 项目名称&#xff1a; 项目背景&#xff1a; 项目目标&#xff1a; 项目成果&#xff1a; 经验教训及学习&#xff1a; 未来优化&#xff1a; 项目名称&#xff1a; 数字华容道小游戏 项目背景&#xff1a; 在学习编程过程中&#xff0c;为了加强特性技术和对概念…

Xcode 26.0.1 (17A400) 发布 - Apple 平台 IDE

Xcode 26.0.1 (17A400) 发布 - Apple 平台 IDEXcode 26.0.1 (17A400) 发布 - Apple 平台 IDE IDE for iOS/iPadOS/macOS/watchOS/tvOS/visonOS 请访问原文链接:https://sysin.org/blog/apple-xcode-26/ 查看最新版。原…

Tenable Nessus 10.10 (macOS, Linux, Windows) - 漏洞评估解决方案

Tenable Nessus 10.10 (macOS, Linux, Windows) - 漏洞评估解决方案Tenable Nessus 10.10 (macOS, Linux, Windows) - 漏洞评估解决方案 发布 Nessus 试用版自动化安装程序,支持 macOS Sequoia、RHEL 9、Ubuntu 24.04…

CNN+MNIST - 实践

CNN+MNIST - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Cou…

南昌做网站优化价格广州shopify代建站

:base(必须有值)&#xff1a;作用是将父类的值继承过来&#xff0c;如果不在构造函数中加入&#xff1a;base(变量) 的话&#xff0c;原父类中的 Model则无法继承过来。 例如&#xff1a;在父类MSG_Model,有连个属性&#xff0c;如图 1.子类构造函数不写:base(参数) 2.1.子类构…

SonarQube Server 2025 Release 5 (macOS, Linux, Windows) - 代码质量、安全与静态分析工具

SonarQube Server 2025 Release 5 (macOS, Linux, Windows) - 代码质量、安全与静态分析工具SonarQube Server 2025 Release 5 (macOS, Linux, Windows) - 代码质量、安全与静态分析工具 Self-managed static analysis…

HTTP协议工作原理与生产环境服务器搭建实战 - 详解

HTTP协议工作原理与生产环境服务器搭建实战 - 详解2025-09-25 14:40 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; displ…

网站建设课程简介枣庄手机网站建设公司

超精密光学3D测量仪器具有高精度、自动化程度高、实时反馈和范围广等优势。它能够实现微米级别的精确测量&#xff0c;能够精确测量产品的尺寸、形状和表面粗糙度等&#xff0c;具有广泛的应用价值和重要意义。 超精密光学3D测量仪器配备多种传感器、控制器和计算机系统&#…

超快轻量级离线翻译服务器MTranServer在腾讯云轻量应用服务器上的全流程部署指南 - 实践

超快轻量级离线翻译服务器MTranServer在腾讯云轻量应用服务器上的全流程部署指南 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important…