HarmonyOS系统开发ArkTS常用组件编程技巧

时间:2024-03-27 21:27:17

目录

样式复用

@Styles方法

@Extend方法


        组件编程在使用过程中有很多技巧,在这里分享样式复用技巧和UI结构复用技巧。

样式复用

我们观察下面的代码,在代码中很多重复行的代码,如:

    Image 的  .width(30).height(30) 是重复的
    Button的  .fontSize(25).width(150).height(65).backgroundColor(Color.Green)是重复的
    Text  的  .fontSize(20).fontColor(Color.White).fontWeight(500) 是重复的
@Entry
@Component
struct customStyle {
  @State isOn: boolean = false;
  @State isDel: boolean = false;

  build() {
    Column({ space: 10 }) {
      Button({ type: ButtonType.Capsule, stateEffect: true }) {
        Row({ space: 10 }) {
          Image($r('app.media.icon_new_folder'))
            .width(30)
            .height(30)
          Text('新建文件')
            .fontSize(20)
            .fontColor(Color.White)
            .fontWeight(500)
        }
      }
      .fontSize(25)
      .width(150)
      .height(65)
      .backgroundColor(Color.Green)
      .onClick(() => {
        console.log('我准备开始建立文件夹');
      })

      Button({ type: ButtonType.Capsule, stateEffect: true }) {
        Row({ space: 10 }) {
          Image($r('app.media.icon_new_folder'))
            .width(30)
            .height(30)
          Text('新建文件')
            .fontSize(20)
            .fontColor(Color.White)
            .fontWeight(500)
        }
      }
      .fontSize(25)
      .width(150)
      .height(65)
      .backgroundColor(Color.Green)
      .onClick(() => {
        console.log('我准备开始建立文件夹');
      })
    }
    .width('100%')
    .height("100%")
    .justifyContent(FlexAlign.Center)

  }
}


@Extend(Image) function consumeImgStyle() {
  .width(30)
  .height(30)
}

@Extend(Button) function consumeButtonStyle() {
  .fontSize(25)
  .width(150)
  .height(65)
}

@Extend(Text) function consumeBTextStyle() {
  .fontSize(20)
  .fontColor(Color.White)
  .fontWeight(500)
}

        当多个组件具有相同的样式时,若每个组件都单独设置,将会有大量的重复代码。为避免重复代码,开发者可使用@Styles或者@Extend装饰器将多条样式设置提炼成一个方法,然后直接在各组件声明的位置进行调用,这样就能完成样式的复用。

@Styles方法

      @Styles方法可定义在组件内或者全局。

  • 1. 组件内定义的@Styles方法只能在当前组件中使用,全局的@Styles方法目前只允许在当前的.ets文件中使用
  • 2. 组件内定义@Styles方法时不需要使用function关键字,全局的@Styles方法需要使用function关键字
  • 3. @Styles方法中只能包含通用属性方法和通用事件方法,例如在上面的代码中 fontSize(20).fontColor(Color.White).fontWeight(500)也是重复的,但这不是通用属性。可以定义的只能是通用属性。
  • 4. @Styles方法不支持参数
  • 5. @Styles方法可以用在任意类型的组件上。注意下面的@Extend则是使用在定制化组件上。

       将上面重复的样式代码分别提取到各自定义的@Style方法中,直接使用@Style方法
定义格式如下。

@Styles定义在Struct结构体内部
===============================================================================
  @Styles consumeImgStyle(){
    .width(30)
    .height(30)
  }

  @Styles consumeButtonStyle(){
    .width(150)
    .height(65)
    .backgroundColor(Color.Green)
  }

@Styles定义在同一个ets文件中的Struct结构体外部,必须加上 function 关键字
===============================================================================

@Styles function globalConsumeImgStyle() {
  .width(30)
  .height(30)
}

@Styles function globalConsumeButtonStyle() {
  .width(150)
  .height(65)
  .backgroundColor(Color.Green)
}

        将重复样式代码提取之后,直接使用提取后的方法,使用@Styles方法之后代码。

@Entry
@Component
struct customStyle {
  @State isOn: boolean = false;
  @State isDel: boolean = false;

  build() {
    Column({ space: 10 }) {
      Button({ type: ButtonType.Capsule, stateEffect: true }) {
        Row({ space: 10 }) {
          Image($r('app.media.icon_delete'))
            .consumeImgStyle()
          Text('删除文件')
            .fontSize(20)
            .fontColor(Color.White)
            .fontWeight(500)
        }
      }
      .fontSize(25)
      .width(150)
      .height(65)

      Button({ type: ButtonType.Capsule, stateEffect: true }) {
        Row({ space: 10 }) {
          Image($r('app.media.icon_new_folder'))
            .consumeImgStyle()
          Text('新建文件')
            .fontSize(20)
            .fontColor(Color.White)
            .fontWeight(500)
        }
      }
      .consumeButtonStyle()
      .fontSize(25)
      .onClick(() => {
        console.log('我准备开始建立文件夹');
      })
    }
    .width('100%')
    .height("100%")
    .justifyContent(FlexAlign.Center)

  }

  @Styles consumeImgStyle(){
    .width(30)
    .height(30)
  }

  @Styles consumeButtonStyle(){
    .width(150)
    .height(65)
    .backgroundColor(Color.Green)
  }
}

定义全局使用的@Styles方法需要加 function
======================================================
@Styles function globalConsumeImgStyle() {
  .width(30)
  .height(30)
}

@Styles function globalConsumeButtonStyle() {
  .width(150)
  .height(65)
  .backgroundColor(Color.Green)
}

@Extend方法

     @Extend装饰的方法同样可用于组件样式的复用,与@Styles不同的是:

  • @Extend方法只能定义在全局,使用范围目前只限于当前的.ets文件
  • @Extend方法只能用于指定类型的组件,如Button组件
  • @Extend方法可包含指定组件的专有属性方法和专有事件方法。
  • @Extend方法支持参数传递

       重复的代码

        Image 的  .width(30).height(30) 是重复的
        Button的  .fontSize(25).width(150).height(65) .backgroundColor(Color.Green)是重复的
        Text  的  .fontSize(20).fontColor(Color.White).fontWeight(500)是重复的

        将上面重复的样式代码分别提取到各自定义的@Extend方法中,直接使用@Extend方法
定义格式如下。

@Extend(Image) function consumeImgStyle() {
  .width(30)
  .height(30)
}

@Extend(Button) function consumeButtonStyle(color: Color) {
  .fontSize(25)
  .width(150)
  .height(65)
  .backgroundColor(color)

}

@Extend(Text) function consumeTextStyle(color: Color, size: number, weight: number) {
  .fontSize(size)
  .fontColor(color)
  .fontWeight(weight)
}

    将重复样式代码提取之后,直接使用提取后的方法,使用@Extend方法之后代码。

@Entry
@Component
struct customStyle {
  @State isOn: boolean = false;
  @State isDel: boolean = false;

  build() {
    Column({ space: 10 }) {
      Button({ type: ButtonType.Capsule, stateEffect: true }) {
        Row({ space: 10 }) {
          Image($r('app.media.icon_new_folder'))
            .consumeImgStyle()
          Text('新建文件')
            .consumeTextStyle(Color.White, 20, 500)
        }
      }
      .consumeButtonStyle(Color.Green)
      .onClick(() => {
        console.log('我准备开始建立文件夹');
      })

      Button({ type: ButtonType.Capsule, stateEffect: true }) {
        Row({ space: 10 }) {
          Image($r('app.media.icon_new_folder'))
            .consumeImgStyle()
          Text('新建文件')
            .consumeTextStyle(Color.White, 20, 500)
        }
      }
      .consumeButtonStyle(Color.Green)
      .onClick(() => {
        console.log('我准备开始建立文件夹');
      })
    }
    .width('100%')
    .height("100%")
    .justifyContent(FlexAlign.Center)
  }
}


@Extend(Image) function consumeImgStyle() {
  .width(30)
  .height(30)
}

@Extend(Button) function consumeButtonStyle(color: Color) {
  .fontSize(25)
  .width(150)
  .height(65)
  .backgroundColor(color)

}

@Extend(Text) function consumeTextStyle(color: Color, size: number, weight: number) {
  .fontSize(size)
  .fontColor(color)
  .fontWeight(weight)
}

UI结构复用


       当页面有多个相同的UI结构时,若每个都单独声明,同样会有大量重复的代码。为避免重复代码,可以将相同的UI结构提炼为一个自定义组件,完成UI结构的复用。
       除此之外,ArkTS还提供了一种更轻量的UI结构复用机制@Builder方法,开发者可以将重复使用的UI元素抽象成一个@Builder方法,该方法可在build()方法中调用多次,以完成UI结构的复用。 

     @Builder装饰的方法同样可用于组件接结构的复用,

  • @Builder方法可以定义在组件内或者全局
  • @Builder方法定义的是组件内的@Builder方法,可通过this访问当前组件的属性和方法,而全局的@Builder方法则不能
  • @Builder方法定义的是组件内的@Builder方法只能用于当前组件,全局的@Builder方法导出(export)后,可用于整个应用。
@Entry
@Component
struct BuilderUI {
  @State isOn: boolean = false;
  @State isDel: boolean = false;

  build() {
    Column({ space: 10 }) {
      this.builderButton($r('app.media.icon_delete'), '删除文件', () => {
        console.log('我准备开始删除文件夹');
      })
      globalBuilderButton($r('app.media.icon_new_folder'), '新建文件', () => {
        console.log('我准备开始建立文件夹');
      })
    }
    .width('100%')
    .height("100%")
    .justifyContent(FlexAlign.Center)
  }

  @Builder builderButton(icon: Resource, text: string, callback: () => void) {
    Button({ type: ButtonType.Capsule, stateEffect: true }) {
      Row({ space: 10 }) {
        Image(icon)
          .width(30)
          .height(30)
        Text(text)
          .fontSize(20)
          .fontColor(Color.White)
          .fontWeight(500)
          .backgroundColor(Color.Green)
      }
    }
    .fontSize(25)
    .width(150)
    .height(65)
    .onClick(callback)
  }
}

@Builder function globalBuilderButton(icon: Resource, text: string, callback: () => void) {
  Button({ type: ButtonType.Capsule, stateEffect: true }) {
    Row({ space: 10 }) {
      Image(icon)
        .width(30)
        .height(30)
      Text(text)
        .fontSize(20)
        .fontColor(Color.White)
        .fontWeight(500)
        .backgroundColor(Color.Green)
    }
  }
  .fontSize(25)
  .width(150)
  .height(65)
  .onClick(callback)
}
  • @Builder方法支持参数传递规则,分为按值传递和按引用传递。带有花括号的{ } 为引用传递,其余为值传递。
  • 按引用传递时,若传递的参数为状态变量,则状态变量的变化将会触发@Builder方法内部UI的刷新,而按值传递时则不会。
@Entry
@Component
struct BuilderUI {
  @State status: number = 10;

  build() {
    Column({ space: 10 }) {
      valueText(this.status)
      referenceText({status:this.status})

      Row({ space: 10 }) {
        Button('值传递 - 1')
          .consumeButtonStyle()
          .onClick(() => {
            this.status--
          })
        Button('引用传递 + 1')
          .consumeButtonStyle()
          .onClick(() => {
            this.status++
          })
      }
    }
    .width('100%')
    .height("100%")
    .justifyContent(FlexAlign.Center)
  }


}

@Builder function valueText(count: number) {
  Text(`进行值传递:  ${count}`).fontSize(30).fontWeight(600)
}

@Builder function referenceText(count: {status:number}) {
  Text(`进行引用传递: ${count.status}`).fontSize(30).fontWeight(600)
  Text('引用参数传递的是状态变量status,这里内部UI会刷新')
}

@Extend(Button) function consumeButtonStyle() {
  .fontSize(20)
  .width(140)
  .height(65)
}