- 作者:老汪软件技巧
- 发表时间:2024-08-29 10:02
- 浏览量:
在ArkUI中,UI显示的内容均为组件,由框架直接提供的称为系统组件,由开发者定义的称为自定义组件。
相比于轻量级 UI 复用机制 @Builder,自定义组件的功能更为强大,日常开发中如果要进行 UI 或业务逻辑进行复用,需要掌握自定义组件的能力。
1. 基本使用
语法:
// 定义
@Component
struct MyComponent {
// 状态变量
@State message:string =''
build(){
// .... 描述 UI
}
}
//----------使用-----------
// 1.不传递参数使用
// MyComponent()
// 2.传递参数使用:通过传递参数的方式 设置子组件中 messsage 的值
// MyComponent({message:'xxx'})
2. 成员函数/变量
自定义组件除了必须要实现build()函数外,还可以定义其他的成员函数,以及成员变量
// HelloComponent.ets
@Component
export struct HelloComponent {
// 成员变量
info: string = '感觉自己闷闷哒'
// 成员变量也可以是函数
sayHello=()=>{}
// 状态变量
@State message: string = 'Hello, World!';
// 成员函数
sayHi() {
console.log('你好呀')
}
build() {
// HelloComponent自定义组件组合系统组件Row和Text
Column() {
Text(this.message)
Text(this.info)
Button('修改数据')
.onClick(() => {
this.info = '(*  ̄3)(ε ̄ *)'
this.message = 'Hello,ArkTS'
this.sayHi()
this.sayHello()
})
}
}
}
// 页面的.ets
import { HelloComponent } from './components/HelloComponent'
@Entry
@Component
struct CustomComponentDemo {
build() {
Column() {
// 使用组件内部定义的初始值
HelloComponent()
// 使用传入的值,覆盖子组件的默认值
HelloComponent({ info: '你好', message: 'ArkTS' })
// 函数也可以传入
HelloComponent({ sayHello(){ console.log('传入的逻辑') } })
}
}
}
2.3. 通用样式事件
自定义组件可以通过点语法的形式设置通用样式,通用事件
@Component
struct MyComponent2 {
build() {
Button(`Hello World`)
}
}
@Entry
@Component
struct MyComponent {
build() {
Row() {
MyComponent2()
.width(200)
.height(300)
.backgroundColor(Color.Red)
.onClick(() => {
console.log('外部添加的点击事件')
})
}
}
}
二、 构建函数-@BuilderParam 传递 UI
就是自定义组件允许外部传递 UI
1. 单个@BuilderParam参数
@Component
struct MyButtom {
// 第一步 定义自定义构建函数
@Builder myBuilder(){
Button('默认按钮')
}
//第二步 定义 BuilderParam 接受外部传入的 ui,并设置默认值
@BuilderParam my:()=>void = this.myBuilder
build() {
Column({space:20}) {
Text('================')
//第三步使用 @BuilderParam 装饰的成员变量
this.my()
Text('================')
}
.width(200)
.height(200)
.padding({top:30})
.border({width:10})
.borderRadius(100)
}
}
@Entry
@Component
struct Index {
build() {
Column({ space: 10 }) {
MyButtom()
MyButtom(){
Button('外部组件')
}
MyButtom(){
Image($r('app.media.avatar'))
.width(50)
}
}
.width('100%')
.height('100%')
}
}
2. 多个@BuilderParam 参数
子组件有多个BuilderParam,必须通过参数的方式来传入
@Component
struct MyButtom {
// 第一步 定义自定义构建函数
@Builder
myBuilder() {
Text('<')
}
//第二步 定义 BuilderParam 接受外部传入的 ui,并设置默认值
@BuilderParam leftBui: () => void = this.myBuilder
@BuilderParam centerBui: () => void = this.myBuilder
@BuilderParam rightBui: () => void = this.myBuilder
build() {
Row() {
//第三步使用 @BuilderParam 装饰的成员变量
this.leftBui()
this.centerBui()
this.rightBui()
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.backgroundColor(Color.White)
}
}
@Entry
@Component
struct Index {
@Builder leftBuilder(){
Text('返回')
.fontSize(30)
}
@Builder centerBuilder(){
Text('首页')
.fontSize(30)
}
@Builder rightBuilder(){
Text('菜单')
.fontSize(30)
}
build() {
Column({ space: 10 }) {
//没有传参
MyButtom()
//第四步 通过参数的形式传入多个 Builder
MyButtom({
leftBui:this.leftBuilder,
centerBui:this.centerBuilder,
rightBui:this.rightBuilder
})
}
.backgroundColor('#ccc')
.justifyContent(FlexAlign.Center)
.width('100%')
.height('100%')
}
}
三、 状态共享@Prop -父子单向
@Prop 装饰的变量可以和父组件建立单向的同步关系。@Prop 装饰的变量是可变的,但是变化不会同步回其父组件。
1. 简单类型
简单类型 string、number、boolean、enum
注意:
修改父组件数据,会同步更新子组件修改子组件@Prop 修饰的数据,子组件 UI 更新,但会被父组件状态更新覆盖通过回调函数的方式修改父组件的数据,然后触发@Prop数据的更新
@Component
struct MyButtom {
@Prop
num:number = 0
build() {
Button(`子组件 ${this.num}`)
.backgroundColor(Color.Black)
}
}
@Entry
@Component
struct Index {
@State num:number = 100
build() {
Column({space:10}) {
Button(`父组件 ${this.num}`)
.onClick(()=>{
this.num++
})
MyButtom({ num: this.num })
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
2. 复杂类型
复杂类型的做法类似,通过回调函数将需要修改的数据传递给父组件即可
@Component
struct MyButtom {
@Prop
num: number = 0
addNum: (n: number) => void = () => {} //接收父组件的函数
build() {
Button(`子组件加加 ${this.num}`)
.backgroundColor(Color.Black)
.onClick(() => {
this.addNum(5) //点击时使父组件的num+5,然后带动子组件的num增加
})
}
}
@Entry
@Component
struct Index {
@State num: number = 100
addNum=(n:number)=>{
this.num+=n
}
build() {
Column({ space: 10 }) {
Button(`父组件减减 ${this.num}`)
.onClick(() => {
this.num--
})
MyButtom({ num: this.num, addNum: this.addNum }) //给子组件传一个带参数的箭头函数this.addNum
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
小案例
需求:
小图片用自定义组件写,大图片在父组件身上点击小图片,父组件的图片跟着变化
图片如下:
第一种: 子组件向父组件传参写法
@Component
struct MyButtom {
imgUrl:string[]= ['111','2','3','4']
Url:(n:string)=>void=()=>{}
build() {
Row() {
ForEach(this.imgUrl,(item:string,index:number)=>{
Image($r(`app.media.${item}`))
.width(80)
.onClick(()=>{
this.Url(this.imgUrl[index])
})
})
}
}
}
@Entry
@Component
struct Index {
@State img:string = '111'
igUrl=(n:string)=>{
this.img = n
}
build() {
Column({ space: 10 }) {
MyButtom({Url:this.igUrl})
Image($r(`app.media.${this.img}`))
.width('100%')
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
第二种: 自定义组件的事件写法
@Component
struct MyImg {
// 接收图片地址
imgSrc: string = "app.media.111"
build() {
Image($r(this.imgSrc))
.width(50)
.aspectRatio(1)
.borderRadius(25)
}
}
@Entry
@Component
struct Index {
// 小图地址数组
@State
imgList: string[] = ["app.media.111", "app.media.2", "app.media.3", "app.media.4"]
// 大图路径
@State
bigSrc: string = "app.media.111"
build() {
Column() {
// 大图
Image($r(this.bigSrc))
.width(200)
// 小图
Row() {
ForEach(this.imgList, (item: string) => {
MyImg({ imgSrc: item })
.width(40)
.height(40)
.onClick(() => {
this.bigSrc = item
})
})
}
}
.width("100%")
.height("100%")
.justifyContent(FlexAlign.Center)
}
}