鸿蒙UI开发快速入门 —— part05:组件的样式复用

1、 为什么要样式复用?

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

为此,鸿蒙推出了可以提炼公共样式进行复用的装饰器@Styles;

2、@Styles装饰器

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

有几点需要特别注意:

  • 目前@Styles仅支持通用属性和通用事件。

    什么是通用属性和通用事件?

通用属性指的是所有组件都具备的属性,例如:背景色、边框、宽高、圆角、字体等。通用事件指的是所有组件都具备的用户交互事件,例如:点击事件、焦点事件、长按事件等。

  • @Styles方法不支持参数,反例如下
// 反例:@Styles不支持参数
@Styles function globalFancy (value: number) {
  .width(value)
}

  • @Styles可以定义在组件内或全局,在全局定义时需在方法名前面添加function关键字,组件内定义时则不需要添加function关键字。
// 全局【只能在当前文件内使用,不支持export。】
@Styles function functionName() { ... }

// 在组件内
@Component
struct FancyUse {
  @Styles fancy() {
    .height(100)
  }
}

  • 定义在组件内的@Styles可以通过this访问组件的常量和状态变量,并可以在@Styles里通过事件来改变状态变量的值,示例如下:
@Component
struct FancyUse {
  @State heightValue: number = 100
  @Styles fancy() {
    .height(this.heightValue)
    .backgroundColor(Color.Yellow)
    .onClick(() => {
      this.heightValue = 200
    })
  }
}

  • 组件内@Styles的优先级高于全局@Styles。

    框架优先找当前组件内的@Styles,如果找不到,则会全局查找。

3、@Styles装饰器Demo

以下示例中演示了组件内@Styles和全局@Styles的用法。

// 定义在全局的@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)
    }
  }
}

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
@Component
struct 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 FancyUse {
  @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 —— 多态样式

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

图片

此时,我们就需要用到“多态样式”了。

多态样式是属性方法,可以根据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、结语

至此,我们将UI开发的组件样式相关的介绍完毕了。回顾一下,涉及到了

  • @Builder UI描述的概念以及如何添加UI组件;

  • UI和界面相关的的生命周期;

  • UI的逻辑复用和样式复用;

至此,我们已经可以编写一个静态页面了,但还不够,我们未来将继续学习状态管理和条件渲染。

有了状态管理和条件渲染,我们的界面就能随着用户的交互而发生变化。

下一个阶段,请持续关注:“鸿蒙UI开发快速入门 —— part06”

posted @   鸿蒙自习室  阅读(70)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· [翻译] 为什么 Tracebit 用 C# 开发
· 腾讯ima接入deepseek-r1,借用别人脑子用用成真了~
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· DeepSeek崛起:程序员“饭碗”被抢,还是职业进化新起点?
· RFID实践——.NET IoT程序读取高频RFID卡/标签
点击右上角即可分享
微信分享提示