自定义组件的介绍
在开发和使用自定义组件直接,我们需要了解什么是自定义组件?
在ArkUI中,UI显示的内容均为组件,由框架直接提供的称为系统组件,由开发者定义的称为自定义组件。在进行 UI 界面开发时,通常不是简单的将系统组件进行组合使用,而是需要考虑代码可复用性、业务逻辑与UI分离,后续版本演进等因素。因此,将UI和部分业务逻辑封装成自定义组件是不可或缺的能力。
自定义组件具有以下特点:
-
可组合:允许开发者组合使用系统组件、及其属性和方法。
-
可重用:自定义组件可以被其他组件重用,并作为不同的实例在不同的父组件或容器中使用。
-
数据驱动UI更新:通过状态变量的改变,来驱动UI的刷新。
我们可以这么理解:自定义组件就是通过各种基础组件的组合,封装为可重用,可组合的UI单元。
自定义组件的基本用法
场景描述:
在日常开发中,我们拿到设计稿后,通过对页面内容进行合理抽象,提取出结构相同以及功能明确的UI单元,组合为自定义组件。
案例:
下面我们以一个实例来说明自定义组件的基本用法:
比如我们需要完成一个待办事项列表的页面开发,除了标题栏,选项卡都是内容重复的,那么我们就可以把这个卡片抽象出一个自定义组件
具体代码如下:
@Preview
@Component
export struct ToDoItem {
private content?: string;
//定义状态变量
@State isComplete: boolean = false
build() {
Row() {
//建立状态与视图间的关系
if (this.isComplete) {
Image($r('app.media.check_select')).width(30)
} else {
Image($r('app.media.check_normal')).width(30)
}
Text(this.content??'自定义组件').fontColor(Color.Black)
.opacity(this.isComplete ? 0.4 : 1)
}
.width('100%')
.height(40)
.padding({ left: 16 })
.margin({ top: 10 })
.borderRadius(20)
.backgroundColor(Color.White)
.onClick(() => {
//通过点击事件改变状态,达到交互效果
this.isComplete = !this.isComplete
})
}
}
自定义变量isComplete来控制选项卡的状态,在build函数里通过if/else条件渲染语句来描述状态发生后变化的UI,在用户点击onClick后改变状态,ArkUI会自动帮我们渲染出新的UI界面。
具体使用:
import { ToDoItem } from './widget/ToDoItem'
@Preview
@Entry
@Component
struct TodoListPage {
totalTasks: Array<string> = [];
aboutToAppear() {
this.totalTasks = [
'早起晨读',
'准备早餐',
'阅读名著',
'学习ArkTs',
'看个电影',
]
}
build() {
Column() {
Text('代办列表')
.fontSize(24)
.fontWeight(FontWeight.Bold)
ForEach(this.totalTasks, (task: string) => {
ToDoItem({ content: task })
})
}.backgroundColor(Color.Gray)
.height('100%')
}
}
这样一个有自定义组件的页面就完成了。
注意:我们通过forEach循环把自定义组件添加到页面上,但是forEach必须在容器组件中,不能作为根节点使用。
公共组件的封装
上面的案例是多组件组合为一个自定义组件只针对单个页面,接下来我们进行公共组件的封装
场景描述
我们在应用开发过程中,不同的业务场景,有可能需要使用相同功能和样式的ArkUI组件。例如,登录页面登录按钮, 购物页面结算按钮可能样式相同。该场景常用方法是抽取相同样式的逻辑部分,并将其封装成一个自定义组件到公共组件库中。在业务场景开发时,统一从公共组件库获取封装好的公用组件。
示例
以最常用的BUtton组件为例,当多个业务场景需要使用相同风格样式的Button组件时,我们可以把通用的逻辑封装成一个自定义组件,在通用多级中定制公共的属性,然后将自定义Button以拓展组件的形式集成到公共组件库中,提供给其他team使用,如果要做到尽善尽美,可能需要穷举所有Button的属性。当然可以自行调整。自定义组件的代码如下
@Component
struct CommonButton {
@Prop text: string = '';
@Prop stateEffect: boolean = true;
// ...穷举所有Button独有属性
build() {
Button(this.text)
.fontSize(12)
.fontColor('#FFFFFF')
.stateEffect(this.stateEffect)// stateEffect属性的作用是控制默认点击动画
.xxx //穷举Button其他独有属性赋值
}
}
在使用自定义Button 组件时,若需修改组件显示内容text和点击动画效果stateEffect时(其他Button独有的属性用法相同),需要以参数的形式传入
@Component
struct Index {
build() {
MyButton({ text: '点击带有动效', stateEffect: true, ... }) // 入参包含MyButton 组件中定义的全部 Button独有属性
}
}
但是这样做也有不少缺点,没有使用系统组件那么方便,因为系统的Button组件是链式调用的方法设置,而自定义后只能通过可选参数形式传入,也不利于后期维护,不易拓展,假如升级到新版本,不分组件的属性发生变更(Harmony Next后可能会有重大变化),自定义组件和使用的地方都需要改动,需要慎用。
组件与页面的生命周期:
有过移动开发经验的同学都清楚,自定义组件需要了解组件和页面声明周期的关系,才有助于我们更好的开发自定义组件,我们先看看ArkUI框架下页面的生命周期:
页面生命周期,即被@Entry装饰的组件生命周期,提供以下生命周期接口:
-
onPageShow:页面每次显示时触发一次,包括路由过程、应用进入前台等场景。
-
onPageHide:页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景。
-
onBackPress:当用户点击返回按钮时触发。
组件生命周期,即一般用@Component装饰的自定义组件的生命周期,提供以下生命周期接口:
-
aboutToAppear:组件即将出现时回调该接口,具体时机为在创建自定义组件的新实例后,在执行其build()函数之前执行。
-
onDidBuild:组件build()函数执行完成之后回调该接口,不建议在onDidBuild函数中更改状态变量、使用animateTo等功能,这可能会导致不稳定的UI表现。
-
aboutToDisappear:aboutToDisappear函数在自定义组件析构销毁之前执行。不允许在aboutToDisappear函数中改变状态变量,特别是@Link变量的修改可能会导致应用程序行为不稳定。
总结
目前自定义组件的用法就是以上几种方式,待HarmonyOS Next发布后,如果有更新我会在此基础上进行迭代,如果有兴趣的同学还请持续关注我或者订阅我的专栏。