AI 摘要

文章系统梳理 HarmonyOS 4.0 ArkTS 样式处理:链式与枚举语法、vp/px/fp/lpx 像素单位、layoutWeight 与 aspectRatio 适配;@Styles 全局/组件内复用通用样式,@Extend 带参扩展原生组件;stateStyles 依据 focused、pressed、disabled 等状态动态切换样式,并可与 @Styles 组合实现复用,覆盖开发中 90% 样式场景。

样式语法

链式

参考文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/arkts-declarative-ui-description-0000001524416537-V2#section43320720411

属性方法以链式调用的方式配置系统组件的样式和其他属性,建议每个属性方法单独写一行。


示例:链式调用样式属性

Index 组件(src/main/ets/pages/Index.ets):

Text('Hello HarmonyOS')
  .backgroundColor('red')   // 背景颜色
  .fontSize(50)   // 字体大小
  .width('100%')   // 宽度
  .height(100)  // 高度

枚举

参考文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V2/ts-appendix-enums-0000001478061741-V2

对于系统组件,ArkUI 还为其属性预定义了一些枚举类型。


示例:设置枚举样式属性

Index 组件(src/main/ets/pages/Index.ets):

Text('Hello HarmonyOS')
  .backgroundColor(Color.Blue)  // 背景颜色
  .textAlign(TextAlign.Center)   // 对齐方式
  .fontColor(Color.White)  // 文本颜色

样式适配

像素单位

参考文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V2/ts-pixel-units-0000001478341189-V2

HarmonyOS 为开发者提供 4 种像素单位,ArkTS 采用 vp 为基准数据单位。

名称描述
px物理像素单位
vp虚拟像素单位,根据屏幕像素密度转换为屏幕物理像素
fp字体像素
lpx逻辑像素单位,为实际屏幕宽度与逻辑宽度的比值

layoutWeight

参考文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V2/ts-universal-attributes-size-0000001428061700-V2

layoutWeigh 在父容器尺寸确定时,设置了 layoutWeight 属性的子元素与兄弟元素占主轴尺寸按照权重进行分配,忽略元素本身尺寸设置,表示自适应占满剩余空间。


示例:layoutWeigh

Index 组件(src/main/ets/pages/Index.ets):

Row() {
  Text('请输入手机号:')

  // TextInput占满行组件剩余的宽度
  TextInput()
    .layoutWeight(1)
}.padding(20)

示例效果:


aspectRatio

参考文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V2/ts-universal-attributes-layout-constraints-0000001427744784-V2

aspectRadio 用于指定当前组件的宽高比。


示例:aspectRatio

Index 组件(src/main/ets/pages/Index.ets):

Image($r('app.media.avatar'))
  .width(96)
  .aspectRatio(1)

示例效果:


复用样式

@Styles 复用样式

参考文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/arkts-style-0000001473856690-V2

在开发过程中会出现大量代码在进行重复样式设置,@Styles 可以帮我们进行样式复用。

  • 当前 @Styles 仅支持通用属性复用和通用事件复用。
  • 支持全局定义和组件内定义,同时存在组件内覆盖全局生效。

全局定义复用样式:

  • 写在 struct 声明的组件外。
  • 用 function 关键字声明。
  • 目前仅支持当前的页面组件有效。

示例:全局定义复用样式

Index 组件(src/main/ets/pages/Index.ets):

// 全局定义复用样式
@Styles
function myStyle() {
  // 支持统一样式
  .backgroundColor('green')
  .margin({bottom: 10})
  // 支持通用事件
  .onClick(() =>{
    console.log('click')
  })
}

@Entry
@Component
struct Index {
  build() {
    Row() {
      Column({space : 10}) {
        Button('Count--')
          // 调用自定义样式
          .myStyle()

        Button('Count++')
          // 调用自定义样式
          .myStyle()
      }
      .alignItems(HorizontalAlign.Start)
      .width('100%')
    }
    .height("100%")
    .padding(20)
    .alignItems(VerticalAlign.Top)
  }
}

示例效果:


组件内定义复用样式:

  • 写在 struct 组件内。
  • 对当前的组件有效,覆盖全局同名的复用样式。
  • 不需要 function 关键字。

示例:组件内定义复用样式

Index 组件(src/main/ets/pages/Index.ets):

@Entry
@Component
struct Index {
  // 组件内定义复用样式
  @Styles
  myStyle() {
    .backgroundColor('red')
    .margin({bottom: 10})
    .onClick(() =>{
      console.log('click')
    })
  }

  build() {
    Row() {
      Column({space : 10}) {
        Button('Count--')
          // 调用自定义样式
          .myStyle()

        Button('Count++')
          // 调用自定义样式
          .myStyle()
      }
      .alignItems(HorizontalAlign.Start)
      .width('100%')
    }
    .height("100%")
    .padding(20)
    .alignItems(VerticalAlign.Top)
  }
}

示例效果:


@Extend 复用样式

参考文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/arkts-extend-0000001473696678-V2

@Extend 用于扩展指定的原生组件样式,通过传参提供更灵活的样式复用。

  • 使用 @Extend 装饰器修饰的函数只能是全局函数。
  • @Extend 必须指定扩展的组件,且复用样式只针对该组件有效。
  • 复用样式函数可以进行传参,如果参数是状态变量,状态更新后会刷新 UI。
  • 参数可以是一个函数,实现复用事件且可处理不同逻辑。

示例:扩展原生组件样式

Index 组件(src/main/ets/pages/Index.ets):

@Extend(Text) // 指定扩展Text组件
function myStyle(color:string, cb:()=>void) {
  .backgroundColor(color)
  .padding(10)
  .border({radius : 6})
  .onClick(cb)
}

@Entry
@Component
struct Index {
  build() {
    Row() {
      Column({space : 10}) {
        Text('Text按钮1')
          .myStyle('green', () => {
            console.log('Text1 Click')
          })

        Text('Text按钮2')
          .myStyle('red', () => {
            console.log('Text2 Click')
          })
      }
      .alignItems(HorizontalAlign.Center)
      .width('100%')
    }
    .height("100%")
    .padding(20)
    .alignItems(VerticalAlign.Center)
  }
}

示例效果:


复用样式总结

作用不同:

  • @Styles 用来定义可复用的样式和事件,只支持通用样式和通用事件。
  • @Extend 用来扩展指定的原生组件的样式和事件。

定义方式不同:

  • @Styles 支持全局定义和组件内定义。
  • @Extend 只支持全局定义。

传参方式不同:

  • @Styles 不支持传参。
  • @Extend 支持传参,参数可以是一个函数。

多态样式

stateStyles

参考文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/arkts-statestyles-0000001482592098-V2

stateStyles 可以依据组件的内部状态的不同,快速设置不同样式。

ArkUI 提供四种状态:focused 获焦态、normal 正常态、pressed 按压态、disabled 禁用态。


示例:设置不同组件不同状态下的样式

Index 组件(src/main/ets/pages/Index.ets):

@Entry
@Component
struct Index {
  build() {
    Row() {
      Column({space : 10}) {
        // 可以获得获焦态的第一个元素会自动获焦,故该TextInput默认获得焦点
        TextInput()
          .stateStyles({
            // 组件获焦态的样式
            focused: {
              .backgroundColor('blue')
            }
          })

        Button('按钮')
          .stateStyles({
            // 组件正常态时的样式
            normal: {
              .backgroundColor('green')
            },
            // 组件按压态的样式
            pressed: {
              .backgroundColor('red')
            }
          })

        TextInput()
          .stateStyles({
            // 当从正常态转换到禁用态时,样式生效
            disabled: {
              .backgroundColor('black')
            }
          })
      }
      .alignItems(HorizontalAlign.Center)
      .width('100%')
    }
    .height("100%")
    .padding(20)
    .alignItems(VerticalAlign.Center)
  }
}

示例效果:


stateStyles + @Styles

stateStyles 可以结合 @Styles 实现样式复用。


示例:多态样式实现样式复用

Index 组件(src/main/ets/pages/Index.ets):

@Entry
@Component
struct Index {
  @Styles
  greenColor() {
    .backgroundColor('green')
  }

  build() {
    Row() {
      Column({space : 10}) {
        Button('按钮')
          .stateStyles({
            pressed: this.greenColor
          })
      }
      .alignItems(HorizontalAlign.Center)
      .width('100%')
    }
    .height("100%")
    .padding(20)
    .alignItems(VerticalAlign.Center)
  }
}