五、Harmony OS 之 自定义组件
1.创建自定义组件
在ArkUI中,UI显示的内容均为组件,由框架直接提供的称为系统组件,由开发者定义的称为自定义组件。在进行UI界面开发时,通常不是简单的将系统组件进行组合使用,而是需要考虑代码可复用性、业务逻辑与UI分离,后续版本演进等因素。因此,将UI和部分业务逻辑封装成自定义组件是不可或缺的能力。
自定义组件具有以下特点:
可组合:允许开发者组合使用系统组件、及其属性和方法。
可重用:自定义组件可以被其他组件重用,并作为不同的实例在不同的父组件活容器中使用。
数据驱动UI更新:通过状态变量的改变,来驱动UI的刷新。
/** * author:创客未来 * copyright:com.ckFuture.hrb */ /** * 1:组件必须使用 @Component 装饰器 * 2: @Entry 装饰哪个组件、哪个就呈现在页面上 * 3: 被@Entry装饰的入口组件,build()中必须有且仅有一个**根容器**组件 * 其他自定义组件,build() */ @Entry @Component struct Demo2 { @State message: string = '诗词学习' build() { Row() { Column() { Text(this.message) .fontSize(50) .fontWeight(FontWeight.Bold) itemComponent() itemComponent({content:'我是传递字符串'}) } .width('100%') } .height('100%') } } @Component struct itemComponent{ // 自定义组件可以使用私有化变量(组件内部的变量都是私有化的,默认就是私有化) 传递参数 private content: string = '默认字符串' build(){ Row(){ Image($r('app.media.icon')) .width(20) .height(20) .margin(15) Text(this.content) } .backgroundColor(Color.Pink) .borderRadius(25) .margin({ top:15 }) } }
组件内部点击事件
/** * author:创客未来 * copyright:com.ckFuture.hrb */ import FaultLogger from '@ohos.faultLogger' /** * 1:组件必须使用 @Component 装饰器 * 2: @Entry 装饰哪个组件、哪个就呈现在页面上 * 3: 被@Entry装饰的入口组件,build()中必须有且仅有一个**根容器**组件 * 其他自定义组件,build() */ @Entry @Component struct Demo2 { @State message: string = '诗词学习' build() { Row() { Column() { Text(this.message) .fontSize(50) .fontWeight(FontWeight.Bold) itemComponent() itemComponent({content:'我是传递字符串'}) } .width('100%') } .height('100%') } } @Component struct itemComponent{ // 自定义组件可以使用私有化变量(组件内部的变量都是私有化的,默认就是私有化) 传递参数 private content: string = '默认字符串' // 哪种情况可以驱动UI更新,@State @State isDone: boolean = false build(){ Row(){ Image(this.isDone?$r('app.media.iconOk'):$r('app.media.icon')) .width(20) .height(20) .margin(15) Text(this.content) } .backgroundColor(Color.Pink) .borderRadius(25) .margin({ top:15 }) .onClick(()=>{ //点击这一行修改图片 this.isDone = !this.isDone }) } }
点击自定义组件,文字变化效果
@Component struct itemComponent{ // 自定义组件可以使用私有化变量(组件内部的变量都是私有化的,默认就是私有化) 传递参数 private content: string = '默认字符串' // 哪种情况可以驱动UI更新,@State @State isDone: boolean = false build(){ Row(){ Image(this.isDone?$r('app.media.icon'):$r('app.media.icon')) .width(20) .height(20) .margin(15) Text(this.content) .decoration({ type:this.isDone?TextDecorationType.LineThrough:TextDecorationType.None }) } .backgroundColor(Color.Pink) .borderRadius(25) .margin({ top:15 }) .onClick(()=>{ //点击这一行修改图片 this.isDone = !this.isDone }) } }
2.自定义组件构建函数
@Builder装饰器
ArkUI还提供了一种更轻量的UI元素复用机制@Builder,@Builder所装饰的函数遵循build()函数语法规则,开发者可以将重复使用的UI元素抽象成一个方法,在build方法里调用。
2.1 组件内部的构建函数创建与调用
/** * 自定义组件构建函数 @Builder */ @Entry @Component struct Demo3 { @State message: string = '@Builder' @State isDone:boolean = false /** * 构建函数 * @param content 参数 */ @Builder item(content:string){ Row(){ Image(this.isDone?$r('app.media.icon'):$r('app.media.icon')) .width(20) .height(20) .margin(15) Text(content) .decoration({ type:this.isDone?TextDecorationType.LineThrough:TextDecorationType.None }) } .backgroundColor(Color.Pink) .borderRadius(25) .margin({ top:15 }) .onClick(()=>{ //点击这一行修改图片 this.isDone = !this.isDone }) } build() { Row() { Column() { Text(this.message) .fontSize(50) .fontWeight(FontWeight.Bold) //调用组件内部的构建函数 this.item("轻舟已过万重山") } .width('100%') } .height('100%') } }
2.2 组件外部定义构建函数
组件外边定义构建函数好处是全局的,多个组件都可以引用。
/** * 自定义组件构建函数(组件外部定义) @Builder */ @Entry @Component struct Demo3 { @State message: string = '@Builder' @State isDone:boolean = false build() { Row() { Column() { Text(this.message) .fontSize(50) .fontWeight(FontWeight.Bold) //调用组件内部的构建函数 item("轻舟已过万重山") } .width('100%') } .height('100%') } } /** * 构建函数(外部) * 写在外面的自定义组件构建函数 方便多组件共同调用 * @param content 参数 */ @Builder function item(content:string){ Row(){ Image(this.isDone?$r('app.media.icon'):$r('app.media.icon')) .width(20) .height(20) .margin(15) Text(content) .decoration({ type:this.isDone?TextDecorationType.LineThrough:TextDecorationType.None }) } .backgroundColor(Color.Pink) .borderRadius(25) .margin({ top:15 }) .onClick(()=>{ //点击这一行修改图片 this.isDone = !this.isDone }) }
@Builder装饰器使用说明
参数传递规则说明
自定义构建函数的参数传递有按值传递和按引用传递两种,均需要遵守以下规则:
① 参数的类型必须与参数声明的类型一致,不允许undefind、null和返回 undefind、null的表达式。
② 在自定义构建函数内部,不允许改变参数值。如果需要改变参数值,且同步回调用点,建议使用@Link。
3.定义组件重用样式
@Styles装饰器
3.1 组件内部声明重用样式和调用
/** * 自定义组件重用样式 */ @Entry @Component struct Demo5 { @State message: string = '@Styles' /** * 定义重用样式 * 目的是将重复公用的属性封装 */ @Styles commonStyle(){ .width(200) .height(100) .backgroundColor(Color.Gray) } build() { Row() { Column({space:20}) { Text(this.message).fontSize(50).commonStyle() Button().commonStyle() Image('').commonStyle() Row(){}.commonStyle() } .width('100%') } .height('100%') } }
/** * 自定义组件重用样式 */ @Entry @Component struct Demo5 { @State message: string = '@Styles' /** * 定义重用样式 * 目的是将重复公用的属性封装 */ // @Styles commonStyle(){ // .width(200) // .height(100) // .backgroundColor(Color.Gray) // } build() { Row() { Column({space:20}) { Text(this.message).fontSize(50).commonStyle() Button().commonStyle() Image('').commonStyle() Row(){}.commonStyle() } .width('100%') } .height('100%') } } //外部通过样式函数 @Styles function commonStyle(){ .width(250) .height(50) .backgroundColor(Color.Gray) }
整体代码
/** * 自定义样式函数使用装饰器 @Styles * 内部函数优先级 > 外部函数 * 内部不需要function关键字 * 外部需要function关键字 * 弊端:只能写通用样式,不能传参。 */ @Entry @Component struct Demo5 { @State message: string = '@Styles' /** * 定义重用样式函数 不需要function * 目的是将重复公用的属性封装 */ @Styles commonStyle(){ .width(200) .height(100) .backgroundColor(Color.Gray) } build() { Row() { Column({space:20}) { Text(this.message).fontSize(50).commonStyle() Button().commonStyle() Image('').commonStyle() Row(){}.commonStyle() } .width('100%') } .height('100%') } } //外部通过样式函数 @Styles function commonStyle(){ .width(250) .height(50) .backgroundColor(Color.Gray) }
4.定义扩展组件样式
@Extend装饰器
语法
@Extend(UIComponentName) function functionName{...}
使用规则
① 和@Style不同,@Extend仅支持定义在全局,不支持在组件内部定义。
② 和@Style不同,@Extend支持封装指定的组件的私有属性和私有事件和预定义相同组件的@Extend的方法。
③ 和@Style不同,@Extend装饰的方法支持参数,开发者可以在调用时传递参数,调用遵循TS方法传值调用。
/** * 定义扩展组件样式 @Extend */ @Entry @Component struct Demo6 { @State message: string = '@Extend' @State count: number = 0 build() { Row() { Column() { Text(this.message) .fontSize(50) .fontWeight(FontWeight.Bold) Text('HarmonyOS4.0').textStyle(40,Color.Blue) Text('一件三联').textStyle(40,'red') Text('下次一定').textStyle(20,'#ababab') Text('干啥玩意').textStyle(50,Color.Pink) Button(this.count.toString()).btnStyle(()=>{ this.count++ }) } .width('100%') } .height('100%') } } /** * 定义 Text组件的扩展组件样式 */ @Extend(Text) function sizeColor(fs: number,fc: Color|string){ .fontSize(fs) .fontColor(fc) } @Extend(Text) function textStyle(fs: number,fc: Color|string){ .sizeColor(fs,fc) .fontWeight(FontWeight.Bold) } //定义 Button组件的扩展样式 //传递函数 click:()=>void 或者 click:Function @Extend(Button) function btnStyle(click:()=>void){ .fontSize(40) .width(150) .height(50) .onClick(()=>{ click() }) }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效