鸿蒙5:自定义构建函数

目录

1. 自定义构建函数

1.1 构建函数-@Builder

1.2 构建函数-传参传递 (值传递和引用传递)

1.3 构建函数-传递参数练习-Tabs组件使用

1.4 构建函数-@BuilderParam 传递UI

1.5 @BuilderParam传值

1.6 尾随闭包


1. 自定义构建函数

1.1 构建函数-@Builder

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

只要使用Builder修饰符修饰的内容,都可以做成对应的UI描述

@Entry
  @ComponentV2
  struct BuilderCase {
    @Local
    list: string[] = ["A", "B","C", "D", "E", "F"]

    @Builder
    getItemBuilder (itemName: string) {
      Row() {
        Text(`${itemName}. 选项`)
      }
      .height(60)
        .backgroundColor("#ffe0dede")
        .borderRadius(8)
        .width("100%")
        .padding({
          left: 20,
          right: 20
        })
    }

    build() {
      Column({ space: 10 }) {
        ForEach(this.list, (item: string) => {
          this.getItemBuilder(item)
        })
      }
      .padding(20)
    }
  }

用法- 使用@Builder修饰符修饰

@Entry
  @ComponentV2
  struct BuilderCase02 {
    build() {
      Column() {
        Row() {
          Row() {
            Text("异常时间")
            Text("2023-12-12")
          }
          .width('100%')
            .justifyContent(FlexAlign.SpaceBetween)
            .padding({
              left: 15,
              right: 15
            })
            .borderRadius(8)
            .height(40)
            .backgroundColor(Color.White)
        }.padding({
          left: 10,
          right: 10
        })

      }
      .width('100%')
        .height('100%')
        .backgroundColor('#ccc')
        .padding({ top: 30 })
    }
  }

  • 全局定义- @Builder function name () {}
@Entry
  @ComponentV2
  struct BuilderCase02 {
    build() {
      Column() {
        Column({ space: 10 }) {
          getCellContent("异常时间", "2023-12-12")
          getCellContent("异常位置", "回龙观")
          getCellContent("异常类型", "漏油")
        }
      }
      .width('100%')
        .height('100%')
        .backgroundColor('#ccc')
        .padding({ top: 30 })
    }
  }

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

  }

1.2 构建函数-传参传递 (值传递和引用传递)

调用@Builder装饰的函数默认按值传递。当传递的参数为状态变量时,状态变量的改变不会引起@Builder方法内的UI刷新。所以当使用状态变量的时候,推荐使用按引用传递

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

@Builder
  export function getCellContent(leftTitle: string, rightValue: string) {
    Row() {
      Row() {
        Text(leftTitle)
        Text(rightValue)
      }
      .width('100%')
        .justifyContent(FlexAlign.SpaceBetween)
        .padding({
          left: 15,
          right: 15
        })
        .borderRadius(8)
        .height(40)
        .backgroundColor(Color.White)
    }.padding({
      left: 10,
      right: 10
    })
  }

interface ParamObj {
  leftTitle: string
  rightValue: string
}

@Builder
  export function getCellContentObj(obj: ParamObj) {
    Row() {
      Row() {
        Text(obj.leftTitle)
        Text(obj.rightValue)
      }
      .width('100%')
        .justifyContent(FlexAlign.SpaceBetween)
        .padding({
          left: 15,
          right: 15
        })
        .borderRadius(8)
        .height(40)
        .backgroundColor(Color.White)
    }.padding({
      left: 10,
      right: 10
    })
  }

@Entry
  @ComponentV2
  struct BuilderCase03 {
    @Local area: string = '望京'
    build() {
      Column({ space: 20 }) {
        getCellContent("异常时间", "2023-12-12")
        getCellContent("异常位置", this.area)
        getCellContent("异常类型", "漏油")
        Divider()
        getCellContentObj({
          leftTitle: '异常位置',
          rightValue: this.area
        })
        Button('修改数据').onClick(() => {
          this.area = '黑龙江'
        })
      }
      .width('100%')
        .height('100%')
        .backgroundColor('#ccc')
        .padding({ top: 30 })
    }
  }
  • 使用 @Builder 复用逻辑的时候,支持传参可以更灵活的渲染UI
  • 参数可以使用状态数据,不过需要建议通过对象的方式传入 @Builder

注意问题:

当参数存在两个或者两个以上的时候,就算通过对象字面量的形式传递,值的改变也不会引起UI刷新。

1.3 构建函数-传递参数练习-Tabs组件使用

上图中,是tabs组件中的tabbar属性,支持自定义builder,意味着我们可以定制它的样式

  • 准备八个图标放到资源目录下

  • 新建一个页面, 声明一个interface并建立四个数据的状态

interface TabInterface {
  name: string
  icon: ResourceStr
  selectIcon: ResourceStr
  title: string
}

  • 循环生成对应的TabContent
interface TabInterface {
  name: string
  icon: ResourceStr
  selectIcon: ResourceStr
  title: string
}

@Entry
  @ComponentV2
  struct TabBarDemo {
    @Local list: TabInterface[] = [{
      icon: $r("app.media.ic_public_message"),
      selectIcon: $r('app.media.ic_public_message_filled'),
      name: 'wechat',
      title: '微信',
    }, {
      icon: $r('app.media.ic_public_contacts_group'),
      selectIcon: $r('app.media.ic_public_contacts_group_filled'),
      name: 'connect',
      title: '联系人',
    }, {
      icon: $r('app.media.ic_gallery_discover'),
      selectIcon: $r('app.media.ic_gallery_discover_filled'),
      name: 'discover',
      title: '发现',
    }, {
      icon: $r('app.media.ic_public_contacts'),
      selectIcon: $r('app.media.ic_public_contacts_filled'),
      name: 'my',
      title: '我的',
    }]

    build() {
      Tabs() {
        ForEach(this.list, (item: TabInterface) => {
          TabContent() {
            Text(item.title)
          }
          .tabBar(item.title)
                })
      }
      .barPosition(BarPosition.End)
    }
  }

此时,如果我们想实现图中对应的效果,就需要使用自定义Builder来做,因为TabContent的tabBar属性支持CustomBuilder类型,CustomBuilder类型就是builder修饰的函数

  • 在当前组件中声明一个builder函数

 @Builder
  CommonTabBar (item: TabInterface) {
    Column () {
      Image(item.icon)
        .width(20)
        .height(20)
      Text(item.title)
        .fontSize(12)
        .fontColor("#1AAD19")
        .margin({
          top: 5
        })
    }
  }

interface TabInterface {
  name: string
  icon: ResourceStr
  selectIcon: ResourceStr
  title: string
}



@Entry
  @ComponentV2
  struct TabBarDemo {
    @Local list: TabInterface[] = [{
      icon: $r("app.media.ic_public_message"),
      selectIcon: $r('app.media.ic_public_message_filled'),
      name: 'wechat',
      title: '微信',
    }, {
      icon: $r('app.media.ic_public_contacts_group'),
      selectIcon: $r('app.media.ic_public_contacts_group_filled'),
      name: 'connect',
      title: '联系人',
    }, {
      icon: $r('app.media.ic_gallery_discover'),
      selectIcon: $r('app.media.ic_gallery_discover_filled'),
      name: 'discover',
      title: '发现',
    }, {
      icon: $r('app.media.ic_public_contacts'),
      selectIcon: $r('app.media.ic_public_contacts_filled'),
      name: 'my',
      title: '我的',
    }]

    @Builder
    CommonTabBar (item: TabInterface) {
      Column () {
        Image(item.icon)
          .width(20)
          .height(20)
        Text(item.title)
          .fontSize(12)
          .fontColor("#1AAD19")
          .margin({
            top: 5
          })
      }
    }

    build() {
      Tabs() {
        ForEach(this.list, (item: TabInterface) => {
          TabContent() {
            Text(item.title)
          }
          .tabBar(this.CommonTabBar(item))
                })
      }
      .barPosition(BarPosition.End)
    }
  }

  • 定义一个数据来绑定当前tabs的激活索引
@Local currentIndex: number = 0

  • 根据当前激活索引设置不同的颜色的图标
@Builder CommonTabBar (item: TabInterface) {
  Column() {
    Image(item.name === this.list[this.currentIndex].name ? item.selectIcon : item.icon)
      .width(20)
      .height(20)
    Text(item.title)
      .fontSize(12)
      .fontColor(item.name === this.list[this.currentIndex].name ? "#1AAD19" : "#2A2929")
      .margin({
        top: 5
      })
  }
}

1.4 构建函数-@BuilderParam 传递UI

插槽-Vue-Slot React-RenderProps

  • 把UI结构体的函数(Builder修饰的函数)当成参数传入到组件中,让组件放入固定的位置去渲染
  • 子组件接收传入的函数的修饰符/装饰器叫做BuilderParam

  • BuilderParam的基本使用 - 如何实现定制化Header?

使用BuilderParam的步骤

  • 前提:需要出现父子组件的关系
  • 前提:BuilderParam应出现在子组件中
  • 1. 子组件声明 @BuilderParam getConent: () => void
  • 2. BuilderParam的参数可以不给初始值,如果给了初始值, 就是默认内容
  • 3. 父组件传入的时候,它需要用builder修饰的函数又或者是 一个箭头函数中包裹着
  • 4. 调用builder函数的逻辑

  • 封装子组件
@Entry
  @ComponentV2
  struct BuildParamCase {
    build() {
      Column() {
        //   Header容器
        ChildHeader()
      }
      .width('100%')
    }
  }

@ComponentV2
  struct ChildHeader {
    build() {
      Row() {
        // 左
        Text('返回')
        // 中
        Text('首页')
          .layoutWeight(1)
          .textAlign(TextAlign.Center)
        // 右
        Text('确定')
      }
      .width('100%')
        .backgroundColor(Color.Pink)
        .padding(20)
    }
  }

  • 子组件中定义 @BuilderParam 接受结构参数
@Entry
  @ComponentV2
  struct BuildParamCase {
    @Builder
    LeftBuilder() {
      Image($r('sys.media.ohos_ic_compnent_titlebar_back'))
        .width(20)
    }

    @Builder
    CenterBuilder() {
      Row(){
        Text('最新推荐')
        Text('🔥')
      }
      .layoutWeight(1)
        .justifyContent(FlexAlign.Center)
    }

    @Builder
    RightBuilder(){
      Image($r('sys.media.ohos_ic_public_scan'))
        .width(20)
    }

    build() {
      Column() {
        // Header容器
        ChildHeader({
          leftContent: this.LeftBuilder,
          centerContent: this.CenterBuilder,
          rightContent: this.RightBuilder
        })
      }
      .width('100%')
    }
  }

@ComponentV2
  struct ChildHeader {
    @BuilderParam leftContent: ()=>void
    @BuilderParam centerContent: ()=>void
    @BuilderParam rightContent: ()=>void

    build() {
      Row() {
        // 左
        this.leftContent()
        // 中
        this.centerContent()
        // 右
        this.rightContent()
      }
      .width('100%')
        .backgroundColor(Color.Pink)
        .padding(20)
    }
  }

  • 子组件中定义默认值

@Entry
@ComponentV2
struct BuildParamCase {
  @Builder
  LeftBuilder() {
    Image($r('sys.media.ohos_ic_compnent_titlebar_back'))
      .width(20)
  }

  @Builder
  CenterBuilder() {
    Row(){
      Text('最新推荐')
      Text('🔥')
    }
    .layoutWeight(1)
    .justifyContent(FlexAlign.Center)
  }

  @Builder
  RightBuilder(){
    Image($r('sys.media.ohos_ic_public_scan'))
      .width(20)
  }

  build() {
    Column() {
      // Header容器
      ChildHeader({
        centerContent: this.CenterBuilder,
        rightContent: this.RightBuilder
      })
    }
    .width('100%')
  }
}

@ComponentV2
struct ChildHeader {
  @Builder leftDefault () {
    Text('返回')
  }
  @BuilderParam leftContent: ()=>void = this.leftDefault
  @BuilderParam centerContent: ()=>void
  @BuilderParam rightContent: ()=>void

  build() {
    Row() {
      // 左
      this.leftContent()
      // 中
      this.centerContent()
      // 右
      this.rightContent()
    }
    .width('100%')
    .backgroundColor(Color.Pink)
    .padding(20)
  }
}

1.5 @BuilderParam传值

场景:二次封装组件,可以用列表组件渲染数据 - 但是每一个选项的UI具体结构由调用者决定

  • 拷贝图片到assets

📎图片.zip

  • 封装一个列表的组件,可以渲染传入的数组,结构也是用户自己定义

@ComponentV2
  struct GoodList {
    @Param list: object[] = [] // 不知道传过来什么类型, 统一用object

    @Builder
    defaultRender() {
      Text('默认结构')
    }

    @BuilderParam renderItem: (item: object) => void = this.defaultRender

    build() {
      List({ space: 10 }) {
        ForEach(this.list, (item: object) => {
          ListItem() {
            this.renderItem(item)
          }
        })
      }
      .padding(20)
    }
  }

  • 父组件调用
import GoodList from '../components/GoodList'

interface GoodItem {
  goods_name: string
  goods_price: number
  goods_img: string
  goods_count: number
  id: number
}

@Entry
@ComponentV2
struct ListBuilder {
  @Local list: GoodItem[] = [
    {
      "id": 1,
      "goods_name": "班俏BANQIAO超火ins潮卫衣女士2020秋季新款韩版宽松慵懒风薄款外套带帽上衣",
      "goods_img": "assets/1.webp",
      "goods_price": 108,
      "goods_count": 1,
    },
    {
      "id": 2,
      "goods_name": "嘉叶希连帽卫衣女春秋薄款2020新款宽松bf韩版字母印花中长款外套ins潮",
      "goods_img": "assets/2.webp",
      "goods_price": 129,
      "goods_count": 1,
    },
    {
      "id": 3,
      "goods_name": "思蜜怡2020休闲运动套装女春秋季新款时尚大码宽松长袖卫衣两件套",
      "goods_img": "assets/3.webp",
      "goods_price": 198,
      "goods_count": 1,
    },
    {
      "id": 4,
      "goods_name": "思蜜怡卫衣女加绒加厚2020秋冬装新款韩版宽松上衣连帽中长款外套",
      "goods_img": "assets/4.webp",
      "goods_price": 99,
      "goods_count": 1,
    },
    {
      "id": 5,
      "goods_name": "幂凝早秋季卫衣女春秋装韩版宽松中长款假两件上衣薄款ins盐系外套潮",
      "goods_img": "assets/5.webp",
      "goods_price": 156,
      "goods_count": 1,
    },
    {
      "id": 6,
      "goods_name": "ME&CITY女装冬季新款针织抽绳休闲连帽卫衣女",
      "goods_img": "assets/6.webp",
      "goods_price": 142.8,
      "goods_count": 1,
    },
    {
      "id": 7,
      "goods_name": "幂凝假两件女士卫衣秋冬女装2020年新款韩版宽松春秋季薄款ins潮外套",
      "goods_img": "assets/7.webp",
      "goods_price": 219,
      "goods_count": 2,
    },
    {
      "id": 8,
      "goods_name": "依魅人2020休闲运动衣套装女秋季新款秋季韩版宽松卫衣 时尚两件套",
      "goods_img": "assets/8.webp",
      "goods_price": 178,
      "goods_count": 1,
    },
    {
      "id": 9,
      "goods_name": "芷臻(zhizhen)加厚卫衣2020春秋季女长袖韩版宽松短款加绒春秋装连帽开衫外套冬",
      "goods_img": "assets/9.webp",
      "goods_price": 128,
      "goods_count": 1,
    },
    {
      "id": 10,
      "goods_name": "Semir森马卫衣女冬装2019新款可爱甜美大撞色小清新连帽薄绒女士套头衫",
      "goods_img": "assets/10.webp",
      "goods_price": 153,
      "goods_count": 1,
    }
  ]

  @Builder
  renderItem() {
    Row({ space: 10 }) {
      Image('assets/1.webp')
        .borderRadius(8)
        .width(120)
        .height(200)
      Column() {
        Text('芷臻(zhizhen)加厚卫衣2020春秋季女长袖韩版宽松短款加绒春秋装连帽开衫外套冬')
          .fontWeight(FontWeight.Bold)
        Text("¥ 178")
          .fontColor(Color.Red)
          .fontWeight(FontWeight.Bold)
      }
      .padding({
        top: 5,
        bottom: 5
      })
      .alignItems(HorizontalAlign.Start)
      .justifyContent(FlexAlign.SpaceBetween)
      .height(200)
      .layoutWeight(1)
    }
    .width('100%')
  }

  build() {
    Column() {
      GoodList({
        list: this.list,
        renderItem: () => {
          this.renderItem()
        }
      })
    }
    .width('100%')
    .height('100%')
  }
}

  • 父组件接受传值,渲染
import GoodList from '../components/GoodList'

interface GoodItem {
  goods_name: string
  goods_price: number
  goods_img: string
  goods_count: number
  id: number
}

@Entry
  @ComponentV2
  struct ListBuilder {
    @Local list: GoodItem[] = [
      {
        "id": 1,
        "goods_name": "班俏BANQIAO超火ins潮卫衣女士2020秋季新款韩版宽松慵懒风薄款外套带帽上衣",
        "goods_img": "assets/1.webp",
        "goods_price": 108,
        "goods_count": 1,
      },
      {
        "id": 2,
        "goods_name": "嘉叶希连帽卫衣女春秋薄款2020新款宽松bf韩版字母印花中长款外套ins潮",
        "goods_img": "assets/2.webp",
        "goods_price": 129,
        "goods_count": 1,
      },
      {
        "id": 3,
        "goods_name": "思蜜怡2020休闲运动套装女春秋季新款时尚大码宽松长袖卫衣两件套",
        "goods_img": "assets/3.webp",
        "goods_price": 198,
        "goods_count": 1,
      },
      {
        "id": 4,
        "goods_name": "思蜜怡卫衣女加绒加厚2020秋冬装新款韩版宽松上衣连帽中长款外套",
        "goods_img": "assets/4.webp",
        "goods_price": 99,
        "goods_count": 1,
      },
      {
        "id": 5,
        "goods_name": "幂凝早秋季卫衣女春秋装韩版宽松中长款假两件上衣薄款ins盐系外套潮",
        "goods_img": "assets/5.webp",
        "goods_price": 156,
        "goods_count": 1,
      },
      {
        "id": 6,
        "goods_name": "ME&CITY女装冬季新款针织抽绳休闲连帽卫衣女",
        "goods_img": "assets/6.webp",
        "goods_price": 142.8,
        "goods_count": 1,
      },
      {
        "id": 7,
        "goods_name": "幂凝假两件女士卫衣秋冬女装2020年新款韩版宽松春秋季薄款ins潮外套",
        "goods_img": "assets/7.webp",
        "goods_price": 219,
        "goods_count": 2,
      },
      {
        "id": 8,
        "goods_name": "依魅人2020休闲运动衣套装女秋季新款秋季韩版宽松卫衣 时尚两件套",
        "goods_img": "assets/8.webp",
        "goods_price": 178,
        "goods_count": 1,
      },
      {
        "id": 9,
        "goods_name": "芷臻(zhizhen)加厚卫衣2020春秋季女长袖韩版宽松短款加绒春秋装连帽开衫外套冬",
        "goods_img": "assets/9.webp",
        "goods_price": 128,
        "goods_count": 1,
      },
      {
        "id": 10,
        "goods_name": "Semir森马卫衣女冬装2019新款可爱甜美大撞色小清新连帽薄绒女士套头衫",
        "goods_img": "assets/10.webp",
        "goods_price": 153,
        "goods_count": 1,
      }
    ]

    @Builder
    renderItem (item: GoodItem) {
      Row({ space: 10 }) {
        Image(item.goods_img)
          .borderRadius(8)
          .width(120)
          .height(200)
        Column() {
          Text(item.goods_name)
            .fontWeight(FontWeight.Bold)
          Text("¥ " + item.goods_price)
            .fontColor(Color.Red)
            .fontWeight(FontWeight.Bold)
        }
        .padding({
          top: 5,
          bottom: 5
        })
          .alignItems(HorizontalAlign.Start)
          .justifyContent(FlexAlign.SpaceBetween)
          .height(200)
          .layoutWeight(1)
      }
      .width('100%')
    }

    build() {
      Column() {
        GoodList({
          list: this.list,
          renderItem: (item: object) => {
            this.renderItem(item as GoodItem)
          }
        })
      }
      .width('100%')
        .height('100%')
    }
  }

1.6 尾随闭包

Column () { } 中大括号就是尾随闭包的写法

当我们的组件只有一个BuilderParam的时候,此时可以使用尾随闭包的语法

也就是像我们原来使用Column或者Row组件时一样,直接在大括号中传入

我们用尾随闭包来封装这样的组件,理解一下BuildParam的使用

首先封装一个Panel组件

@ComponentV2
  struct PanelComp {
    @Param leftText:string = '左侧标题'

    @BuilderParam
    rightContent:()=>void = this.defaultContent
    @Builder
    defaultContent(){
      Row({space:16}){
        Checkbox().select(true).shape(CheckBoxShape.CIRCLE)
        Text('是')
      }
    }

    build() {
      Row(){
        Text(this.leftText)
        this.rightContent()
      }
      .width('100%')
        .padding(20)
        .backgroundColor('#ccc')
        .borderRadius(8)
        .justifyContent(FlexAlign.SpaceBetween)
    }
  }

export { PanelComp }

  • 接下来父组件使用,并分别传递左侧文字和右侧的结构
import { PanelComp } from '../components/PanelComp'


@Entry
  @ComponentV2
  struct PanelPage {
    @Local isOn: boolean = false

    // @Builder rightContent() {
    //   Toggle({ type: ToggleType.Switch })
    // }

    build() {
      Column() {
        PanelComp({
          leftText: '低电量模式',
          // rightContent: this.rightContent
        }) {
          Toggle({
            type: ToggleType.Switch,
            isOn: $$this.isOn
          })
        }
        Text(this.isOn.toString()).fontSize(20)

        PanelComp({
          leftText: '设置'
        }) {
          Text('查看详情 >')
        }
      }
      .padding(20)
        .width('100%')
        .height('100%')
        .backgroundColor(Color.Pink)
    }
  }

只有一个BuilderParam且不需要传参的时候,可以使用尾随闭包

注意:尾随闭包用空大括号就代表传递空内容,会替代默认内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值