1. HarmonyOS介绍
1.1 HarmonyOS简介
随着万物互联时代的开启,应用的设备底座将从几十亿手机扩展到数百亿IoT设备。全新的全场景设备体验,正深入改变消费者的使用习惯。 同时应用开发者也面临设备底座从手机单设备到全场景多设备的转变,全场景多设备的全新底座,为消费者带来万物互联时代更为高效、便捷的体验。
新的场景同时也带来了新的挑战。开发者不仅需要支持更加多样化的设备,还需要支持跨设备的协作。不同设备类型意味着不同的传感器能力、硬件能力、屏幕尺寸、操作系统和开发语言,还意味着差异化的交互方式。同时跨设备协作也让开发者面临分布式开发带来的各种复杂性,例如跨设备的网络通信、数据同步等。若采取传统开发模式,适配和管理工作量将非常巨大。
HarmonyOS是新一代的智能终端操作系统,为不同设备的智能化、互联与协同提供了统一的语言,为用户带来简捷,流畅,连续,安全可靠的全场景交互体验。
三大技术理念
一次开发,多端部署
可分可合,自由流转
统一生态,原生智能
核心特性
-
分布式能力:跨设备协同(如手机调用平板摄像头、多设备数据同步)。
-
统一 OS:适用于手机、平板、智能家居、车载设备等全场景。
-
高性能:方舟编译器优化,提升运行效率。
-
安全机制:微内核设计,权限精细化管理。
2. 开发须知
2.1 工具
2.2 HarmonyOS SDK
HarmonyOS SDK是面向鸿蒙原生应用和元服务开发的开放能力合集,提供包括应用框架、应用服务、系统、媒体、AI、图形在内的六大领域丰富完备的开放能力,助您构建焕然一新的鸿蒙原生应用和元服务,带来创新易用的全场景体验。
3. DevEco Studio的使用
3.1 安装DevEco Studio
DevEco Studio支持Windows和macOS系统,为了保证DevEco Studio正常运行,建议电脑配置满足如下要求:
Windows
-
操作系统:Windows10 64位、Windows11 64位
-
内存:16GB及以上
-
硬盘:100GB及以上
-
分辨率:1280*800像素及以上
macOs
-
操作系统:macOS(X86) 11/12/13/14 macOS(ARM) 12/13/14
-
内存:8GB及以上
-
硬盘:100GB及以上
-
分辨率:1280*800像素及以上
DevEco Studio提供开箱即用的开发体验,将HarmonyOS SDK、Node.js、Hvigor、OHPM、模拟器平台等进行合一打包,简化DevEco Studio安装配置流程,所以我们在满足对应电脑配置条件,直接官网(developer.huawei.com/consumer/cn...
3.2 首次打开,新建项目
3.3 如何汉化DevEco Studio?
打开 File> Settings
然后选择Plugins, 然后输入"Chinese", 然后Enable按钮开启,根据提示重新加载编辑器
便可汉化成功
3.4 声明式UI代码提示的好处?
与其他编辑器一样,代码生成/补全、代码实时检查及快速修复等功能都是具备的,也不详细阐述,前面我们提过ArkUI不同我们以往的命令式UI编程方式,它是一种声明式UI框架编程,那么代码提示对我们鸿蒙应用的开发也就
会起很大的帮助。
我们可以很快的知道Text组件有哪些属性方法,而且从属性方法字面意思,以及入参定义,可以更快地帮助我们去学习ArkUI的使用。
3.5 预览器的使用
预览器支持"实时预览"和"动态预览"。
以ArkTS为例,使用预览器的方法如下:
-
创建或打开一个ArkTS应用/元服务工程。本示例以打开一个本地ArkTS Demo工程为例。
-
在工程目录下,打开任意一个.ets文件,我们这里以我们刚才建立的Hello World应用为例子(src/main/ets/pages/Index.ets)。
-
通过菜单栏,单击视图 > 工具窗口 >预览器打开预览器:
- 多端模拟预览
- 页面预览与组件预览
3.6 模拟器的安装和使用
- 创建模拟器
- 等待模拟器系统镜像安装成功,设置虚拟器名称、内存、存储空间
- 在DevEco Studio 4.0 Release或更高版本,当我们的应用需要使用某些受限开放权限,需要使用自动签名功能
- 在编辑器右上角便可以选到我们下载好的模拟器,点击启动图标
- 最终便可以成功在模拟器部署我们的app应用
3.7 CodeGenie - AI辅助问答工具
DevEco Studio 集成了AI辅助问答工具CodeGenie,开发者可以使用该工具解决HarmonyOs开发所遇到得难题或是查询API的使用,提升开发的效率。前提是:登录华为开发者账户,才能使用CodeGenie
4. ArkTs与ArkUI
ArkTS是一种为构建高性能应用而设计的编程语言。ArkTS在继承TypeScript语法的基础上进行了优化,以提供更高的性能和开发效率。它专注于低运行时开销,取消动态类型特性(如任意类型Any的滥用),同时提供对方舟开发框架ArkUI的声明式语法和其他特性的支持,这也是TypeScript不具备的。
ArkUI(方舟UI框架)为应用的UI开发提供了完整的基础设施,包括简洁的UI语法、丰富的UI功能(组件、布局、动画以及交互事件),以及实时界面预览工具等,可以支持开发者进行可视化界面开发。
ArkUI框架提供了两种开发范式,分别是基于ArkTS的声明式开发范式(简称"声明式开发范式")和兼容JS的类Web开发范式(简称"类Web开发范式")
4.1 ArkTS的组成
通过我们的Hello World这个应用的index.ets(也是就是ArkTs入口文件),来分析其组成, 从而去理解基于ArkTS的声明式开发范式是如何开发的。
-
装饰器: 用于装饰类、结构、方法以及变量,并赋予其特殊的含义。如上述示例中@Entry、@Component和@State都是装饰器,@Component表示自定义组件,@Entry表示该自定义组件为入口页面,@State表示组件中的状态变量,状态变量变化会触发UI刷新。
-
自定义组件:可复用的UI单元,可组合其他组件,如上述被@Component装饰的struct Index。
-
UI描述:以声明式的方式来描述UI的结构,例如build()方法中的代码块。
-
系统组件:ArkUI框架中默认内置的基础和容器组件,可直接被开发者调用,比如示例中的RelativeContainer、Text,也就是这些系统组件,UI构建与显示的最小单位,像列表、网格、按钮、单选框、进度条、文本等,开发者通过多种组件的组合,构建出满足自身应用诉求的完整界面。
-
属性方法:组件可以通过链式调用配置多项属性,如fontSize()、width()、height()、backgroundColor()等。
-
事件方法:组件可以通过链式调用设置多个事件的响应逻辑,如跟随在Text后面的onClick()。
4.2 页面和自定义组件生命周期
在开始之前,我们先明确自定义组件和页面的关系:
-
自定义组件:@Component装饰的struct,可以组合多个系统组件实现UI的复用,可以调用组件的生命周期。
-
页面:即应用的UI页面。可以由一个或者多个自定义组件组成,@Entry装饰的自定义组件为页面的入口组件,即页面的根节点,一个页面有且仅能有一个@Entry。只有被@Entry装饰的组件才可以调用页面的生命周期。
4.2.1 页面组件的生命周期
页面生命周期,即被@Entry装饰的组件生命周期,提供以下生命周期接口:
-
onPageShow:页面每次显示时触发一次,包括路由过程、应用进入前台等场景。
-
onPageHide:页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景。
-
onBackPress:当用户点击返回按钮时触发。
组件生命周期,即一般用@Component装饰的自定义组件的生命周期,提供以下生命周期接口:
-
aboutToAppear:组件即将出现时回调该接口,具体时机为在创建自定义组件的新实例后,在执行其build()函数之前执行。
-
onDidBuild:组件build()函数执行完成之后回调该接口,开发者可以在这个阶段进行埋点数据上报等不影响实际UI的功能。不建议在onDidBuild函数中更改状态变量、使用animateTo等功能,这可能会导致不稳定的UI表现。
-
aboutToDisappear:aboutToDisappear函数在自定义组件析构销毁之前执行。不允许在aboutToDisappear函数中改变状态变量,特别是@Link变量的修改可能会导致应用程序行为不稳定。
我们通过以下简单示例展示了生命周期的调用时机:
在Index页面组件,声明当前Index页面的页面生命周期函数(onPageShow / onPageHide / onBackPress),以及组件的生命周期函数(aboutToAppear / onDidBuild/aboutToDisappear)
在Index页面组件,引入ChildComponent子组件。声明ChildComponent自定义组件的声明周期函数(aboutToAppear / onDidBuild/aboutToDisappear)
同时在Index页面添加Button按钮,点击可以跳转Page页面,同时将Index页面组件销毁,以此来展示生命周期的调用时机
点击按钮后,我们查看打印日志:
5. 构建更加丰富的页面
接下来,我们通过构建更加丰富的页面,来进一步学习ArkTs,以及通过ArkUI提供的各类系统组件(如文本Text, 图片Image,列表List、网格Grid、轮播Swiper、导航Tab等),这样可以更快上手应用的开发学习。
5.1 Tab组件搭建页面架构
那么首先,我们把原来的Hello World的Index.ets的代码进行清空,我们可以需要先实现Tab组件,搭好页面的架构,
我们采用底部导航方式搭建页面架构,代码如下:
typescript
@Entry
@Component
struct Index {
@State currentIndex: number = 0
private tabsController : TabsController = new TabsController();
// 声明tabBuilder的自定义函数组件
@Builder
tabBarBuilder(title: string, targetIndex: number, selectedIcon: Resource, unselectIcon: Resource) {
Column() {
Image(this.currentIndex === targetIndex ? selectedIcon : unselectIcon)
.width(24)
.height(24)
Text(title)
.fontFamily('HarmonyHeiTi-Medium')
.fontSize(10)
.fontColor(this.currentIndex === targetIndex ? '#0A59F7' : 'rgba(0,0,0,0.60)')
.textAlign(TextAlign.Center)
.lineHeight(14)
.fontWeight(500)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
.onClick(() => {
this.currentIndex = targetIndex;
this.tabsController.changeIndex(targetIndex);
})
}
build() {
Tabs({ barPosition: BarPosition.End, controller: this.tabsController }) {
// 设置导航内容页
TabContent() {
Text('快速入门')
}
// 设置导航页签栏
.tabBar(this.tabBarBuilder('快速入门', 0, $r('app.media.ic_01_on'), $r('app.media.ic_01_off')))
TabContent() {
Text('课程学习')
}
.tabBar(this.tabBarBuilder('课程学习', 1, $r('app.media.ic_01_on'), $r('app.media.ic_01_off')))
TabContent() {
Text('知识地图')
}
.tabBar(this.tabBarBuilder('知识地图', 2, $r('app.media.ic_01_on'), $r('app.media.ic_01_off')))
}
.scrollable(false)
.vertical(false)
.divider({
strokeWidth: 0.5,
color: '#0D182431'
})
.backgroundColor('#F1F3F5')
}
}
5.2 Swiper组件构建轮播
typescript
@Entry
@Component
struct Index {
@State message: string = '快速入门';
...
build() {
Tabs({ barPosition: BarPosition.End, controller: this.tabsController }) {
// 设置导航内容页
TabContent() {
Text(this.message)
.fontSize(24)
.fontWeight(700)
.width('100%')
.textAlign(TextAlign.Start)
.padding({ left: 16 })
.fontFamily('HarmonyHeiTi-Bold')
.lineHeight(33)
Column() {
// 自定义组件 - 轮播
Banner()
}
}
// 设置导航页签栏
.tabBar(this.tabBarBuilder('快速入门', 0, $r('app.media.ic_01_on'), $r('app.media.ic_01_off')))
...
}
...
}
}
Banner组件的实现:
typescript
class BannerClass {
id: string = '';
imageSrc: string= '';
url: string = '';
constructor(id: string, imageSrc: string, url: string) {
this.id = id;
this.imageSrc = imageSrc;
this.url = url;
}
}
@Component
struct Banner {
@State bannerList: Array<BannerClass> = [
new BannerClass('pic0', 'app.media.banner_pic0',
'https://developer.huawei.com/consumer/cn/training/course/video/C101718352529709527'),
new BannerClass('pic1', 'app.media.banner_pic1',
'https://developer.huawei.com/consumer/cn/'),
new BannerClass('pic2', 'app.media.banner_pic2',
'https://developer.huawei.com/consumer/cn/deveco-studio/'),
new BannerClass('pic3', 'app.media.banner_pic3',
'https://developer.huawei.com/consumer/cn/arkts/'),
new BannerClass('pic4', 'app.media.banner_pic4',
'https://developer.huawei.com/consumer/cn/arkui/'),
new BannerClass('pic5', 'app.media.banner_pic5',
'https://developer.huawei.com/consumer/cn/sdk')
];
build() {
Swiper() {
ForEach(this.bannerList, (item: BannerClass, index: number) => {
Image($r(item.imageSrc))
// objectFit属性使图片缩放到高度和宽度确定的框内
.objectFit(ImageFit.Contain)
.width('100%')
.padding({ top: 11, left: 16, right: 16 })
.borderRadius(16)
}, (item: BannerClass, index: number) => item.id)
}
.autoPlay(true)
.loop(true)
.indicator(
new DotIndicator()
.color('#1a000000')
.selectedColor('#0A59F7')
)
}
}
运行我们的代码后,
5.3 Grid组件开发 "赋能套件"
typescript
@Entry
@Component
struct Index {
@State message: string = '快速入门';
...
build() {
Tabs({ barPosition: BarPosition.End, controller: this.tabsController }) {
// 设置导航内容页
TabContent() {
...
Column() {
// 自定义组件 - 轮播
Banner()
// 自定义组件 - Grid实现"赋能套件区域"
EnablementView()
}
}
// 设置导航页签栏
.tabBar(this.tabBarBuilder('快速入门', 0, $r('app.media.ic_01_on'), $r('app.media.ic_01_off')))
...
}
...
}
}
EnablementView组件的实现
typescript
export class ArticleClass {
id: string = '';
imageSrc: string = '';
title: string = '';
brief: string = '';
webUrl: string = '';
constructor(id: string, imageSrc: string, title: string, brief: string, webUrl: string) {
this.id = id;
this.imageSrc = imageSrc;
this.title = title;
this.brief = brief;
this.webUrl = webUrl;
}
}
@Component
struct EnablementView {
@State enablementList: Array<ArticleClass> = [
new ArticleClass('1', 'app.media.tutorial_pic1', 'Step1 环境的搭建',
'本篇教程实现了快速入门------一个用于了解和学习HarmonyOS的应用程序。',
'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
...
];
build() {
Column(){
Text('赋能套件')
.fontColor('#182431')
.fontSize(16)
.fontWeight(500)
.fontFamily('HarmonyHeiTi-medium')
.textAlign(TextAlign.Start)
.padding({ left: 16 })
.margin({ bottom: 8.5 })
Grid() {
ForEach(this.enablementList, (item: ArticleClass, index: number) => {
GridItem() {
EnablementItem({ enablementItem: item })
}
}, (item: ArticleClass, index: number) => item.id)
}
// rowsTemplate和columnsTemplate属性值是一个由多个空格和'数字+fr'间隔拼接的字符串,
// fr的个数即网格布局的行或列数,fr前面的数值大小,
// 用于计算该行或列在网格布局宽度上的占比,最终决定该行或列宽度。
// scrollBar用于关闭滚动条
.rowsTemplate('1fr')
.columnsGap(8)
.scrollBar(BarState.Off)
.height(169)
.padding({ top: 2, left: 16, right: 16 })
}
.margin({ top: 18 })
.alignItems(HorizontalAlign.Start)
}
}
EnablementItem组件的实现:
typescript
EnablementItem {
@Prop enablementItem: ArticleClass; // @Prop装饰器
build() {
Column() {
Image($r(this.enablementItem.imageSrc))
.width('100%')
.objectFit(ImageFit.Cover)
.height(96)
.borderRadius({
topLeft: 16,
topRight: 16
})
Text(this.enablementItem.title)
.height(16)
.width('100%')
.fontSize(14)
.textAlign(TextAlign.Start)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.maxLines(1)
.fontWeight(400)
.padding({ left: 12, right: 12 })
.margin({ top: 8 })
Text(this.enablementItem.brief)
.height(32)
.width('100%')
.fontSize(12)
.textAlign(TextAlign.Start)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.maxLines(2)
.fontWeight(400)
.fontColor('rgba(0, 0, 0, 0.6)')
.padding({ left: 12, right: 12 })
.margin({ top: 2 })
}
.width(160)
.height(169)
.borderRadius(16)
.backgroundColor(Color.White)
}
}
运行效果为
5.4 List组件开发"入门教程"
typescript
@Entry
@Component
struct Index {
@State message: string = '快速入门';
...
build() {
Tabs({ barPosition: BarPosition.End, controller: this.tabsController }) {
// 设置导航内容页
TabContent() {
Collumn() {
...
Scroll() {
Column() {
// 自定义组件 - 轮播
Banner()
// 自定义组件 - Grid实现"赋能套件区域"
EnablementView()
// 自定义组件 - List实现"入门教程区域"
TutorialView()
}
}
.layoutWeight(1)
.scrollBar(BarState.Off)
.align(Alignment.TopStart)
}
}
// 设置导航页签栏
.tabBar(this.tabBarBuilder('快速入门', 0, $r('app.media.ic_01_on'), $r('app.media.ic_01_off')))
...
}
...
}
}
TutorialView组件的实现
typescript
class ArticleClass {
...
constructor(id: string, imageSrc: string, title: string, brief: string, webUrl: string) {
...
}
}
@Component
struct TutorialView {
@State tutorialList: Array<ArticleClass> = [
new ArticleClass('1', 'app.media.tutorial_pic1', 'Step1 环境的搭建',
'本篇教程实现了快速入门------一个用于了解和学习HarmonyOS的应用程序。',
'https://developer.huawei.com/consumer/cn/forum/home?all=1'),
...
];
build() {
Column() {
Text('入门教程')
.fontColor('#182431')
.fontSize(16)
.fontWeight(500)
.fontFamily('HarmonyHeiTi-medium')
.textAlign(TextAlign.Start)
.padding({ left: 16 })
.margin({ bottom: 8.5 })
// 设置列表项之间为12vp的距离
List({ space: 12 }) {
ForEach(this.tutorialList, (item: ArticleClass) => {
ListItem() {
TutorialItem({ tutorialItem: item })
}
}, (item: ArticleClass) => item.id)
}
.scrollBar(BarState.Off)
.padding({ left: 16, right: 16 })
// lanes属性用于确定交叉轴排列的列表项数量。这里我们的交叉轴是横轴,则横向我们是每行只有一个ListItem
.lanes(1)
}
.margin({ top: 18 })
.alignItems(HorizontalAlign.Start)
}
}
TutorialItem组件的实现
typescript
struct TutorialItem {
@Prop tutorialItem: ArticleClass
build() {
Row() {
Column() {
Text(this.tutorialItem.title)
.height(19)
.width('100%')
.fontSize(14)
.textAlign(TextAlign.Start)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.maxLines(1)
.fontWeight(400)
.margin({ top: 4 })
Text(this.tutorialItem.brief)
.height(32)
.width('100%')
.fontSize(12)
.textAlign(TextAlign.Start)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.maxLines(2)
.fontWeight(400)
.fontColor('rgba(0, 0, 0, 0.6)')
.margin({ top: 5 })
}
.height('100%')
// 这个属性决定了组件可以占用其父容器剩余空间的多少
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
.margin({ right: 12 })
Image($r(this.tutorialItem.imageSrc))
.objectFit(ImageFit.Cover)
.height(64)
.width(108)
.borderRadius(16)
}
.width('100%')
.height(88)
.borderRadius(16)
.backgroundColor(Color.White)
.padding(12)
.alignItems(VerticalAlign.Top)
}
}
运行效果为
6. 使用Navigation实现路由导航
6.1 Navigation介绍
**组件导航(Navigation)**主要用于实现页面间以及组件内部的页面跳转,支持在不同组件间传递跳转参数,提供灵活的跳转栈操作,从而更便捷地实现对不同页面的访问和复用。
Navigation是路由导航的根视图容器,NavDestination是子页的根视图容器, 一般都作为组件的根容器,
typescript
@Entry
@Component
export struct Page {
build() {
Navigation() {
Column() {
...
}
}
}
}
@Component
export struct ChildPage {
build() {
NavDestination() {
Column() {
...
}
}
}
}
Navigation组件主要包含导航页和子页。导航页由标题栏(包含菜单栏)、内容区和工具栏组成,分别可以通过hideTitleBar、hideNavBar、hideToolBar属性进行隐藏,导航页不存在页面栈中,与子页,以及子页之间可以通过路由操作进行切换。
NavDestination是Navigation子页面的根容器,用于承载子页面的一些特殊属性以及生命周期等。NavDestination可以设置独立的标题栏和菜单栏等属性,使用方法与Navigation相同。NavDestination也可以通过mode属性设置不同的显示类型,用于满足不同页面的诉求。
6.2 路由栈NavPathStack
Navigation路由相关的操作都是基于页面栈NavPathStack提供的方法进行,每个Navigation都需要创建并传入一个NavPathStack对象,用于管理页面。主要涉及页面跳转、页面返回、页面替换、页面删除、参数获取、路由拦截等功能。
typescript
@Entry
@Component
struct Index {
// 创建一个页面栈对象并传入Navigation
pageStack: NavPathStack = new NavPathStack()
build() {
Navigation(this.pageStack) {
}
.title('Main')
}
}
那么我们拿对应的this.pageStack来进行路由跳转,如:
typescript
this.pageStack.pushPath({ name: "PageOne", param: "PageOne Param" })
this.pageStack.pushPathByName("PageOne", "PageOne Param")
// 返回到上一页
this.pageStack.pop()
// 返回到上一个PageOne页面
this.pageStack.popToName("PageOne")
// 返回到索引为1的页面
this.pageStack.popToIndex(1)
// 返回到根首页(清除栈中所有页面)
this.pageStack.clear()
// 将栈顶页面替换为PageOne
this.pageStack.replacePath({ name: "PageOne", param: "PageOne Param" })
this.pageStack.replacePathByName("PageOne", "PageOne Param")
// 获取栈中所有页面name集合
this.pageStack.getAllPathName()
// 获取索引为1的页面参数
this.pageStack.getParamByIndex(1)
// 获取PageOne页面的参数
this.pageStack.getParamByName("PageOne")
// 获取PageOne页面的索引集合
this.pageStack.getIndexByName("PageOne")
// 等等...
6.3 路由导航示例学习
接下来我们还是以上面我们"快速入门"Tab页签的内容来实现点击Banner跳转到具体文章详情页的效果,来学习一下Navigation的用法:
typescript
// 我们将上面"快速入门"页签 的TabContent内容区代码进行提取封装QuickStartPage组件上
@Component
export struct QuickStartPage {
// 1、首先我们实例化一个articlePathStack的路由栈对象,作为Navigation组件的入参
// @Provide('articlePathStack')装饰器的意思类似于我们Vue的provide,能articlePathStack路由栈
// 透传到各个层级子组件进行使用
@Provide('articlePathStack') articlePathStack: NavPathStack = new NavPathStack();
// 3、navDestination属性方法的入参,一个自定义函数方法,充当路由表的作用,入参name
// 当路由栈对象this.articlePathStack.pushPath({ name: "PageOne", param: "PageOne Param" })
// 执行路由操作的时候,可以根据那么匹配到对应页面组件,进行跳转
// 这种方式是 静态import页面再进行路由跳转
// 另一种方式是动态路由方式
@Builder
quickStartRouter(name: string, param?: ArticleClass | BannerClass) {
if (name === 'bannerDetail') {
BannerDetailPage()
}
}
build() {
// 2、Navigation作为根容器,并且传入路由栈对象articlePathStack
Navigation(this.articlePathStack) {
Column() {
Header()
Scroll() {
Column() {
Banner()
EnablementView()
TutorialView()
}
}
.layoutWeight(1)
.scrollBar(BarState.Off)
.align(Alignment.TopStart)
}
.height('100%')
.width('100%')
.backgroundColor('#F1F3F5')
}
.hideTitleBar(true)
.navDestination(this.quickStartRouter)
}
}
看完上面这个导航页的实现,接下来我们看看子页的实现
typescript
@Component
export struct BannerDetailPage {
// 接受上层组件送过来的路由栈对象articlePathStack
@Consume('articlePathStack') articlePathStack: NavPathStack;
build() {
NavDestination() {
Column() {
Text('我是Banner详情组件')
Button('返回导航页').onClick(() => {
// 进行路由操作
this.articlePathStack.pop()
})
}
}
.width('100%')
.height('100%')
}
}
那么我现在需要在Banner组件上实现点击跳转的动作,让其可以从导航页跳转到子页上去
typescript
@Component
struct Banner {
@State bannerList: Array<BannerClass> = [
...
];
// 依然接收上层组件的路由栈对象articlePathStack
@Consume('articlePathStack') articlePathStack: NavPathStack;
build() {
Swiper() {
ForEach(this.bannerList, (item: BannerClass, index: number) => {
Image($r(item.imageSrc)).onClick(() => {
// 我们点击Banner图,然后跳转到路由名字为"bannerDetail"的页面
this.articlePathStack.pushPathByName('bannerDetail', item);
})
}, (item: BannerClass, index: number) => item.id)
}
.autoPlay(true)
.loop(true)
.indicator(
new DotIndicator()
.color('#1a000000')
.selectedColor('#0A59F7')
)
}
}
运行得到得效果是
7. 网络请求得两种方式-Http、RCP
7.1 HTTP数据请求
应用通过HTTP发起一个数据请求,支持常见的GET、POST、OPTIONS、HEAD、PUT、DELETE、TRACE、CONNECT方法, 使用该功能需要申请ohos.permission.INTERNET权限,应用的 module.json5
文件中进行配置
typescript
"permissions": [
"ohos.permission.INTERNET"
]
通过一个简单request接口来学一下如何使用
typescript
// 1、从@kit.NetworkKit中导入http
import { http } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 2、调用createHttp()方法,创建一个HttpRequest对象
let httpRequest = http.createHttp();
// 3、订阅HTTP Response Header 事件等其他事件
httpRequest.on('headersReceive', (header) => {
console.info('header: ' + JSON.stringify(header));
});
// ...
httpRequest.request(
"EXAMPLE_URL", // 第一个参数是请求路径
// 第二个参数是配置信息
{
// 请求方式
method: "POST",
// 自定义Header
header: {
'Content-Type': 'application/json'
},
// ...其他配置信息
},
// 第三参数是响应回调
(err: BusinessError, data: http.HttpResponse) => {
if (!err) {
... // 处理data
// 当该请求使用完毕时,调用destroy方法主动销毁
httpRequest.destroy();
} else {
console.error('error:' + JSON.stringify(err));
// 取消订阅HTTP响应头事件
httpRequest.off('headersReceive');
// 当该请求使用完毕时,调用destroy方法主动销毁
httpRequest.destroy();
}
}
)
7.2 RPC数据请求
Remote Communication Kit中的@hms.collaboration.rcp(后续简称RCP )指的是远程通信平台 (remote communication platform),RCP提供了网络数据请求功能 ,相较于Network Kit中HTTP请求能力,RCP更具易用性,且拥有更多的功能。在开发过程中,如果有些场景使用Network Kit中HTTP请求能力达不到预期或无法实现,那么就可以尝试使用RCP中的数据请求功能来实现。
通过一个简单案例来学一下如何使用
typescript
// 导入包
import { rcp } from '@hms.collaboration';
// 定义请求头
let headers: rcp.RequestHeaders = {
'accept': 'application/json'
};
// 创建通信会话对象
const session = rcp.createSession();
// 定义请求对象rep,声明一个PATCH方式的请求
let req = new rcp.Request('http://example.com/fetch', 'PATCH', headers);
// 发起请求
session.fetch(req).then((response) => {
Logger.info(`Request succeeded, message is ${JSON.stringify(response)}`);
}).catch((err: BusinessError) => {
Logger.error(`err: err code is ${err.code}, err message is ${JSON.stringify(err)}`);
});
8. 总结
在前面,我们首先从三大技术理念和核心的特性介绍了HarmonyOs的优势,然后在进一步开发HarmonyOs应用前,我们了解到要开发需要知道使用什么编辑器,使用什么语言和UI框架,接着我们介绍DevEco Studio的使用,知道怎么建项目,预览器和模拟器的使用,以及AI辅助问答工具的使用。
在这些基础上,我们介绍ArKTs的基本组成,以及对ArkUI声明式特性的支持,同时了解开发页面和组件的生命周期执行时机,然后用Tab、Swiper、Grid、List等ArkUI系统组件来构建一个丰富的页面,加深理解和使用。
有了页面,我们使用使用Navigation实现路由导航,再介绍如何使用Http、Rcp进行网络请求,形成基本的学习入门闭环。
由于时间问题,前面所介绍的内容也是相对简单,以及比较多的细节也没有讲到,更多的学习内容:
1、系统调度的基本单元 UIAbility组件的了解和使用场景?
2、Navigation实现路由导航如何采用动态路由实现?
3、NavDestination负责管理的路由生命周期如何使用?
4、如何创建一个WebView?
5、如何保存应用的数据(持久化存储)?
6、模块的MVVM, 工程的三层架构?
7、页面如何自适应不同设备?