ArkUI自定义组件

一、自定义组件

组件是 OpenHarmony 页面最小显示单元,一个页面可由多个组件组合而成,也可只由一个组件组合而成,这些组件可以是ArkUI框架自带系统组件,比如 Text 、 Button 等,也可以是自定义组件,

在ArkUI中,UI显示的内容均为组件,由框架直接提供的称为系统组件,由开发者定义的称为自定义组件。在进行 UI 界面开发时,通常不是简单的将系统组件进行组合使用,而是需要考虑代码可复用性、业务逻辑与UI分离,后续版本演进等因素。因此,将UI和部分业务逻辑封装成自定义组件是不可或缺的能力。

自定义组件具有以下特点:

  • 可组合:允许开发者组合使用系统组件、及其属性和方法。
  • 可重用:自定义组件可以被其他组件重用,并作为不同的实例在不同的父组件或容器中使用。
  • 数据驱动UI更新:通过状态变量的改变,来驱动UI的刷新。

二、语法如下

自定义一个组件,需要要定义组件名称,做到见名知意,比如定义一个标题栏组件,笔者把它命名为 TitleBar ,为了让系统知道这是一个组件,需要使用 @Component 修饰符和 struct 关键字修饰,格式:【@Component struct + 组件名称】,如下所示:

@Component 
struct TitleBar {
  build() {
    // 省略
  }
}

@Entry 
@Component struct Index {
  build() {
    // 省略
  }
}

说明如下:

  • struct:表示 TitleBar 是一个结构体,使用 struct 关键字必须实现 build() 方法,否则编译器报错:Require build function for struct 。
  • @Component:表示 TitleBar 这个结构体具有组件化的能力,可以成为一个独立的组件。
  • @Entry:表示当前组件是页面的总入口,就是页面的根节点,一个页面有且仅有一个 @Entry 修饰符,只有被 @Entry 修饰的组件或者子组件才会在页面上显示。

注意:自定义组件禁止添加构造函数,否则编译器报错

三、自定义组件案例

3.1.需求说明

以之前的商品列表展示页面为案例演示:

先给上面的内容添加,标题栏添加返回按钮和刷新按钮,如下:

实现代码如下:

class Item{
  //定位属性
  name: string
  image: any
  price: number

  constructor(name: string, image: any, price: number) {
    this.name = name
    this.image = image
    this.price = price
  }
}

@Entry
@Component
struct ListExample {
  private items:Array<Item> = [
    new Item("HUAWEI Mate 60 Pro",$r("app.media.mate60"),  7999.00),
    new Item("HUAWEI MatePad Pro",$r("app.media.MatePadPro"),4299.00),
    new Item("HUAWEI WATCH GT 4", $r("app.media.WATCHGT4"), 1538.00),
    new Item("HUAWEI FreeBuds Pro 3", $r("app.media.FreeBudsPro3"), 1499.00),
    new Item("HUAWEI MateBook 14s", $r("app.media.MateBook14s"), 5299.00),
    new Item("HUAWEI FreeBuds Pro 3", $r("app.media.FreeBudsPro3"), 1499.00),
    new Item("HUAWEI MateBook 14s", $r("app.media.MateBook14s"), 5299.00),
    new Item("HUAWEI Mate 60",$r("app.media.mate60"),  5999.00),
    new Item("HUAWEI MatePad2 Pro",$r("app.media.MatePadPro"),4599.00),
    new Item("HUAWEI WATCH GT 5", $r("app.media.WATCHGT4"), 1638.00),
    new Item("HUAWEI FreeBuds Pro 4", $r("app.media.FreeBudsPro3"), 1699.00),
  ]
  build() {
    Column({space:8}){
      Row(){
        //返回按钮
        Image($r("app.media.icon_back_button")).width(30).height(30)

        Text("商品列表")
          .fontSize(30)
          .fontWeight(FontWeight.Bold)
        Blank()//这里为了有让文字和刷新按钮之间空间,Blank会占满剩余空间
        //刷新按钮
        Image($r("app.media.icon_refresh_button")).width(30).height(30)
      }
      .width("100%")
      .padding(20)

      List({space:10}){
        ForEach(this.items,(item: Item)=>{
          ListItem(){
            Row({space:10}){
              Image(item.image).width(100)
              Column({space:4}){
                Text(item.name).fontSize(15).fontWeight(FontWeight.Bold)
                Text(`原价:¥ ${item.price}`).fontColor("#F36").fontSize(18)
              }
              .height("100%")
              .alignItems(HorizontalAlign.Start)
            }
            .width("90%")  //设置宽度
            .height(120)    //设置高度
            .justifyContent(FlexAlign.SpaceBetween) //设置主轴方向主轴方向均匀分配弹性元素,相邻元素之间距离相同。 第一个元素与行首对齐,最后一个元素与行尾对齐。
            .backgroundColor("#FFFFFF") //设置背景为白色
            .borderRadius(10) //这是圆角班级
            .padding(20) //内边距
          }
        })
      }
        .alignListItem(ListItemAlign.Center) //ListItem在List交叉轴方向的布局方式,默认为首部对齐。这里改为居中对齐
        .height("100%")
        .width("100%")

    }
    .width("100%")
    .height("100%")
    .backgroundColor("#D3D3D3")
  }
}

3.2.页面内部自定义标题组件

上面的代码的将标题部分,抽取出来,形成组件,如下:

//自定义标题组件
@Component
struct TitleBar{
  private title:string;

  build(){
    Row(){
      //返回按钮
      Image($r("app.media.icon_back_button")).width(30)

      //标题
      Text(this.title)
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
      Blank()//这里为了有让文字和刷新按钮之间空间,Blank会占满剩余空间
      //刷新按钮
      Image($r("app.media.icon_refresh_button")).width(30)
    }
    .width("100%")
    .padding(20)
  }
}

class Item{
  //定位属性
  name: string
  image: any
  price: number

  constructor(name: string, image: any, price: number) {
    this.name = name
    this.image = image
    this.price = price
  }
}

@Entry
@Component
struct ListExample {
  private items:Array<Item> = [
    new Item("HUAWEI Mate 60 Pro",$r("app.media.mate60"),  7999.00),
    new Item("HUAWEI MatePad Pro",$r("app.media.MatePadPro"),4299.00),
    new Item("HUAWEI WATCH GT 4", $r("app.media.WATCHGT4"), 1538.00),
    new Item("HUAWEI FreeBuds Pro 3", $r("app.media.FreeBudsPro3"), 1499.00),
    new Item("HUAWEI MateBook 14s", $r("app.media.MateBook14s"), 5299.00),
    new Item("HUAWEI FreeBuds Pro 3", $r("app.media.FreeBudsPro3"), 1499.00),
    new Item("HUAWEI MateBook 14s", $r("app.media.MateBook14s"), 5299.00),
    new Item("HUAWEI Mate 60",$r("app.media.mate60"),  5999.00),
    new Item("HUAWEI MatePad2 Pro",$r("app.media.MatePadPro"),4599.00),
    new Item("HUAWEI WATCH GT 5", $r("app.media.WATCHGT4"), 1638.00),
    new Item("HUAWEI FreeBuds Pro 4", $r("app.media.FreeBudsPro3"), 1699.00),
  ]
  build() {
    Column({space:8}){

      //调用自定义组件实现标题
      TitleBar({title:"商品列表"})

      List({space:10}){
        ForEach(this.items,(item: Item)=>{
          ListItem(){
            Row({space:10}){
              Image(item.image).width(100)
              Column({space:4}){
                Text(item.name).fontSize(15).fontWeight(FontWeight.Bold)
                Text(`原价:¥ ${item.price}`).fontColor("#F36").fontSize(18)
              }
              .height("100%")
              .alignItems(HorizontalAlign.Start)
            }
            .width("90%")  //设置宽度
            .height(120)    //设置高度
            .justifyContent(FlexAlign.SpaceBetween) //设置主轴方向主轴方向均匀分配弹性元素,相邻元素之间距离相同。 第一个元素与行首对齐,最后一个元素与行尾对齐。
            .backgroundColor("#FFFFFF") //设置背景为白色
            .borderRadius(10) //这是圆角班级
            .padding(20) //内边距
          }
        })
      }
        .alignListItem(ListItemAlign.Center) //ListItem在List交叉轴方向的布局方式,默认为首部对齐。这里改为居中对齐
        .height("100%")
        .width("100%")

    }
    .width("100%")
    .height("100%")
    .backgroundColor("#D3D3D3")
  }
}

预览效果如下:

上面这样实现没用问题,但是像标题组件这种组件,后续其他模块也要使用,那么就需要定义在外部一个功能的模块,后续调用

3.3.外部自定义标题组件

将上面的标题组件,定位在其他的模块文件中,以供其他的模块调用

1)在ets目录下先创建components目录,然后创建CommonComponents.ets,内容如下:

//自定义标题组件
@Component
export struct TitleBar{
  private title:string;

  build(){
    Row(){
      //返回按钮
      Image($r("app.media.icon_back_button")).width(30)

      //标题
      Text(this.title)
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
      Blank()//这里为了有让文字和刷新按钮之间空间,Blank会占满剩余空间
      //刷新按钮
      Image($r("app.media.icon_refresh_button")).width(30)
    }
    .width("100%")
    .padding(20)
  }
}

注意:在struct前面需要添加export,其他模块才可以导入使用

2)删除上一个章节模块中定义的组件,在模块中导入外部定义的组件,代码如下:

//导入组件
import {TitleBar} from "../components/CommonComponents"

class Item{
  //定位属性
  name: string
  image: any
  price: number

  constructor(name: string, image: any, price: number) {
    this.name = name
    this.image = image
    this.price = price
  }
}

@Entry
@Component
struct ListExample {
  private items:Array<Item> = [
    new Item("HUAWEI Mate 60 Pro",$r("app.media.mate60"),  7999.00),
    new Item("HUAWEI MatePad Pro",$r("app.media.MatePadPro"),4299.00),
    new Item("HUAWEI WATCH GT 4", $r("app.media.WATCHGT4"), 1538.00),
    new Item("HUAWEI FreeBuds Pro 3", $r("app.media.FreeBudsPro3"), 1499.00),
    new Item("HUAWEI MateBook 14s", $r("app.media.MateBook14s"), 5299.00),
    new Item("HUAWEI FreeBuds Pro 3", $r("app.media.FreeBudsPro3"), 1499.00),
    new Item("HUAWEI MateBook 14s", $r("app.media.MateBook14s"), 5299.00),
    new Item("HUAWEI Mate 60",$r("app.media.mate60"),  5999.00),
    new Item("HUAWEI MatePad2 Pro",$r("app.media.MatePadPro"),4599.00),
    new Item("HUAWEI WATCH GT 5", $r("app.media.WATCHGT4"), 1638.00),
    new Item("HUAWEI FreeBuds Pro 4", $r("app.media.FreeBudsPro3"), 1699.00),
  ]
  build() {
    Column({space:8}){

      //调用自定义组件实现标题
      TitleBar({title:"商品列表"})

      List({space:10}){
        ForEach(this.items,(item: Item)=>{
          ListItem(){
            Row({space:10}){
              Image(item.image).width(100)
              Column({space:4}){
                Text(item.name).fontSize(15).fontWeight(FontWeight.Bold)
                Text(`原价:¥ ${item.price}`).fontColor("#F36").fontSize(18)
              }
              .height("100%")
              .alignItems(HorizontalAlign.Start)
            }
            .width("90%")  //设置宽度
            .height(120)    //设置高度
            .justifyContent(FlexAlign.SpaceBetween) //设置主轴方向主轴方向均匀分配弹性元素,相邻元素之间距离相同。 第一个元素与行首对齐,最后一个元素与行尾对齐。
            .backgroundColor("#FFFFFF") //设置背景为白色
            .borderRadius(10) //这是圆角班级
            .padding(20) //内边距
          }
        })
      }
        .alignListItem(ListItemAlign.Center) //ListItem在List交叉轴方向的布局方式,默认为首部对齐。这里改为居中对齐
        .height("100%")
        .width("100%")

    }
    .width("100%")
    .height("100%")
    .backgroundColor("#D3D3D3")
  }
}

预览效果如下:

四、自定义函数

上面的自定义组件,可以用于整个项目的一些全局组件的封装调用,但是有时候我们往往需要对某个页面内部的部分内容进行封装调用,这个时候使用自定义函数更加的合适,当然这并不是说不能使用之前的自定义组件来实现

4.1.自定义函数

1)在组件外部自定义函数,这里所说的外部还是在同一个ets文件中,只不过不在struct里面,如下抽取商品列表的Row组件,自定义函数 ProductList ,然后调用,代码如下:

//导入组件
import {TitleBar} from "../components/CommonComponents"


class Item{
  //定位属性
  name: string
  image: any
  price: number

  constructor(name: string, image: any, price: number) {
    this.name = name
    this.image = image
    this.price = price
  }
}

//自定义函数,抽取出商品列表
@Builder function ProductList(item:Item){
  Row({space:10}){
    Image(item.image).width(100)
    Column({space:4}){
      Text(item.name).fontSize(15).fontWeight(FontWeight.Bold)
      Text(`原价:¥ ${item.price}`).fontColor("#F36").fontSize(18)
    }
    .height("100%")
    .alignItems(HorizontalAlign.Start)
  }
  .width("90%")  //设置宽度
  .height(120)    //设置高度
  .justifyContent(FlexAlign.SpaceBetween) //设置主轴方向主轴方向均匀分配弹性元素,相邻元素之间距离相同。 第一个元素与行首对齐,最后一个元素与行尾对齐。
  .backgroundColor("#FFFFFF") //设置背景为白色
  .borderRadius(10) //这是圆角班级
  .padding(20) //内边距
}


@Entry
@Component
struct ListExample {
  private items:Array<Item> = [
    new Item("HUAWEI Mate 60 Pro",$r("app.media.mate60"),  7999.00),
    new Item("HUAWEI MatePad Pro",$r("app.media.MatePadPro"),4299.00),
    new Item("HUAWEI WATCH GT 4", $r("app.media.WATCHGT4"), 1538.00),
    new Item("HUAWEI FreeBuds Pro 3", $r("app.media.FreeBudsPro3"), 1499.00),
    new Item("HUAWEI MateBook 14s", $r("app.media.MateBook14s"), 5299.00),
    new Item("HUAWEI FreeBuds Pro 3", $r("app.media.FreeBudsPro3"), 1499.00),
    new Item("HUAWEI MateBook 14s", $r("app.media.MateBook14s"), 5299.00),
    new Item("HUAWEI Mate 60",$r("app.media.mate60"),  5999.00),
    new Item("HUAWEI MatePad2 Pro",$r("app.media.MatePadPro"),4599.00),
    new Item("HUAWEI WATCH GT 5", $r("app.media.WATCHGT4"), 1638.00),
    new Item("HUAWEI FreeBuds Pro 4", $r("app.media.FreeBudsPro3"), 1699.00),
  ]
  build() {
    Column({space:8}){

      //调用自定义组件实现标题
      TitleBar({title:"商品列表"})

      List({space:10}){
        ForEach(this.items,(item: Item)=>{
          ListItem(){
            //调用自定义函数
            ProductList(item)
          }
        })
      }
        .alignListItem(ListItemAlign.Center) //ListItem在List交叉轴方向的布局方式,默认为首部对齐。这里改为居中对齐
        .height("100%")
        .width("100%")
    }
    .width("100%")
    .height("100%")
    .backgroundColor("#D3D3D3")
  }
}

注意:

  • 在组件外部自定义函数的时候使用@Builder和function关键字,后面写函数名,括号中可以写参数
  • 在调用的时候使用  ProductList(item)的形式即可

2)在内部自定义函数,也就是在struct里面,如下抽取商品列表的Row组件(先注释或者删除上面上面在外部定义的函数),在内部自定义函数 ProductList ,然后调用,代码如下:

//导入组件
import {TitleBar} from "../components/CommonComponents"

class Item{
  //定位属性
  name: string
  image: any
  price: number

  constructor(name: string, image: any, price: number) {
    this.name = name
    this.image = image
    this.price = price
  }
}

@Entry
@Component
struct ListExample {
  private items:Array<Item> = [
    new Item("HUAWEI Mate 60 Pro",$r("app.media.mate60"),  7999.00),
    new Item("HUAWEI MatePad Pro",$r("app.media.MatePadPro"),4299.00),
    new Item("HUAWEI WATCH GT 4", $r("app.media.WATCHGT4"), 1538.00),
    new Item("HUAWEI FreeBuds Pro 3", $r("app.media.FreeBudsPro3"), 1499.00),
    new Item("HUAWEI MateBook 14s", $r("app.media.MateBook14s"), 5299.00),
    new Item("HUAWEI FreeBuds Pro 3", $r("app.media.FreeBudsPro3"), 1499.00),
    new Item("HUAWEI MateBook 14s", $r("app.media.MateBook14s"), 5299.00),
    new Item("HUAWEI Mate 60",$r("app.media.mate60"),  5999.00),
    new Item("HUAWEI MatePad2 Pro",$r("app.media.MatePadPro"),4599.00),
    new Item("HUAWEI WATCH GT 5", $r("app.media.WATCHGT4"), 1638.00),
    new Item("HUAWEI FreeBuds Pro 4", $r("app.media.FreeBudsPro3"), 1699.00),
  ]

  //自定义函数,抽取出商品列表, 内部定义不能加function 
  @Builder ProductList(item:Item){
  Row({space:10}){
    Image(item.image).width(100)
    Column({space:4}){
      Text(item.name).fontSize(15).fontWeight(FontWeight.Bold)
      Text(`原价:¥ ${item.price}`).fontColor("#F36").fontSize(18)
    }
    .height("100%")
    .alignItems(HorizontalAlign.Start)
  }
  .width("90%")  //设置宽度
  .height(120)    //设置高度
  .justifyContent(FlexAlign.SpaceBetween) //设置主轴方向主轴方向均匀分配弹性元素,相邻元素之间距离相同。 第一个元素与行首对齐,最后一个元素与行尾对齐。
  .backgroundColor("#FFFFFF") //设置背景为白色
  .borderRadius(10) //这是圆角班级
  .padding(20) //内边距
}
  
  build() {
    Column({space:8}){

      //调用自定义组件实现标题
      TitleBar({title:"商品列表"})

      List({space:10}){
        ForEach(this.items,(item: Item)=>{
          ListItem(){
            //调用自定义函数
            this.ProductList(item)
          }
        })
      }
        .alignListItem(ListItemAlign.Center) //ListItem在List交叉轴方向的布局方式,默认为首部对齐。这里改为居中对齐
        .height("100%")
        .width("100%")
    }
    .width("100%")
    .height("100%")
    .backgroundColor("#D3D3D3")
  }
}

注意:

  • 在组件内部自定义函数的时候使用@Builder关键字,后面写函数名,括号中可以写参数,注意不能加function关键字,否则会报错
  • 在调用的时候使用  this.ProductList(item)的形式即可,this必须要有

4.2.@styles装饰器,仅可封装组件通用属性

1)组件外部定义样式函数,将组件中Column的样式设置,封装成函数 fillStyles,然后调用,如下:

//导入组件
import {TitleBar} from "../components/CommonComponents"

class Item{
  //定位属性
  name: string
  image: any
  price: number

  constructor(name: string, image: any, price: number) {
    this.name = name
    this.image = image
    this.price = price
  }
}

//封装组件通用属性成函数
@Styles function fillStyles(){
  .width("100%")
  .height("100%")
  .backgroundColor("#D3D3D3")
}

@Entry
@Component
struct ListExample {
  private items:Array<Item> = [
    new Item("HUAWEI Mate 60 Pro",$r("app.media.mate60"),  7999.00),
    new Item("HUAWEI MatePad Pro",$r("app.media.MatePadPro"),4299.00),
    new Item("HUAWEI WATCH GT 4", $r("app.media.WATCHGT4"), 1538.00),
    new Item("HUAWEI FreeBuds Pro 3", $r("app.media.FreeBudsPro3"), 1499.00),
    new Item("HUAWEI MateBook 14s", $r("app.media.MateBook14s"), 5299.00),
    new Item("HUAWEI FreeBuds Pro 3", $r("app.media.FreeBudsPro3"), 1499.00),
    new Item("HUAWEI MateBook 14s", $r("app.media.MateBook14s"), 5299.00),
    new Item("HUAWEI Mate 60",$r("app.media.mate60"),  5999.00),
    new Item("HUAWEI MatePad2 Pro",$r("app.media.MatePadPro"),4599.00),
    new Item("HUAWEI WATCH GT 5", $r("app.media.WATCHGT4"), 1638.00),
    new Item("HUAWEI FreeBuds Pro 4", $r("app.media.FreeBudsPro3"), 1699.00),
  ]

  //自定义函数,抽取出商品列表, 内部定义不能加function
  @Builder ProductList(item:Item){
  Row({space:10}){
    Image(item.image).width(100)
    Column({space:4}){
      Text(item.name).fontSize(15).fontWeight(FontWeight.Bold)
      Text(`原价:¥ ${item.price}`).fontColor("#F36").fontSize(18)
    }
    .height("100%")
    .alignItems(HorizontalAlign.Start)
  }
  .width("90%")  //设置宽度
  .height(120)    //设置高度
  .justifyContent(FlexAlign.SpaceBetween) //设置主轴方向主轴方向均匀分配弹性元素,相邻元素之间距离相同。 第一个元素与行首对齐,最后一个元素与行尾对齐。
  .backgroundColor("#FFFFFF") //设置背景为白色
  .borderRadius(10) //这是圆角班级
  .padding(20) //内边距
}

  build() {
    Column({space:8}){

      //调用自定义组件实现标题
      TitleBar({title:"商品列表"})

      List({space:10}){
        ForEach(this.items,(item: Item)=>{
          ListItem(){
            //调用自定义函数
            this.ProductList(item)
          }
        })
      }
        .alignListItem(ListItemAlign.Center) //ListItem在List交叉轴方向的布局方式,默认为首部对齐。这里改为居中对齐
        .height("100%")
        .width("100%")
    }
    .fillStyles() //调用外部定义的样式
  }
}

注意:

  • 在组件外部自定义样式函数的时候使用@Styles和function关键字,后面写函数名,括号中可以写参数
  • 在调用的时候使用 .fillStyles() 的形式即可,前面必须有一个点,当成普通的属性使用即可

2)组件内部定义样式函数,将组件中Column的样式设置,封装成函数 fillStyles(先删除之前在外部定义的样式函数),然后调用,如下:

//导入组件
import {TitleBar} from "../components/CommonComponents"

class Item{
  //定位属性
  name: string
  image: any
  price: number

  constructor(name: string, image: any, price: number) {
    this.name = name
    this.image = image
    this.price = price
  }
}

@Entry
@Component
struct ListExample {
  private items:Array<Item> = [
    new Item("HUAWEI Mate 60 Pro",$r("app.media.mate60"),  7999.00),
    new Item("HUAWEI MatePad Pro",$r("app.media.MatePadPro"),4299.00),
    new Item("HUAWEI WATCH GT 4", $r("app.media.WATCHGT4"), 1538.00),
    new Item("HUAWEI FreeBuds Pro 3", $r("app.media.FreeBudsPro3"), 1499.00),
    new Item("HUAWEI MateBook 14s", $r("app.media.MateBook14s"), 5299.00),
    new Item("HUAWEI FreeBuds Pro 3", $r("app.media.FreeBudsPro3"), 1499.00),
    new Item("HUAWEI MateBook 14s", $r("app.media.MateBook14s"), 5299.00),
    new Item("HUAWEI Mate 60",$r("app.media.mate60"),  5999.00),
    new Item("HUAWEI MatePad2 Pro",$r("app.media.MatePadPro"),4599.00),
    new Item("HUAWEI WATCH GT 5", $r("app.media.WATCHGT4"), 1638.00),
    new Item("HUAWEI FreeBuds Pro 4", $r("app.media.FreeBudsPro3"), 1699.00),
  ]

  //自定义函数,抽取出商品列表, 内部定义不能加function
  @Builder ProductList(item:Item){
  Row({space:10}){
    Image(item.image).width(100)
    Column({space:4}){
      Text(item.name).fontSize(15).fontWeight(FontWeight.Bold)
      Text(`原价:¥ ${item.price}`).fontColor("#F36").fontSize(18)
    }
    .height("100%")
    .alignItems(HorizontalAlign.Start)
  }
  .width("90%")  //设置宽度
  .height(120)    //设置高度
  .justifyContent(FlexAlign.SpaceBetween) //设置主轴方向主轴方向均匀分配弹性元素,相邻元素之间距离相同。 第一个元素与行首对齐,最后一个元素与行尾对齐。
  .backgroundColor("#FFFFFF") //设置背景为白色
  .borderRadius(10) //这是圆角班级
  .padding(20) //内边距
}

  //封装组件通用属性成函数,不需要添加function
  @Styles fillStyles(){
    .width("100%")
    .height("100%")
    .backgroundColor("#D3D3D3")
  }

  build() {
    Column({space:8}){

      //调用自定义组件实现标题
      TitleBar({title:"商品列表"})

      List({space:10}){
        ForEach(this.items,(item: Item)=>{
          ListItem(){
            //调用自定义函数
            this.ProductList(item)
          }
        })
      }
        .alignListItem(ListItemAlign.Center) //ListItem在List交叉轴方向的布局方式,默认为首部对齐。这里改为居中对齐
        .height("100%")
        .width("100%")
    }
    .fillStyles() //调用外部定义的样式
  }
}

注意:

  • 在组件内部自定义样式函数的时候使用@Styles关键字,后面写函数名,括号中可以写参数,注意不能加function关键字,否则会报错
  • 在调用的时候和上面一样,还是使用 .fillStyles() 的形式即可,前面必须有一个点,当成普通的属性使用即可,不需要添加this

4.3.@Extend装饰器,仅可定义在全局,可以设置组件特有属性

将商品价格Text的样式抽取出来,封装成样式,但是有一个问题,这里的字体样式大小设置,是继承自Text组件,跟上一个章节中宽和高不同,宽和高是公共属性,这时候就无法在使用@styles装饰器, 而是使用@Extend装饰器,但是需要注意@Extend装饰器仅可定义在全局,可以设置组件特有属性,如下:

//导入组件
import {TitleBar} from "../components/CommonComponents"

class Item{
  //定位属性
  name: string
  image: any
  price: number

  constructor(name: string, image: any, price: number) {
    this.name = name
    this.image = image
    this.price = price
  }
}

//@Extend装饰器,仅可定义在全局,可以设置组件特有属性
@Extend(Text) function priceText(){
  .fontColor("#F36").fontSize(18)
}

@Entry
@Component
struct ListExample {
  private items:Array<Item> = [
    new Item("HUAWEI Mate 60 Pro",$r("app.media.mate60"),  7999.00),
    new Item("HUAWEI MatePad Pro",$r("app.media.MatePadPro"),4299.00),
    new Item("HUAWEI WATCH GT 4", $r("app.media.WATCHGT4"), 1538.00),
    new Item("HUAWEI FreeBuds Pro 3", $r("app.media.FreeBudsPro3"), 1499.00),
    new Item("HUAWEI MateBook 14s", $r("app.media.MateBook14s"), 5299.00),
    new Item("HUAWEI FreeBuds Pro 3", $r("app.media.FreeBudsPro3"), 1499.00),
    new Item("HUAWEI MateBook 14s", $r("app.media.MateBook14s"), 5299.00),
    new Item("HUAWEI Mate 60",$r("app.media.mate60"),  5999.00),
    new Item("HUAWEI MatePad2 Pro",$r("app.media.MatePadPro"),4599.00),
    new Item("HUAWEI WATCH GT 5", $r("app.media.WATCHGT4"), 1638.00),
    new Item("HUAWEI FreeBuds Pro 4", $r("app.media.FreeBudsPro3"), 1699.00),
  ]

  //自定义函数,抽取出商品列表, 内部定义不能加function
  @Builder ProductList(item:Item){
  Row({space:10}){
    Image(item.image).width(100)
    Column({space:4}){
      Text(item.name).fontSize(15).fontWeight(FontWeight.Bold)
      Text(`原价:¥ ${item.price}`)
        .priceText() //使用自定义的样式
    }
    .height("100%")
    .alignItems(HorizontalAlign.Start)
  }
  .width("90%")  //设置宽度
  .height(120)    //设置高度
  .justifyContent(FlexAlign.SpaceBetween) //设置主轴方向主轴方向均匀分配弹性元素,相邻元素之间距离相同。 第一个元素与行首对齐,最后一个元素与行尾对齐。
  .backgroundColor("#FFFFFF") //设置背景为白色
  .borderRadius(10) //这是圆角班级
  .padding(20) //内边距
}

  //封装组件通用属性成函数,不需要添加function
  @Styles fillStyles(){
    .width("100%")
    .height("100%")
    .backgroundColor("#D3D3D3")
  }

  build() {
    Column({space:8}){

      //调用自定义组件实现标题
      TitleBar({title:"商品列表"})

      List({space:10}){
        ForEach(this.items,(item: Item)=>{
          ListItem(){
            //调用自定义函数
            this.ProductList(item)
          }
        })
      }
        .alignListItem(ListItemAlign.Center) //ListItem在List交叉轴方向的布局方式,默认为首部对齐。这里改为居中对齐
        .height("100%")
        .width("100%")
    }
    .fillStyles() //调用外部定义的样式
  }
}

注意:

  • 在组件内部自定义样式函数的时候使用@Extend和function关键字,后面写函数名,括号中可以写参数
  • 在调用的时候和上面一样,还是使用 .priceText() 的形式即可,前面必须有一个点,当成普通的属性使用即可,不需要添加this
  • 尽可以定义在全局使用,不可以在内部定义
posted @ 2023-12-26 20:00  酒剑仙*  阅读(295)  评论(0)    收藏  举报