HarmonyOS自定义构建函数 原创

liuyang8888
发布于 2025-3-19 16:24
1106浏览
0收藏

1. 构建函数-@Builder

如果不想使用 @Component 直接抽象组件,ArkUI还提供了一种更轻量的UI元素复用机制 @Builder,可以将重复使用的UI元素抽象成一个方法,在 build 方法里调用。称之为自定义构建函数。

HarmonyOS自定义构建函数-鸿蒙开发者社区

用法- 可以使用 @Builder 修饰符进行修饰。

例如上面图片设置页面的每个操作项,可以单独的抽离出来,进行复用。

案例代码如下:

@Entry
@Component
struct Index {
  build() {
    Column(){
      Row(){
        Row(){
          Text('语言切换')
          Text('中文')
        }
        .width('100%')
        .height(40)
        .borderRadius(10)
        .justifyContent(FlexAlign.SpaceBetween)
        .padding({left: 15, right: 15})
        .backgroundColor(Color.White)
      }
      .width('100%')
      .height(40)
      .padding({left: 10, right: 10})
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .backgroundColor('#ccc')
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.

实现效果,如图所示。

HarmonyOS自定义构建函数-鸿蒙开发者社区

假设有N个这样的单个元素,但是重复的去写会浪费大量的代码,丧失代码的可读性,此时我们就可以使用builder构建函数。

1.1 全局定义- @Builder functionname () {}

使用@Builder抽取组件代码,如下所示

@Builder
function getItem(leftStr: string, rightStr: string) {
  Row() {
    Row() {
      Text(leftStr)
      Text(rightStr)
    }
    .width('100%')
    .height(40)
    .borderRadius(10)
    .justifyContent(FlexAlign.SpaceBetween)
    .padding({ left: 15, right: 15 })
    .backgroundColor(Color.White)

  }
  .width('100%')
  .height(40)
  .padding({ left: 10, right: 10 })
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

在组件中使用,完整代码如下:

@Builder
function getItem(leftStr: string, rightStr: string) {
  Row() {
    Row() {
      Text(leftStr)
      Text(rightStr)
    }
    .width('100%')
    .height(40)
    .borderRadius(10)
    .justifyContent(FlexAlign.SpaceBetween)
    .padding({ left: 15, right: 15 })
    .backgroundColor(Color.White)

  }
  .width('100%')
  .height(40)
  .padding({ left: 10, right: 10 })
}

@Entry
@Component
struct BuilderCase {
  build() {
    Column({ space: 10 }) {
      getItem('昵称', 'Mark')
      getItem('语言切换', '中文')
      getItem('位置设置', '深圳')
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .backgroundColor('#ccc')
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.

实现效果,如图所示。

HarmonyOS自定义构建函数-鸿蒙开发者社区

全局自定义函数的问题

· 全局的自定义构建函数可以被整个应用获取,不允许使用this和bind方法。

· 如果不涉及组件状态变化,建议使用全局的自定义构建方法。

· 补一句-如果数据是响应式的-此时该函数不会自动渲染-哪怕是全局自定义函数,不可被其他文件引用。

将数据声明为State响应式数据

interface IFormatData{
  nickname: string
  lang: string
  address: string
}

export class IFormatDataModel implements IFormatData {
  nickname: string = ''
  lang: string = ''
  address: string = ''

  constructor(model: IFormatData) {
    this.nickname = model.nickname
    this.lang = model.lang
    this.address = model.address
  }
}

  @State
  formatData: IFormatDataModel = new IFormatDataModel({nickname:'mark', lang: '中文', address: '深圳' })
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.


传递数据,绑定为对应字段,代码如下:

interface IFormatData{
  nickname: string
  lang: string
  address: string
}

export class IFormatDataModel implements IFormatData {
  nickname: string = ''
  lang: string = ''
  address: string = ''

  constructor(model: IFormatData) {
    this.nickname = model.nickname
    this.lang = model.lang
    this.address = model.address
  }
}

@Entry
@Component
struct BuilderStateCase {
  @State
  formatData: IFormatDataModel = new IFormatDataModel({nickname:'mark', lang: '中文', address: '深圳' })
  build() {
    Column({ space: 10 }) {
      getItem('昵称', this.formatData.nickname)
      getItem('语言切换', this.formatData.lang)
      getItem('位置设置', this.formatData.address)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .backgroundColor('#ccc')
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.

修改响应式数据,完整代码如下:

interface IFormatData{
  nickname: string
  lang: string
  address: string
}

export class IFormatDataModel implements IFormatData {
  nickname: string = ''
  lang: string = ''
  address: string = ''

  constructor(model: IFormatData) {
    this.nickname = model.nickname
    this.lang = model.lang
    this.address = model.address
  }
}

@Builder
function getItem(leftStr: string, rightStr: string) {
  Row() {
    Row() {
      Text(leftStr)
      Text(rightStr)
    }
    .width('100%')
    .height(40)
    .borderRadius(10)
    .justifyContent(FlexAlign.SpaceBetween)
    .padding({ left: 15, right: 15 })
    .backgroundColor(Color.White)

  }
  .width('100%')
  .height(40)
  .padding({ left: 10, right: 10 })
}


@Entry
@Component
struct BuilderStateCase {

  @State
  formatData: IFormatDataModel = new IFormatDataModel({nickname:'mark', lang: '中文', address: '广州' })

  build() {
    Column({ space: 10 }) {
      getItem('昵称', this.formatData.nickname)
      getItem('语言切换', this.formatData.lang)
      getItem('位置设置', this.formatData.address)

      Text(JSON.stringify(this.formatData))
      Button('修改数据-语言切换')
        .onClick(()=>{
          this.formatData.lang =  this.formatData.lang ==  '英文' ? '中文' : '英文'
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .backgroundColor('#ccc')
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.

实现效果,如图所示。

HarmonyOS自定义构建函数-鸿蒙开发者社区

我们发现,点击修改数据-语言切换按钮是没有任何反应的,说明此时即使用了State,但是此时的全局builder依然不更新。

那怎么办?我们试试在组件内部定义。

1.2 组件内定义- 语法 @Builder name () {}

把@Builder标注的代码部分,放置到组件内部,完整代码如下:

interface IFormatData{
  nickname: string
  lang: string
  address: string
}

export class IFormatDataModel implements IFormatData {
  nickname: string = ''
  lang: string = ''
  address: string = ''

  constructor(model: IFormatData) {
    this.nickname = model.nickname
    this.lang = model.lang
    this.address = model.address
  }
}

@Entry
@Component
struct BuilderCase_2 {
  @State
  formatData: IFormatDataModel = new IFormatDataModel({ nickname: 'mark', lang: '中文', address: '广州' })

  @Builder
  getItem(leftStr: string, rightStr: string) {
    Row() {
      Row() {
        Text(leftStr)
        Text(rightStr)
      }
      .width('100%')
      .height(40)
      .borderRadius(10)
      .justifyContent(FlexAlign.SpaceBetween)
      .padding({ left: 15, right: 15 })
      .backgroundColor(Color.White)

    }
    .width('100%')
    .height(40)
    .padding({ left: 10, right: 10 })
  }

  build() {
    Column({ space: 10 }) {
      this.getItem('昵称', this.formatData.nickname)
      this.getItem('语言切换', this.formatData.lang)
      this.getItem('位置设置', this.formatData.address)


      Text(JSON.stringify(this.formatData))
      Button('修改数据-语言切换')
        .onClick(() => {
          this.formatData.lang = this.formatData.lang == '英文' ? '中文' : '英文'
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .backgroundColor('#ccc')
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.


实现效果,如图所示:

HarmonyOS自定义构建函数-鸿蒙开发者社区

调用多了this,其他和全局属性一样,没有任何变化,此时我们发现修改数据依然没有任何变化,这是为什么呢?

注意:我们刚刚传过去的是什么类型,string是一个基础数据类型,它是按值传递的,不具备响应式更新的特点。

总结

全局Builder函数和组件Builder构建函数可以实现一种轻量级的UI复用。

区别: 全局自定义构建函数不允许使用this,bind,它适合一种纯渲染的UI结构。

组件内自定义Builder可以实现this调用。

2. 构建函数-传参传递

自定义构建函数的参数传递有​按值传递​和​按引用传递​两种,均需遵守以下规则:

参数的类型必须与参数声明的类型一致,不允许undefined、null和返回undefined、null的表达式。

在自定义构建函数内部,不允许改变参数值。如果需要改变参数值,且同步回调用点,建议使用​@Link​。

@Builder内UI语法遵循​UI语法规则​。

我们发现上一个案例,使用了string这种基础数据类型,即使它属于用State修饰的变量,也不会引起UI的变化。

按引用传递参数时,传递的参数可为状态变量,且状态变量的改变会引起@Builder方法内的UI刷新。ArkUI提供$$作为按引用传递参数的范式。

格式如下:

SomeBuilder( $$ : 类型);
  • 1.

也就是我们需要在builder中传入一个对象,该对象使用$$(可使用其他字符)的符号来修饰,此时数据具备响应式了。

定义IOptions接口,在@Builder中传入IOptions接口。

interface IOptions {
  leftStr: string
  rightStr: string
}

@Builder
  getItem($$: IOptions) {
    Row() {
      Row() {
        Text($$.leftStr)
        Text($$.rightStr)
      }
      .width('100%')
      .height(40)
      .borderRadius(10)
      .justifyContent(FlexAlign.SpaceBetween)
      .padding({ left: 15, right: 15 })
      .backgroundColor(Color.White)

    }
    .width('100%')
    .height(40)
    .padding({ left: 10, right: 10 })
  }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.

传递参数值,代码如下:

this.getItem({ leftStr: '昵称', rightStr:this.formatData.nickname })
this.getItem({ leftStr: '语言切换', rightStr:this.formatData.lang })
this.getItem({ leftStr: '位置设置', rightStr:this.formatData.address })
  • 1.
  • 2.
  • 3.

修改后的完整代码如下;

interface IFormatData{
  nickname: string
  lang: string
  address: string
}

export class IFormatDataModel implements IFormatData {
  nickname: string = ''
  lang: string = ''
  address: string = ''

  constructor(model: IFormatData) {
    this.nickname = model.nickname
    this.lang = model.lang
    this.address = model.address
  }
}
interface IOptions {
  leftStr: string
  rightStr: string
}
@Entry
@Component
struct ParamCase{
  @State
  formatData: IFormatDataModel = new IFormatDataModel({ nickname: 'mark', lang: '中文', address: '深圳' })

  @Builder
  getItem($$: IOptions) {
    Row() {
      Row() {
        Text($$.leftStr)
        Text($$.rightStr)
      }
      .width('100%')
      .height(40)
      .borderRadius(10)
      .justifyContent(FlexAlign.SpaceBetween)
      .padding({ left: 15, right: 15 })
      .backgroundColor(Color.White)

    }
    .width('100%')
    .height(40)
    .padding({ left: 10, right: 10 })
  }

  build() {
    Column({ space: 10 }) {
      this.getItem({ leftStr: '昵称', rightStr: this.formatData.nickname })
      this.getItem({ leftStr: '语言切换', rightStr: this.formatData.lang })
      this.getItem({ leftStr: '位置设置', rightStr: this.formatData.address })

Text(JSON.stringify(this.formatData))
      Button('修改数据-语言切换')
        .onClick(() => {
          this.formatData.lang = this.formatData.lang == '英文' ? '中文' : '英文'
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .backgroundColor('#ccc')
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.


实现效果,如图所示。

HarmonyOS自定义构建函数-鸿蒙开发者社区

这里,点击“修改数据-语言切换”按钮,语言切换的数据发生改变。

同样的,全局 Builder 也支持这种用法,完整代码如下。

interface IFormatData {
  nickname: string
  lang: string
  address: string
}

export class IFormatDataModel implements IFormatData {
  nickname: string = ''
  lang: string = ''
  address: string = ''

  constructor(model: IFormatData) {
    this.nickname = model.nickname
    this.lang = model.lang
    this.address = model.address
  }
}

interface IOptions {
  leftStr: string
  rightStr: string
}

@Builder
function getItem(options: IOptions) {
  Row() {
    Row() {
      Text(options.leftStr)
      Text(options.rightStr)
    }
    .width('100%')
    .height(40)
    .borderRadius(10)
    .justifyContent(FlexAlign.SpaceBetween)
    .padding({ left: 15, right: 15 })
    .backgroundColor(Color.White)

  }
  .width('100%')
  .height(40)
  .padding({ left: 10, right: 10 })
}

@Entry
@Component
struct ParamCase_2 {
  @State
  formatData: IFormatDataModel = new IFormatDataModel({ nickname: 'mark', lang: '中文', address: '深圳' })
  build() {
    Column({ space: 10 }) {
      getItem({ leftStr: '昵称', rightStr: this.formatData.nickname })
      getItem({ leftStr: '语言切换', rightStr: this.formatData.lang })
      getItem({ leftStr: '位置设置', rightStr: this.formatData.address })
      Text(JSON.stringify(this.formatData))
      Button('修改数据-语言切换')
        .onClick(() => {
          this.formatData.lang = this.formatData.lang == '英文' ? '中文' : '英文'
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .backgroundColor('#ccc')
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.

使用 @Builder 复用逻辑的时候,支持传参可以更灵活的渲染UI。

参数可以使用状态数据,不过建议通过对象的方式传入 @Builder。

3. 构建函数-​@BuilderParam ​传递UI

Component可以抽提组件。

Builder可以实现轻量级的UI复用。

完善了吗?其实还不算,比如下面这个例子,如图所示。

HarmonyOS自定义构建函数-鸿蒙开发者社区

大家发现没有,项目中会有很多地方用到这种类似卡片 Card 的地方,里面的内容各有不同,怎么办?

在 Vue 里面有个叫做 slot 插槽的东西,就是可以传入自定义的结构,整体复用父组件的外观。

ArkTS提供了一个叫做 BuilderParam 的修饰符,可以在组件中定义这样一个函数属性,在使用组件时直接传入。

BuilderParam只能应用在 Component 组件中,不能使用 Entry 修饰的组件中使用。

语法:

@BuilderParam name: () => void
  • 1.

声明一个HmCard组件

@Component
struct HmCard {
  @BuilderParam
  content?: () => void
 
  build() {
    Column() {
      Text("卡片组件")
      Divider()
      Row() {
        Text("传入内容:")
        if (this.content) {
          this.content()
        }
      }
 
    }
    .width('100%')
    .height(400)
    .border({ width: 1 })
    .borderRadius(10)
    .backgroundColor('#ccc')
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.

父组件调用传入

@Entry
@Component
struct BuilderParamCase{
  @Builder
  getContent() {
    Row() {
      Text("插槽内容")
        .fontColor(Color.Red)
    }
  }
 
  build() {
    Column() {
      HmCard({ content: this.getContent })
    }
    .justifyContent(FlexAlign.Center)
    .width('100%')
    .height('100%')
    .padding(10)
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

完整代码如下:

@Component
struct HmCard {
  @BuilderParam
  content?: () => void
  build() {
    Column() {
      Text("卡片组件")
      Divider()
      Row() {
        Text("传入内容:")
        if (this.content) {
          this.content()
        }
      }
    }
    .width('100%')
    .height(400)
    .border({ width: 1 })
    .borderRadius(10)
    .backgroundColor('#ccc')
  }
}

@Entry
@Component
struct BuilderParamCase{
  @Builder
  getContent() {
    Row() {
      Text("插槽内容")
        .fontColor(Color.Red)
    }
  }

  build() {
    Column() {
      HmCard({ content: this.getContent })
    }
    .justifyContent(FlexAlign.Center)
    .width('100%')
    .height('100%')
    .padding(10)
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.

运行效果,如图所示。

HarmonyOS自定义构建函数-鸿蒙开发者社区

需要注意的是,传入的函数必须是使用 Builder 修饰符修饰的。

BuilderParams类似于 Vue 中的插槽。

(1)子组件中定义一个用 BuilderParam 修饰的函数。

(2)父组件需要给子组件传入一个用 Builder 修饰的函数来赋值给子组件。

(3)子组件要在想要显示插槽的地方来调用传入的方法。

插槽默认值

当我们的调用组件的时候,如果没有给插槽传递参数值,则这个时候我们可以给 BuilderParam 函数默认值,代码如下。

@Component
struct HmCard2{
  @Builder
  contentDefault(){
    Text('我是插槽的默认值')
      .fontColor(Color.Green)
  }
  @BuilderParam
  content: () => void = this.contentDefault
  build() {
    Column() {
      Text("卡片组件")
      Divider()
      Row() {
        Text("传入内容:")
        if (this.content) {
          this.content()
        }
      }

    }
    .width('100%')
    .height(400)
    .border({ width: 1 })
    .borderRadius(10)
    .backgroundColor('#ccc')
  }
}


@Entry
@Component
struct BuilderParamCase_2 {
  @Builder
  getContent() {
    Row() {
      Text("插槽内容")
        .fontColor(Color.Red)
    }
  }

  build() {
    Column() {
      HmCard2()
    }
    .justifyContent(FlexAlign.Center)
    .width('100%')
    .height('100%')
    .padding(10)
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.

实现效果,如图所示:

HarmonyOS自定义构建函数-鸿蒙开发者社区

尾随闭包

当我们的组件只有一个BuilderParam 的时候,此时可以使用尾随闭包的语法也就是像我们原来使用Column或者Row组件时一样,直接在大括号中传入,代码格式如下:

HmCard3() {
   Text("插槽内容-尾随闭包的写法")
     .fontColor(Color.Red)
   this.getContent()
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

如果有多个呢,不好意思,必须在组件的函数中老老实实的传入多个builder自定义函数。

完整代码如下:

@Component
struct  HmCard3 {
  @BuilderParam
  content?: () => void
  @BuilderParam
  header?: () => void
  build() {
    Column () {
      Text("卡片组件")
      if(this.header) {
        this.header()
      }
      Divider()
      Text("传入内容")
      if(this.content) {
        this.content()
      }
    }
  }
}

@Entry
@Component
struct BuilderParamCase_3 {

  @Builder
  getContent () {
    Row() {
      Text("插槽内容")
        .fontColor(Color.Red)
    }
  }
  @Builder
  getHeader () {
    Row() {
      Text("头部内容")
        .fontColor(Color.Red)
    }
  }
  build() {
    Row() {
      Column() {
        HmCard3({
          header: () => {
            this.getHeader()
          },
          content: () => {
            this.getContent()
          }
        })
      }
      .width('100%')
    }
    .height('100%')
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.

实现效果,如图所示。

HarmonyOS自定义构建函数-鸿蒙开发者社区

4. 案例

封装 HmCard 和 HmCardItem 组件,使用 BuilderParam 属性完成如下效果图。

HarmonyOS自定义构建函数-鸿蒙开发者社区

完整代码如下:

@Entry
@Component
struct BuilderParamCardCase {

  build() {
    Column() {
      HmCard4() {
        HmCardItem({ leftStr: '员工姓名', rightStr: '周星星' })
        HmCardItem({ leftStr: '员工编号', rightStr: '9527' })
        HmCardItem({ leftStr: '员工权限', rightStr: '普通' })
        HmCardItem({ leftStr: '员工组织', rightStr: '研发部' })
      }
    }
    .height('100%')
    .backgroundColor("#ccc")
  }
}

@Component
struct HmCard4 {
  @BuilderParam
  CardContent?: () => void

  build() {
    Column() {
      Column() {
        if (this.CardContent) {
          this.CardContent()
        }
      }.borderRadius(8)
      .backgroundColor(Color.White)

    }.padding({
      left: 15,
      right: 15
    })
    .margin({
      top: 10
    })

  }
}

@Component
struct HmCardItem {
  leftStr: string = ''
  rightStr: string = ''

  build() {
    Row() {
      Text(this.leftStr)
      Text(this.rightStr).fontColor("#ccc")
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceBetween)
    .padding({
      left: 10,
      right: 10
    })
    .height(50)
    .border({
      width: {
        bottom: 1
      },
      color: '#f4f5f6'
    })
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.

 

 

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
收藏
回复
举报


回复
    相关推荐