鸿蒙应用开发-组件样式

样式处理

1. 样式-语法(链式&枚举)

ArkTS以声明方式组合和扩展组件来描述应用程序的UI 同时还提供了基本的属性、事件和子组件配置方法,帮助开发者实现应用交互逻辑。

1.样式属性

  • 属性方法以 . 链式调用的方式配置系统组件的样式和其他属性,建议每个属性方法单独写一行。
java 复制代码
@Entry
@Component
struct Index {
  build() {
    Text('演示')
      .backgroundColor('red')
      .fontSize(50)
      .width('100%')
      .height(100)
  }
}

2.枚举值

  • 对于系统组件,ArkUI还为其属性预定义了一些枚举类型。
java 复制代码
@Entry
@Component
struct Index {
  build() {
    Text('演示')
     .fontSize(50)
      .width('100%')
      .height(100)
      .backgroundColor(Color.Blue)
      .textAlign(TextAlign.Center)
      .fontColor(Color.White)
  }
}
  • 样式相关属性通过链式函数的方式进行设置
  • 如果类型是枚举的,通过枚举传入对应的值
2. 样式-单位 vp 和适配
  1. vp 是什么?virtual pixel
  • 屏幕密度相关像素,根据屏幕像素密度转换为屏幕物理像素,当数值不带单位时,默认单位 vp;在实际宽度为1440物理像素的屏幕上,1vp 约等于 3px(物理像素)

  • 上图的意思是,使用这个单位在不同屏幕物理分辨率的实际尺寸一致(A设备1英寸,B设备1英寸)。

2.之前 vwremrpx 相对于屏幕宽度的单位,可以实现等比例适配,vp 可以吗?

java 复制代码
import promptAction from '@ohos.promptAction'

@Entry
@Component
struct Index {
  build() {
    Text('演示')
      .width('100%')
      .backgroundColor('red')
      .onAreaChange((oldArea, newArea) => {
        promptAction.showToast({
          // 1. onAreaChange改变尺寸后会触发
          // 2. newArea为现在元素尺寸
          message: newArea.width.toString()
        })
      })
  }
}

我们发现:不同的设备屏幕的宽度 vp 是不一致的,那怎么适配呢?

  1. 根据官方的文档,结合自己的理解,采用:伸缩布局,网格系统,栅格系统进行布局适配。

伸缩 layoutWeight(flex: number)占剩余空间多少份,可以理解成CSS的 flex: 1

java 复制代码
@Entry
@Component
struct Index {
  build() {
    Row(){
      Text('left')
        .layoutWeight(1)
        .backgroundColor('red')
      Text('right')
        .layoutWeight(2)
        .backgroundColor('green')
    }
    .width('100%')
  }
}

等比例,设置元素宽高比 aspectRatio(ratio: number)

java 复制代码
@Entry
@Component
struct Index {
  build() {
    Text('left')
      .width('50%')
        // 宽高比例
      .aspectRatio(1)
      .backgroundColor('red')
  }
}
  • vp 是鸿蒙默认单位,和屏幕像素有关,最终表现视觉大小在任何设备一致
  • 鸿蒙一般以伸缩 layoutWeight、网格、栅格进行布局适配,如要等比例缩放可以设置高宽比 aspectRatio
    📕📕📕 案例→实现知乎评论回复-评论区域

设计稿一般是1080px:(这里没有设计稿,提供了一些尺寸)

  • Nav
    • 左侧返回按钮24vp高宽背景颜色#f5f5f5,图标12vp尺寸颜色#848484
    • 标题18vp
  • Comment
    • 头像尺寸32vp高宽,右侧间距10vp
    • 标题15vp,颜色默认
    • 内容16vp,颜色#565656
    • 底部12vp,颜色#c3c4c5
java 复制代码
@Entry
@Component
struct Index {
  build() {
    Column(){
      // 导航
      Row(){
        Row(){
          Image($r('app.media.ic_public_arrow_left'))
            .width(16)
            .aspectRatio(1)
            // svg 图标可以使用填充颜色
            // .fillColor('red')
        }
        .width(24)
        .aspectRatio(1)
        .backgroundColor('#f5f5f5')
        .borderRadius(12)
        .justifyContent(FlexAlign.Center)
        .margin({ left: 16 })

        Text('评论回复')
          .layoutWeight(1)
          .textAlign(TextAlign.Center)
          .padding({ right: 40 })
      }
      .height(40)
      .border({ width: { bottom: 0.5 }, color: '#e4e4e4' })
      // 评论
      Row(){
        Image($r('app.media.avatar'))
          .width(32)
          .aspectRatio(1)
          .borderRadius(16)
        Column({ space: 5 }){
          Text('周杰伦')
            .width('100%')
            .fontWeight(FontWeight.Bold)
            .fontSize(15)
          Text('大理石能雕刻出肌肉和皮肤的质感,那个年代的工匠好牛啊')
            .width('100%')
          Row(){
            Text('10-21 · IP属地北京')
              .fontSize(12)
              .fontColor('#c3c4c5')
            Row({ space: 4 }){
              Image($r('app.media.ic_public_heart'))
                .width(14)
                .aspectRatio(1)
                .fillColor('#c3c4c5')
              Text('100')
                .fontSize(12)
                .fontColor('#c3c4c5')
            }
          }
          .width('100%')
          .justifyContent(FlexAlign.SpaceBetween)
        }
        .layoutWeight(1)
        .padding({ left: 10 })
      }
      .padding(15)
      .alignItems(VerticalAlign.Top)
    }
  }
}

样式-@Styles 复用

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

  • 当前 @Styles 仅支持 通用属性 和 通用事件。
  • 支持 全局 定义和 组件内 定义,同时存在组件内覆盖全局生效。
java 复制代码
// 全局
@Styles 
function functionName() { ... }

@Entry
@Component
sturt Index{
  // 组件内
  @Styles 
  functionName() { ... }

  build() {
    Text('Text')
      .functionName()
  }
}

案例:文字和按钮相同背景,点击+1

  • 全局
java 复制代码
@Styles function sameStyle() {
  .backgroundColor(Color.Green)
  .onClick(() => {
    this.count++
  })
}

@Entry
@Component
struct Index {
  @State
  count: number = 10

  build() {
    Column() {
      Text(this.count.toString())
        .width(100)
        .height(50)
        .margin({ bottom: 10 })
        .textAlign(TextAlign.Center)
        .sameStyle()

      Button('+1')
        .sameStyle()
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}
  • 组件内
java 复制代码
@Entry
@Component
struct Index {
  @State
  count: number = 10

  // 不需要 `function` 关键字,覆盖全局
  @Styles
  sameStyle (){
    .backgroundColor(Color.Pink)
    .onClick(() => {
      this.count += 10
    })
  }

  build() {
    Column() {
      Text(this.count.toString())
        .width(100)
        .height(50)
        .margin({ bottom: 10 })
        .textAlign(TextAlign.Center)
        .sameStyle()

      Button('+1')
        .sameStyle()
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}

📕📕📕 练习案例-登录表单-样式优化

java 复制代码
import promptAction from '@ohos.promptAction'
@Entry
@Component
struct Index {

  @State
  mobile: string = ''
  @State
  code: string = ''


  @Styles
  inputStyle () {
    .border({ width: 1, color: Color.Gray })
    .layoutWeight(1)
    .margin({ left: 10, bottom: 10, top: 10 })
    .backgroundColor(Color.White)
  }

  build() {
    Column(){
      Row(){
        Text('手机号')
        TextInput({ text: this.mobile })
          .inputStyle()
          .onChange((value)=>this.mobile = value)
      }
      Row(){
        Text('验证码')
        TextInput({ text: this.code })
          .inputStyle()
          .onChange((value)=>this.code = value)
      }
      Row({ space: 15 }){
        Button('重置')
          .backgroundColor('#ccc')
          .onClick(()=>{
            this.mobile = ''
            this.code = ''
          })
        Button('登录')
          .onClick(()=>{
            if (this.mobile && this.code) {
              promptAction.showToast({ message: `${this.mobile} 登录成功` })
            } else {
              promptAction.showToast({ message: `请输入手机号或验证码` })
            }
          })
      }
    }
    .padding({ left: 15, right: 15 })
  }
}

样式-@Extends 复用

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

  • 使用 @Extend 装饰器修饰的函数只能是 全局
  • 函数可以进行 传参,如果参数是状态变量,状态更新后会刷新UI
  • 且参数可以是一个函数,实现复用事件且可处理不同逻辑
java 复制代码
// 全局  原生组件                     参数
//  ↓     ↓                          ↓ 
@Extend(Text) function functionName(w: number) { 
  .width(w)
}

需求:把 Text 改成按钮样式,且绑定 click 事件执行不同逻辑

java 复制代码
import promptAction from '@ohos.promptAction'
@Extend(Text) function myClick(color: string, cb: () => void) {
  .backgroundColor(color)
  .width(100)
  .height(50)
  .textAlign(TextAlign.Center)
  .borderRadius(25)
  .onClick(() => cb())
}

@Entry
@Component
struct Other {
  @State
  color: string = '#ccc'

  build() {
    Column({ space: 20 }) {
      Text('Text1')
        .myClick(this.color, () => {
          this.color = '#069'
        })
      Text('Text2')
        .myClick('green', () => {
          promptAction.showToast({ message: '做其他事~' })
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

样式-多态

stateStyles 是属性方法,可以根据UI内部状态来设置样式,类似于 css 伪类,但语法不同。ArkUI 提供以下四种状态:

  • focused:获焦态。

  • normal:正常态。

  • pressed:按压态。

  • disabled:不可用态。

java 复制代码
import promptAction from '@ohos.promptAction'

// 胶囊按钮
@Extend(Text)
function capsule(){
  .height(40)
  .borderRadius(20)
  .backgroundColor(Color.Gray)
  .padding({ left: 15, right: 15 })
  .margin({ bottom: 15 })
}

@Entry
@Component
struct Index {
  @State
  disabled: boolean = false
  @State
  focused: boolean = false

  build() {
    Column() {

      // Button TextInput 默认开启获取焦点,页面中默认第一个这样的元素获取焦点
      // Button 比较多限制,一个是默认开启获取焦点能看,二是禁用状态下样式无法修改
      // Button('Button').focusable(false)

      Text('toggle disabled:' + this.disabled)
        .capsule()
        .onClick(()=>{
          this.disabled = !this.disabled
        })
      Text('toggle focused:' + this.focused)
        .capsule()
        .onClick(()=>{
          this.focused = !this.focused
        })
      Text('clickMe')
        .capsule()
        .enabled(!this.disabled)
        .focusable(this.focused)
        .onClick(() => {
          promptAction.showToast({ message: 'click' })
        })
        .fontColor('#fff')
        .stateStyles({
          normal: {
            .backgroundColor(Color.Blue)
          },
          focused: {
            .backgroundColor(Color.Red)
          },
          disabled: {
            .backgroundColor(Color.Black)
          },
          pressed: {
            .backgroundColor(Color.Orange)
          }
        })
    }
  }
}
  • 使用比较多的应该是 norma
    pressed 结合下的按压效果
  • enabled(true|false) 开启|禁用focusable(true|false) 开启获取焦点能力|关闭

注意:

  • 页面初始化的时候,默认第一个能获取焦点的元素,会自动获取焦点

👀关注公众号:Android老皮!!!欢迎大家来找我探讨交流👀

相关推荐
鸿蒙自习室31 分钟前
鸿蒙动画开发06——打断动画
ui·华为·harmonyos·鸿蒙
Allen zhu2 小时前
【PowerHarmony】电鸿蒙学习记录-编写helloworld!
学习·华为·harmonyos
Jiaberrr2 小时前
开启鸿蒙开发之旅:交互——点击事件
前端·华为·交互·harmonyos·鸿蒙
SuperHeroWu73 小时前
【HarmonyOS】应用实现读取剪切板内容(安全控件和自读取)
安全·华为·harmonyos·鸿蒙·权限·剪切板·systepasteboard
lqj_本人4 小时前
鸿蒙next版开发:相机开发-元数据(ArkTS)
数码相机·华为·harmonyos
SameX5 小时前
初识 HarmonyOS Next 的分布式管理:设备发现与认证
前端·harmonyos
楚疏笃6 小时前
鸿蒙学习生态应用开发能力全景图-开发者支持平台(5)
学习·华为·harmonyos
鸿蒙程序媛6 小时前
【鸿蒙开发】第五章 ArkTS语言UI范式-UI相关的装饰器
harmonyos
亦世凡华、13 小时前
【HarmonyOS】鸿蒙系统在租房项目中的项目实战(一)
经验分享·harmonyos·harmonyos next·arkui·鸿蒙开发
Swift社区16 小时前
如何构建安全可靠的 HarmonyOS 应用
harmonyos·arkts·arkui