鸿蒙开发--语法和生命周期
组件是 OpenHarmony 页面最小显示单元,一个页面可由多个组件组合而成,也可只由一个组件组合而成,这些组件可以是 ArkUI 开发框架自带系统组件,比如 Text 、 Button 等,也可以是自定义组件,本节笔者简单介绍一下自定义组件的语法规范。
自定义组件
重点
状态
组件化
定义界面状态
按照声明式 UI 的开发范式,⾸先需要分析和定义⻚⾯的各种状态,并声明相应的状态变量⽤于表示不同的状态。
当前案例中,界⾯共有两个状态,分别是开灯
和关灯
状态,所以我们可以使⽤⼀个 boolean 类型的变量来表示这两个状态, true 表示开灯, false 表示关灯。如下:
@State isOn:boolean = false;
说明: @State
用于声明该变量为状态变量
改变状态
当 isOn 为 true 就是开灯
当 isOn 为 false 就是关灯
当状态数据改变的时候,界面就会自动更新
组件化
在鸿蒙开发中,组件是构成界⾯的最⼩单元,我们所看到的界⾯,都是由众多组件组合⽽成的,所以编写界⾯其实就是组合组件的过程,ArkTS 提供了很多的内置组件,例如: Text 、 Button 、 Image 等等;并且 ArkTS 还⽀持⾃定义组件,让开发者可根据具体需求⾃定义组件中的内容。
开关灯例子代码
- 在这个例子中 通过改变 isOn 状态来改变图片
@Entry
@Component
struct Light {
@State isOn: boolean = false;
build() {
Column({ space: 20 }) {
Row() {
/* 开关 */
if (!this.isOn) {
Image('pages/light/imgs/img_dark.png').width(300).height(300)
} else {
Image('pages/light/imgs/img_light.png').width(300).height(300)
}
}
Row({ space: 20 }) {
Button('开灯').onClick(() => {
this.isOn = true;
}).width(100)
Button('关灯').onClick(() => {
this.isOn = false;
}).width(100)
}
}.height('100%').width('100%').justifyContent(FlexAlign.Center)
}
}
声明组件的完整语法
组件参数
如果组件的定义包含参数,可在组件名称后⾯的 () 中配置相应参数。各组件⽀持的参数,可查看 API ⽂档,查看⽅式如下
- ⾸先将⿏标在相应组件悬停
点击 Show in API Reference ,就会弹出 API ⽂档
- 子组件
如果组件支持子组件配置,可在()
后的{}
添加子组件,若不支持子组件,则不需要{}
- 属性方法
属性方法用于配置组件的样式和其他属性,可以在组件声明的末尾进行链式调用。各组件支持的属性可以查看 API ⽂档
除去每个组件的专有属性,还有各组件都能配置的通用属性,通用属性也可以通过 api 文档查看
- 事件方法
事件方法用于为组件绑定交互事件,可以在组件声明的末尾进行链式调用.各组件的支持事件可以查看 API 文档
除去每隔组件的专有事件,还有各个组件都支持的通用事件,通用事件也可以通过 api 文档查看
自定义组件
除去系统预置的组件外,ArkTs 还支持自定义组件。使用自定义组件,可以使代码的机构更加清晰,并且提高代码的复用性
语法说明
各部分语法说明
- struct 关键字
struct
是ArkTs
新增的用于自定义组件或者自定义弹窗的关键字.其声明的数据结构和 Ts 的类十分相似。可包含属性和方法
- build 方法
build()
方法用于声明自定义组件的 UI 结构
- 组件属性
组件属性可用作自定义组件的参数,使得自定义组件更为通用
@Component
装饰器
@Component
装饰器用于装饰struct
关键字声明的数据结构。struct
被@Component
装饰后才具备组件化的能力
注意: 装饰器是 TS 一种特殊的语法,用于装饰类,方法,属性,用于修改或者扩展其原有的行为
INFO
在学完自定义组件的语法之后,我们会发现 前文案例中的每个页面实际上都是一个自定义组件
但是和自定义组件的语法相比,前边的案例还多出一个@Entry
装饰器,那@Entry
的作用是什么呢?
@Entry
在鸿蒙应用中,每个页面都是由一些列组件组合形成的。并且这些组件都逐层嵌套,因此最终形成了一个组件树
我们前边所编写的每个页面就相当于组件树的根节点,而@Entry
装饰器的作用就是标识该组件为组件树的根节点,也就是页面的入口组件
案例赏析
- 自定义组件
新建一个 CustomButton.ets 文件
INFO
图片路径必须是 pages 开头
自定义组件传递参数必须放在 build 的外面
传递参数通过对象来传递 调用的时候必须加上 this,但是他前面不用加@State
@Component
export struct SwitchButton {
color:Color = Color.Blue;
build() {
Button(){
Image("pages/light/imgs/icon_switch.png")
}.type(ButtonType.Circle).backgroundColor(this.color).height(45).width(45)
}
}
- 调用的时候
INFO
引入的时候必须写在第一行
调用的地址写相对路径
参数传递必须通过对象传递
import {SwitchButton} from './components/CustomButton'
@Entry
@Component
struct Light {
@State isOn: boolean = false;
build() {
Column({ space: 20 }) {
Row() {
/* 开关 */
if (!this.isOn) {
Image('pages/light/imgs/img_dark.png').width(300).height(300)
} else {
Image('pages/light/imgs/img_light.png').width(300).height(300)
}
}
Row({ space: 20 }) {
SwitchButton({color:Color.Red}).onClick(()=>{
this.isOn = false;
})
SwitchButton({color:Color.Green}).onClick(()=>{
this.isOn = true;
})
}
}.height('100%').width('100%').justifyContent(FlexAlign.Center)
}
}
组件生命周期
ArkUI 开发框架赋予了组件独有的生命周期方法,对于系统组件来讲,生命周期方法是 onAppear 和 onDisAppear ,笔者在第三章 第 2 节 讲过,这里不再叙述了,这里只讲述一下自定义组件的生命周期。
组件的生命周期
使用 @Component 修饰的组件,ArkUI 开发框架会自动为其赋予私有的生命周期方法 aboutToAppear() 和 aboutToDisappear() ,它们用于通知开发者该自定义组件的生命周的变更。
aboutToAppear:函数在创建自定义组件的新实例后,在执行其 build() 函数之前执行。允许在该函数中改变状态变量,更改将在后续执行 build() 函数中生效。
aboutToDisappear:函数在自定义组件析构消耗之前执行。不允许在该函数中改变状态变量,特别是 @Link 变量的修改可能会导致应用程序行为不稳定。
页面的生命周期
页面本质上也是一个组件,只是页面对于组件来讲多了一个修饰符 @Entry,该修饰符表示当前组件是一个页面,它需要在 config.json 中做配置,页面除了具有组件的生命周期外,它还有自己独有的生命周期方法:
onPageShow:页面显示时触发一次,包括路由过程、应用进入前后台等场景,仅 @Entry 修饰的自定义组件生效。
onPageHide:页面消失时触发一次,包括路由过程、应用进入前后台等场景,仅 @Entry 修饰的自定义组件生效。
onBackPress:当用户点击返回按钮时触发,仅 @Entry 修饰的自定义组件生效。该方法返回 boolean 类型的值,说明如下:
返回 true 表示页面自己处理返回逻辑, 不进行页面路由。
返回 false 表示使用默认的返回逻辑。
不返回值会作为 false 处理。
组件生命周期制作表格对比说明如下:
函数名 | 描述 |
---|---|
onAppear | 系统组件独有的方法,组件从组件树上挂载的回调。 |
onDisAppear | 系统组件独有的方法,组件从组件树上卸载的回调。 |
aboutToAppear | 函数在创建自定义组件的新实例后,在执行其 build() 函数之前执行。允许在该函数中改变状态变量,更改将在后续执行 build() 函数中生效。 |
aboutToDisappear | 函数在自定义组件析构消耗之前执行。不允许在该函数中改变状态变量,特别是 @Link 变量的修改可能会导致应用程序行为不稳定。 |
onPageShow | 页面显示时触发该回调,包括路由过程、应用进入前后台等场景。仅 @Entry 修饰的自定义组件生效。 |
onPageHide | 页面消失时触发该回调,包括路由过程、应用进入前后台等场景。仅 @Entry 修饰的自定义组件生效。 |
onBackPress | 当用户点击返回按钮时触发,该方法返回 boolean 类型,true:表示页面自己处理返回逻辑, 不进行页面路由。false:表示使用默认的返回逻辑。不返回值会作为 false 处理。仅 @Entry 修饰的自定义组件生效。 |
这些回调函数是私有的,在运行时由开发框架在特定的时间进行调用,不能从应用程序中手动调用这些回调函数。
允许在生命周期函数中使用 Promise 和异步回调函数,比如网络资源获取,定时器设置等;不允许在生命周期函数中使用 async await 。
再按一次,退出应用
我们在使用第三方 APP 的时候会遇见点击返回键提示再按一次退出应用的场景,比如在短时间内不按,就不会退出 APP 达到留住用户的目的,接下来我们实现这个再按一次退出应用的例子。
根据页面生命周期的方法可知,点击返回键的时候会调用 onBackPress() 方法,因此判断是否是第一次点击,如果是则返回 true 并给用户提示,如果不是则判断两次点击的时间间隔,若间间隔小于 2 秒,那么就直接退出 APP ,否则给用户提示,本样例复用第二章的测试代码,如下所示:
import app from '@system.app';
@Entry @Component struct Index {
private lastExitTime: number = -1; // 记录点击时间
@State count: number = 0; // 状态数据
build() {
Stack({alignContent: Alignment.BottomEnd}) { // 堆叠式布局
Text(this.count.toString()) // 显示文本
.fontSize(50) // 文字大小
.textAlign(TextAlign.Center) // 居中对齐
.size({width: '100%', height: '100%'}) // 控件大小
Button('+') // 显示一个+按钮
.size({width: 80, height: 80}) // 按钮大小
.fontSize(50) // 按钮文字大小
.onClick(() => { // 按钮点击事件
this.count++; // count累加,触发build()方法回调
})
.margin(50)
}
.width('100%')
.height('100%')
}
onBackPress() {
if (-1 == this.lastExitTime) { // 第一次点击返回键,提示toast
this.lastExitTime = new Date().getTime();
prompt.showToast({
message: "再按一次退出应用"
})
return true;
} else {
let currentTime = new Date().getTime();
if(currentTime - this.lastExitTime > 2000) { // 时间大于2000提示
prompt.showToast({
message: "再按一次退出应用"
})
this.lastExitTime = currentTime;
return true;
} else { // 2秒内点击,退出APP
app.terminate();
}
}
return false;
}
}
样例运行结果如下图所示:
以上就是自定义一个组件需要遵循的语法规范,自定义组件具有以下特点:
可组合:允许开发人员组合使用内置组件和其他组件,以及公共属性和方法。
可重用:可以被其他组件重用,并作为不同的实例在不同的父组件或容器中使用;
有生命周期:生命周期的回调方法可以在组件中配置,用于业务逻辑处理;
数据驱动更新:可以由状态数据驱动,实现 UI 自动更新