HarmonyOS Next UI开发入门 ——组件的样式复用(05)

287 阅读4分钟

环境及工具

HarmonyOS 5.0.0 Release
DevEcoStudio
适用于HarmonyOS Next原生开发

1、 为什么要样式复用

如果每个组件的样式都需要单独设置,在开发过程中会出现大量代码在进行重复样式设置,虽然可以复制粘贴,但为了代码简洁性和后续方便维护,样式的复用就很有必要了。
为此,鸿蒙推出了可以提炼公共样式进行复用的装饰器@Styles;

2、@Styles装饰器

@Styles装饰器可以将多条样式设置提炼成一个方法,直接在组件声明的位置调用。通过@Styles装饰器可以快速定义并复用自定义样式。用于快速定义并复用自定义样式。
有几点需要特别注意:

  • 目前@Styles仅支持通用属性和通用事件。
  • @Styles方法不支持参数
  • @Styles可以定义在组件内或全局,在全局定义时需在方法名前面添加function关键字,组件内定义时则不需要添加function关键字。
  • 定义在组件内的@Styles可以通过this访问组件的常量和状态变量,并可以在@Styles里通过事件来改变状态变量的值
  • 组件内@Styles的优先级高于全局@Styles。 框架优先找当前组件内的@Styles,如果找不到,则会全局查找。

3、@Styles装饰器Demo

// 定义在全局的@Styles封装的样式
@Styles
function globalFancy() {
  .width(150)
  .height(100)
  .backgroundColor(Color.Pink)
}

@Entry
@Component
struct FancyUse {
  @State heightValue: number = 100

  // 定义在组件内的@Styles封装的样式
  @Styles
  fancy() {
    .width(200)
    .height(this.heightValue)
    .backgroundColor(Color.Yellow)
    .onClick(() => {
      this.heightValue = 200
    })
  }

  build() {
    Column({ space: 10 }) {
      // 使用全局的@Styles封装的样式
      Text('FancyA')
        .globalFancy()
        .fontSize(30)
      // 使用组件内的@Styles封装的样式
      Text('FancyB')
        .fancy()
        .fontSize(30)
    }
  }
}

101.png

4、使用@Extend扩展已有组件样式

在实际开发中,我们可能会引用一些三方组件或者原生组件,同时,我们可能会针对这些三方组件做一些样式设置。此时,我们可以使用@Extend装饰器,它用于扩展已有组件样式。

使用方法很简单,代码示例如下:

@Extend(UIComponentName) function functionName {   
    // ... 
}

使用@Extend有几点需要注意:

  • 和@Styles不同,@Extend仅支持定义在全局,不支持在组件内部定义,而且只在定义的文件上下文中生效。
  • 和@Styles不同,@Extend支持封装指定组件的私有属性、私有事件和自身定义的全局方法,示例代码如下:
// @Extend(Text)可以支持Text的私有属性fontColor
@Extend(Text) function fancy () {  
    .fontColor(Color.Red)
}
// superFancyText可以调用预定义的fancy
@Extend(Text) function superFancyText(size:number) {    
    .fontSize(size)    
    .fancy()
}
  • @Extend装饰的方法支持参数,开发者可以在调用时传递参数,调用遵循TS方法传值调用。
// xxx.ets
@Extend(Text) function fancy (fontSize: number) {  
    .fontColor(Color.Red)  
    .fontSize(fontSize)}
@Entry
@Componentstruct FancyUse {  
    build() {    
        Row({ space: 10 }) {      
            Text('Fancy')        
                .fancy(16)      
            Text('Fancy')        
                .fancy(24)    
        }  
    }
}
  • @Extend装饰的方法的参数可以为function,作为Event事件的句柄。
@Extend(Text) function makeMeClick(onClick: () => void) {
  .backgroundColor(Color.Blue)
  .onClick(onClick)}
@Entry
@Component
struct Index {
  @State label: string = 'Hello World';
  onClickHandler() {
    this.label = 'Hello ArkUI';
  }
  build() {
    Row({ space: 10 }) {
      Text(`${this.label}`)
        .makeMeClick(this.onClickHandler.bind(this))
    }
  }
}
  • @Extend的参数可以为状态变量,当状态变量改变时,UI可以正常的被刷新渲染。
@Extend(Text) function fancy (fontSize: number) {
  .fontColor(Color.Red)
  .fontSize(fontSize)}
@Entry
@Component
struct FancyUse {
  @State fontSizeValue: number = 20
  build() {
    Row({ space: 10 }) {
      Text('Fancy')
        .fancy(this.fontSizeValue)
        .onClick(() => {
          this.fontSizeValue = 30
        })
    }
  }
}

5、@Extend装饰器Demo

以下示例声明了3个Text组件,每个Text组件均设置了fontStyle、fontWeight和backgroundColor样式【未使用样式复用】。

@Entry
@Component
struct FancyUse {
  @State label: string = 'Hello World'
  build() {
    Row({ space: 10 }) {
      Text(`${this.label}`)
        .fontStyle(FontStyle.Italic)
        .fontWeight(100)
        .backgroundColor(Color.Blue)
      Text(`${this.label}`)
        .fontStyle(FontStyle.Italic)
        .fontWeight(200)
        .backgroundColor(Color.Pink)
      Text(`${this.label}`)
        .fontStyle(FontStyle.Italic)
        .fontWeight(300)
        .backgroundColor(Color.Orange)
    }
    .margin('20%')
  }
}

我们尝试将公用的属性设置抽离,@Extend将样式组合复用,示例如下。

@Extend(Text) function fancyText(weightValue: number, color: Color) {  
    .fontStyle(FontStyle.Italic)  
    .fontWeight(weightValue)  
    .backgroundColor(color)
}
@Entry
@Component
struct FancyUse { 
  @State label: string = 'Hello World'
  build() {    
    Row({ space: 10 }) {      
      Text(`${this.label}`)        
        .fancyText(100, Color.Blue)      
      Text(`${this.label}`)        
        .fancyText(200, Color.Pink)      
      Text(`${this.label}`)        
        .fancyText(300, Color.Orange)    
  }
  .margin('20%')  
  }
}

可以看到,之前重复的fontStyle、fontWeight、backgroundColor都被fancyText一个属性替换了。

6、One More Thing —— 多态样式

在实际开发中,我们一般会遇到这种情况,组件在不同的状态下,显示不同的样式,例如:默认是一个红色圆角按钮,按下后变成一个黑色按钮,示意图如下:

102.gif

此时,我们就需要用到“多态样式”了。 多态样式是属性方法,可以根据UI内部状态来设置样式,类似于css伪类,但语法不同。ArkUI提供以下四种状态:
  • focused:获焦态。
  • normal:正常态。
  • pressed:按压态。
  • disabled:不可用态。

Demo中动图展示了stateStyles最基本的使用场景。Button1处于第一个组件,Button2处于第二个组件。按压时显示为pressed态指定的黑色。使用Tab键走焦,先是Button1获焦并显示为focus态指定的粉色。当Button2获焦的时候,Button2显示为focus态指定的粉色,Button1失焦显示normal态指定的红色。代码如下:

@Entry
@Component
struct StateStylesSample {
  build() {
    Column() {
      Button('Button1')
        .stateStyles({
          focused: {
            .backgroundColor(Color.Pink)
          },
          pressed: {
            .backgroundColor(Color.Black)
          },
          normal: {
            .backgroundColor(Color.Red)
          }
        })
        .margin(20)
      Button('Button2')
        .stateStyles({
          focused: {
            .backgroundColor(Color.Pink)
          },
          pressed: {
            .backgroundColor(Color.Black)
          },
          normal: {
            .backgroundColor(Color.Red)
          }
        })
    }
    .margin('30%')
  }
}

7、总结

至此,我们已经可以编写一个静态页面了

小猫力量.png