鸿蒙开发HarmonyOS4.0入门与实践
准备工作
官网地址
鸿蒙开发者官网:https://developer.huawei.com/consumer/cn/develop/
工具下载
打开 HUAWEI DevEco Studio和SDK下载和升级 | 华为开发者联盟
网站,选择对应的文件点击下载安装即可
入门案例
安装好之后,选择一个空白项目创建
等待工具加载完成,打开这个 pages/Index.ets 文件
这个文件是一个入口文件,点击工具的右侧 Previewer 按钮,会出来预览界面,我们在左侧改动代码会实时的在这里显示
如果点击 Previewer 按钮出来的是一对文字,可以关掉工具,重启一下即可
上面我们修改了文字的颜色,并且给文字添加了一个点击事件,点击之后改变文字的内容为 Hello ArkTS
华为手机模拟器安装
安装文档:https://b11et3un53m.feishu.cn/wiki/LGprwXi1biC7TQkWPNDc45IXndh
ArkUI组件
Image组件
方式一:加载网络图片
1 | Image("https://pic.rmb.bdstatic.com/bjh/37f17dae02f15085e1becd5954b990839309.jpeg@h_1280") |
这种方式需要开通网络访问权限才可以在真机上正常加载
添加网络权限,更多文档说明
找到 module.json5 文件,添加如下配置
1 | { |
此时就可正常查看这个图片了

方式二:加载本地文件
1 | // 加载本地文件 |
app 是固定的开头,media.icon 表示当前图片所在目录,图片的后缀不需要写
interpolation(ImageInterpolation.High)
表示抗锯齿效果,可以提高图片的清晰度
抗锯齿打开效果
抗锯齿关闭效果
Text组件
基本用法
1 | Text("hello world") // 字体内容 |
配置国际显示
首先在 string.json 文件中定义好键值对
英文也对应的配置成一样的
然后基础的 element.string.json 中配置一个name一样的,value无所谓
然后可以使用下面方式来展示配置的国际化语言
1 | Text($r("app.string.Image_width")) // 字体内容 |
默认根据当前手机系统的语言,显示对应的value值,可以修改系统语言,显示不同的文字
TextInput组件
绑定一个值改变图片宽度
1 |
|

Button组件
普通用法
1 | Button("缩小").width(80).type(ButtonType.Circle).stateEffect(true).onClick(() => { |
type支持的类型
类型 | 描述 |
---|---|
Capsule | 胶囊型按钮(圆角默认为高度的一半)。 |
Circle | 圆形按钮。 |
Normal | 普通按钮(默认不带圆角)。 |
图片按钮
1 | Button() |
Slider滑动条
1 | // 滑块 |
Columl和Row
Column和Row在主轴方向上的对齐方式
在交叉轴的对齐方式
设置图片大小Demo
1 |
|
List和ForEach
- layoutWeight(1) 样式权重,数值越大,权重越高,会将除了其他低权重区域的高度减掉之后,剩下的都是自己的
1 | class Item { |
实现效果
Toast
1 | import promptAction from '@ohos.promptAction' |
自定义组件
新建组件 src/main/ets/components/Header.ets
1 | // 定义Header组件 |
使用方法
1 | import {Header} from '../components/Header' |
自定义构建函数
全局自定义构建函数
可以定义在组件外部,并且可以接受参数
1 | // 全局自定义构建函数,函数前面加上 @Builder |
使用方法
1 | build() |
局部构建函数
和全局定义构建函数类似,不需要添加 function 关键词,必须和 build 函数同级,不能放在 build 函数内部
1 | // 局部自定义构建函数 |
使用局部构建函数时要添加 this.xxx
1 | build() |
样式封装
公共样式封装
封装公共样式包含的属性也必须是公共的属性,特殊组件的特殊属性不支持在公共样式内
1 | // 公共样式封装 |
自定义样式封装
可以封装特殊组件的样式
1 | // 特殊组件的样式封装 |
使用
1 | // 折扣价 |
状态管理
@State
- @State装饰器标记的变量必须初始化,不能为空值
- @State支持Object,class,string,number,boolean,enum类型以及这些类型的数组
- 嵌套类型以及数组中的对象属性发生变化,无法触发页面更新
1 | class User { |
任务列表Demo
1 | // 任务对象 |
实现效果
@Prop和@Link
@prop | @LInk | |
---|---|---|
同步类型 | 单项同步 | 双向同步 |
允许装饰的变量类型 | @Prop只支持string、number、boolean、enum类型 父组件是对象类型,子组件是对象属性 不可以是数组、any |
父子类型一致:string、number、boolean、enum、object、class、以及他们的数组 数组中的元素增、删、改、查等都会引起刷新 嵌套类型以及数组中的对象属性无法引起刷新 |
初始化方式 | 不允许子组件进行初始化 | 父组件传递、禁止子组件进进行初始化 |
现在我们使用@Prop和@Link将上面的代码进行组件封装
新建 components/taskComponents/HeaderCar
定义顶部卡片组件
1 | const FinishColor = "#36D" |
新建 components/taskComponents/TaskListItem
封装任务列表组件
1 | class Task { |
最后父组件引用上面个子组件
1 | // 任务对象 |
效果一致
@Provide和@Consume
@Provide和@Consume适用于跨组件传递数据的场景
在父组件定义一个变量,并且用@Provide修饰,然后子组件或者孙子组件使用@Consume修饰接收的变量,然后父组件引用这些子组件时不需要传递参数,子组件可以自动的获取父组件的变量值。并且支持双向同步
代码示例
1 |
|
效果展示
@Observed和@ObjectLink
上面我们知道,嵌套的字段发生改变时,页面不会刷新。为了解决这个问题,我们就要使用 @Observed和@ObjectLink
现在我们来修改任务列表这个代码,我们发现点击完右侧的复选框后,文字的样式并没有发生变化
修改 components/taskComponents/TaskListItem
1 |
|
给 class Task
添加了 @Observe 修饰,然后将每一行做了组件抽离,并接收参数,使用 @ObjectLink 修饰
然后我们需要在RowItem组件中调用父组件的handleTaskChange方法,所以定义了一个handleTaskChange参数,通过父组件传递过来,但是在子组件调用时,this指向会发生变化,所以父组件在传递方法时,使用bind改变这个方法内部的this指向
现在代码的运行效果就是正常的
页面路由
- 页面栈的最大容量上限是32个,使用
router.clear()
方法可以清空页面栈,释放内存 - Router有两种跳转模式,分别为:
- router.pushUrl():目标页面不会替换当前页面,而是压入页面栈,因此可以用
router.back()
返回当前页面 - router.replaceUrl():目标页面会替换当前页面,当前页面会被销毁并释放资源,无法返回当前页面
- router.pushUrl():目标页面不会替换当前页面,而是压入页面栈,因此可以用
- Router有两种页面实例模式,分别是:
- Standard:标准页面实例,每次跳转都会新建一个目标页面压入页面栈,默认就是此模式
- Single:单实例模式,如果目标页已经在页面栈中,则距离页面栈顶部最近的同Url页面会被移动到栈顶,并重新加载
修改首页代码
1 | import router from '@ohos.router' |
修改公共的Header组件,添加点击返回功能
1 | // 定义Header组件 |
最后需要配置页面地址,找到 resources/base/profile/main_pages.json
文件,添加页面路由信息
1 | { |
如果不配置,则不会跳转
另外,在新建时,可以选择新建 Page,这样会自动的往该文件中添加路由信息
效果展示
动画
属性动画
实例代码
1 | import router from '@ohos.router' |
上面代码完成了小鱼游动的效果,点击上下箭头,可以看到小鱼很平滑的在移动
显示动画
修改上面的代码为显示动画
1 | Stack() |
组件转场动画
为小鱼添加入场动画,修改开始游戏按钮的方法
1 | if (!this.isBegin) { |
效果展示
实现摇杆功能
完整代码
1 | import router from '@ohos.router' |
Stage模型
文档介绍
在需要的时候来翻阅文档即可
生命周期
页面及组件的生命周期
完成流程图
接下来通过两个案例来查看生命周期函数的执行情况
案例一
首先给首页添加生命周期函数
1 | import router from '@ohos.router' |
在加载完首页后会触发 aboutToAppear
和 onPageShow
然后点击跳转到 pages/LifeCyclePage
,页面代码如下
1 |
|
会打印如下
- 首先调用页面的 aboutToAppear 页面创建钩子
- 然后触发组件的 aboutToAppear 页面创建钩子
- 接着触发首页的 aboutToDisappear 页面销毁钩子
- 最后触发页面的 onPageShow 显示钩子
这时在页面上显示和隐藏组件,或者增加遍历组件,都只会触发组件的 aboutToAppear 创建和 aboutToDisappear 销毁
这也再次印证了组件是不包含 onBackPress
、onPageShow
、onPageHide
这三个页面级别的生命周期函数
然后再返回首页时,会触发下面的钩子
案例二
首先准备两个页面
LifeCyclePage1.ets
1 | import router from '@ohos.router' |
LifeCyclePage2.ets
1 | import router from '@ohos.router' |
首先点击 “push跳转” 按钮,查看打印结果
会发现在不断地触发创建和隐藏钩子,但是没有触发
aboutToDisappear
页面销毁钩子,这说明通过push方式跳转的页面,系统会帮我们做缓存
接下来点击 “replace跳转” 按钮,查看打印结果
发现通过 replace 跳转会触发上一页面的销毁钩子
UIAbility的启动模式
模式介绍
模式类型 | 作用 |
---|---|
singleton | 每一个UIAbility只存在唯一实例。是默认启动模式,任务列表中只会存在一个相同的UIAbility |
standard | 每次启动UIAbility都会创建一个实例。任务列表中会存在多个相同的UIAbility |
specified | 每个UIAbility实例可以设置key标识,启动UIAbility时,需要指定Key,存在相同的Key的实力会直接被拉起,不存在则创建一个新的实例 |
案例演示
下面我们来使用一下 specified 模式
首先新建 pages/DocumentPage.ets
页面
1 | import {Header} from '../components/Header' |
接着新建文档编辑页面 pages/DocumentEdit.ets
1 | import Want from '@ohos.app.ability.Want' |
然后再首页中添加跳转按钮
1 | import router from '@ohos.router' |

然后再 ets 文件夹右键,选择新建一个 Ability,名称是 DocumentAbility.ts
完成之后会自动帮我们创建好文件,将 DocumentAbility.ts 文件中的默认打开页面修改成文档编辑页面
接着修改 src/main/resources/base/profile/main_pages.json
,设置 DocumentAbility 的启动模式为 specified
然后新建 src/main/ets/myabilitystage/MyAbilityStage.ts
接收key,并返回一个新的key
1 | import AbilityStage from '@ohos.app.ability.AbilityStage'; |
然后在 src/main/ets/myabilitystage/MyAbilityStage.ts
中指定 srcEntry
现在启动手机模拟器,查看效果,通过动画我们就实现根据Key打开Ability

网络请求
内置的Httprequest请求
准备node服务
需要安装 express
1 | npm install express |
新建 nodeServe/index.js
1 | let express = require('express'); |
准备json数据,新建 data.json 文件,内容如下,这个文件模拟了10条数据
1 | [ |
然后启动 node 服务
1 | node index.js |
测试服务是否正常运行
viewModel
新建 src/main/ets/viewModel,这个文件用来放所有页面模型数据
在该文件夹下添加如下文件
ShopInfo.ts
1 | export default class ShopInfo { |
ResponseInfo.ts
1 | class responseData { |
model
新建 src/main/ets/model 文件夹,这个文件夹用来放有关请求的文件
在该文件夹下新增
ShopModel.ts
1 | import http from '@ohos.net.http' |
pages
新建页面 ShopPage.ets
1 | import {Header} from '../components/Header' |
里面用到了 ShopItem 组件,代码如下
view
新建 src/main/ets/views 文件夹,我们将页面用到的组件都放在这个文件夹中
新增 ShopItem.ets
1 | import ShopInfo from '../viewModel/ShopInfo' |
实现效果

总结
上面商铺列表的核心请求逻辑在 ShopModel.ts 文件中,主要代码利用了内置的 httpRequest 来完成请求
1 | // 1.创建Http请求对象 |
第三方库Axios使用
工具安装
首先需要安装一个命令行工具
打开官网相关文档
,点击如下按钮
选择自己的系统进行下载
下载好之后,进入ohpm/bin 目录下,执行 init.bat
然后等待安装完成后,输入 ohpm -v
查看版本
接着配置环境变量
将 bin 目录的位置添加到环境变量中
然后再随便目录下查看版本
可以出现版本号表示安装成功
安装axios
打开**OpenHarmony三方库中心仓**网站,搜索 axios 即可查看安装和使用方式
在项目根目录下执行
1 | ohpm install @ohos/axios |
项目中使用
首先简单封装一下 axios,新建 src/main/ets/utils/service.ts
1 | import axios from '@ohos/axios' |
然后新建接口请求api文件,这个文件用来放所有的请求部分
src/main/ets/api/ShopModelApi.ts
1 | import service from "../utils/service" |
然后修改 src/main/ets/model/ShopModel.ts,使用我们上面写好的方法来加载数据
1 | import {getShopModelListFun} from '../api/ShopModelApi' |
应用数据持久化
首选项实现轻量级数据持久化
场景介绍
用户首选项为应用提供Key-Value键值型的数据处理能力,支持应用持久化轻量级数据,并对其修改和查询。当用户希望有一个全局唯一存储的地方,可以采用用户首选项来进行存储。Preferences会将该数据缓存在内存中,当用户读取的时候,能够快速从内存中获取数据。Preferences会随着存放的数据量越多而导致应用占用的内存越大,因此,Preferences不适合存放过多的数据,适用的场景一般为应用保存用户的个性化设置(字体大小,是否开启夜间模式)等。
运作机制
如图所示,用户程序通过JS接口调用用户首选项读写对应的数据文件。开发者可以将用户首选项持久化文件的内容加载到Preferences实例,每个文件唯一对应到一个Preferences实例,系统会通过静态容器将该实例存储在内存中,直到主动从内存中移除该实例或者删除该文件。
应用首选项的持久化文件保存在应用沙箱内部,可以通过context获取其路径。具体可见获取应用开发路径
。
约束限制
- Key键为string类型,要求非空且长度不超过80个字节。
- 如果Value值为string类型,可以为空,不为空时长度不超过8192个字节。
- 内存会随着存储数据量的增大而增大,所以存储的数据量应该是轻量级的,建议存储的数据不超过一万条,否则会在内存方面产生较大的开销。
使用方法
封装 PreferenceUtils 文件,添加操作缓存的几个方法。新建 src/main/ets/utils/PreferencesUtils.ts
1 | import dataPreferences from '@ohos.data.preferences'; |
然后再应用Ability启动时,去获取 Preference 实例
然后修改首页,增加了控制字体大小的功能,并且将修改后的结果保存到缓存中,重新启动时会从缓存读取上次保存的字体大小
新增一个控制字体大小的组件 src/main/ets/views/IndexFontSizePanel.ets
1 | import PreferenceUtils from "../utils/PreferencesUtils" |
然后再IndexPages中使用
1 | import RouterItem from '../viewModel/RouterItem' |
注意:首选项缓存只能在模拟器或者真机中有效
关系型数据库
新建页面
新建页面 src/main/ets/pages/TaskSqlPage.ets
1 | import {Header} from '../components/Header' |
views/task/HeaderCar
1 | const FinishColor = "#36D" |
src/main/ets/views/task/TaskListItem.ets
1 | import {Task} from '../../viewModel/TaskInfo' |
添加弹框组件
src/main/ets/views/task/TaskDialog.ets
1 |
|
src/main/ets/views/task/TaskRowItem.ets
1 | import {Task} from '../../viewModel/TaskInfo' |
封装接口方法
src/main/ets/model/TaskModel.ets
1 | import relationalStore from "@ohos.data.relationalStore" |
通知
基础通知
1 | import notify from '@ohos.notificationManager'; |
不同的通道类型,发送消息提醒的权限
notify.SlotType 枚举类型
效果展示
进度条通知
1 | import promptAction from '@ohos.promptAction' |
效果

添加行为意图
通过给通知添加行为意图,可以实现点击通知后自动返回到应用内
1 | import wantAgent, {WantAgent} from '@ohos.app.ability.wantAgent' |

黑马健康实战案例
欢迎页实现
静态代码
1 | // 文字样式封装 |

用户协议弹框
新建一个弹框组件页面
src/main/ets/view/welcome/UserPrivacyDialog.ets
1 |
|
然后在欢迎页使用
1 | // 首选项工具 |

首页Tab实现
1 | import {CommonConstants} from '../common/constants/CommonConstants' |

头部搜索框
1 | import {CommonConstants} from '../../common/constants/CommonConstants' |
日期和日期弹框
日期展示组件
1 | import {CommonConstants} from '../../common/constants/CommonConstants' |
日期弹框组件
1 | import {CommonConstants} from '../../common/constants/CommonConstants' |
用到的日期工具类代码
DateUtils.ts
1 | export default class DateUtils { |

统计信息卡片
使用轮播组件,将两个组件包裹起来
1 | import {CommonConstants} from '../../common/constants/CommonConstants' |
热量信息卡片
CalorieStats.ets
1 | import {CommonConstants} from '../../common/constants/CommonConstants' |

卡路里信息卡片
NutrientState.ets
1 | import {CommonConstants} from '../../common/constants/CommonConstants' |

实现记录列表
1 | import {CommonConstants} from '../../common/constants/CommonConstants' |
添加食物列表页面
新建页面ItemIndexPage
1 | import {CommonConstants} from '../common/constants/CommonConstants' |
tab列表组件代码
ItemTabList.ets
1 | import {CommonConstants} from '../../common/constants/CommonConstants' |
效果显示
底部Panel实现
ItemIndexPage.ets
页面增加 Panel 组件
1 | import {CommonConstants} from '../common/constants/CommonConstants' |
PanelHeader
弹框顶部日期
1 | import {CommonConstants} from '../../common/constants/CommonConstants' |
PanelFoodInfo
食物信息
1 | import {CommonConstants} from '../../common/constants/CommonConstants' |
PanelInput
键盘区域
1 | import {CommonConstants} from '../../common/constants/CommonConstants' |
实现数字键盘
这里使用到了Grid布局
键盘组件代码实现
1 | import {CommonConstants} from '../../common/constants/CommonConstants' |
父组件代码
1 | import {CommonConstants} from '../common/constants/CommonConstants' |
食物信息组件修改,根据传递进来的数量,自动计算对应的热量信息
1 | import {CommonConstants} from '../../common/constants/CommonConstants' |
多设备响应式开发
同一个页面,在手机、折叠手机、平板等设备上显示的方式是不一样的,我们可以通过官方提供的 @ohos.mediaquery
库来获取当前屏幕的宽度,然后根据不同宽度做不同处理
第一步:定义一个Bean,这个文件的作用是传入一个配置对象,然后调用 getValue 方法返回不同尺寸下对应的值
src/main/ets/common/bean/BreanpointType.ets
1 | declare interface BreakpointTypeOptions<T> { |
第二步:定义一个常量类,声明各种查询条件及配置对象
src/main/ets/common/constants/BreakpointConstants.ets
1 | import BreakpointType from '../bean/BreanpointType'; |
第三步:创建媒体查询工具类,创建不同尺寸的监听器,当命中时将结果保存到全局存储中
src/main/ets/common/utils/BreakpotionSystem.ets
1 | import mediaQuery from '@ohos.mediaquery'; |
第四步:页面使用
现在我们来修改首页代码,加入响应式功能,实现在不同设备上,Bar的位置显示到不同的地方
src/main/ets/pages/Index.ets
1 | import BreakpointConstants from '../common/constants/BreakpointConstants' |
还要修改卡片信息,在平板设备上就不要左右滑动显示了,而是直接显示两个卡片
src/main/ets/view/record/StatsCard.ets
1 | import BreakpointType from '../../common/bean/BreanpointType' |
最后来看预览效果
首先点击这里,将多设备预览功能按钮打开,这样可以同时看到页面在手机、折叠屏、平板三种设备的显示效果
下面是不同设备的显示结果
显示不同的记录项
核心处理代码
ItemModel.ets
1 | import GroupInfo from '../viewmodel/GroupInfo' |
tab列表页面获取数据后遍历显示不同的页签以及对应的list
ItemTabList.ets
1 | import {CommonConstants} from '../../common/constants/CommonConstants' |
然后点击每一个分类时将当前的分类信息传递给信息展示弹框中
PanelFoodInfo.ets
1 | import {CommonConstants} from '../../common/constants/CommonConstants' |
实现效果