Harmony学习之声明式UI开发

Harmony学习之声明式UI开发

一、场景引入

小明在上一篇文章中掌握了ArkTS语言基础,现在他想要构建一个用户登录界面,包含Logo、用户名输入框、密码输入框和登录按钮。他需要了解如何将这些组件按照设计稿进行布局,并设置合适的样式。本篇文章将系统讲解HarmonyOS声明式UI开发的核心概念、常用组件和布局系统,帮助小明快速构建美观的用户界面。

二、声明式UI核心概念

1. 声明式与命令式对比

命令式开发需要逐步指示每个UI元素的创建、属性设置和关系建立:

// 命令式伪代码
const textView = new TextView();
textView.setText("点击计数:0");
button.setOnClickListener(() => {
  textView.setText(`点击计数:${++count}`);
});

声明式开发采用反向控制逻辑,UI随数据自动更新:

@Component
struct CounterPage {
  @State count: number = 0;

  build() {
    Column() {
      Text(`点击计数:${this.count}`)
      Button('增加')
        .onClick(() => {
          this.count++;
        })
    }
  }
}

2. 核心三要素

要素 作用 典型应用场景
数据驱动 UI随数据自动更新 实时数据展示、表单输入
组件化 高内聚、可复用单元 公共控件封装、业务模块拆分
状态管理 跨组件数据同步机制 全局配置、用户登录状态

三、常用基础组件

1. Text文本组件

Text组件用于显示文本内容,支持丰富的样式设置:

Text('Hello HarmonyOS')
  .fontSize(20)                // 字体大小
  .fontColor('#FF0000')        // 字体颜色
  .fontWeight(FontWeight.Bold) // 字体粗细
  .fontStyle(FontStyle.Italic) // 字体样式
  .textAlign(TextAlign.Center) // 文本对齐
  .maxLines(1)               // 最大行数
  .textOverflow({overflow: TextOverflow.Ellipsis}) // 溢出处理

文本对齐方式

  • TextAlign.Start:左对齐(默认)
  • TextAlign.Center:居中对齐
  • TextAlign.End:右对齐

文本溢出处理

  • TextOverflow.Clip:超出部分裁剪
  • TextOverflow.Ellipsis:超出部分显示省略号

2. Image图片组件

Image组件用于显示图片,支持本地和网络图片:

// 本地图片
Image($r('app.media.logo'))
  .width(100)
  .height(100)
  .objectFit(ImageFit.Contain) // 图片填充模式

// 网络图片
Image('https://example.com/image.jpg')
  .width(200)
  .height(200)

图片填充模式

  • ImageFit.Contain:等比缩放,保持宽高比,可能留白
  • ImageFit.Cover:等比缩放,填充整个容器,可能裁剪
  • ImageFit.Fill:拉伸填充,可能变形

3. Button按钮组件

Button组件用于用户交互:

Button('登录')
  .width(300)
  .height(50)
  .backgroundColor('#007DFF')
  .fontColor(Color.White)
  .fontSize(18)
  .type(ButtonType.Capsule) // 按钮类型
  .onClick(() => {
    // 点击事件处理
    console.log('按钮被点击');
  })

按钮类型

  • ButtonType.Capsule:胶囊按钮(默认)
  • ButtonType.Normal:矩形按钮
  • ButtonType.Circle:圆形按钮

4. TextInput输入框组件

TextInput用于接收用户输入:

TextInput({ placeholder: '请输入用户名' })
  .width('90%')
  .height(50)
  .backgroundColor(Color.White)
  .borderRadius(8)
  .type(InputType.Normal) // 输入类型
  .onChange((value: string) => {
    // 输入内容变化
    console.log(value);
  })

输入类型

  • InputType.Normal:普通文本
  • InputType.Password:密码输入
  • InputType.Email:邮箱输入

四、布局系统

1. Column垂直布局

Column用于垂直排列子组件:

Column({ space: 20 }) { // 子组件间距20vp
  Text('顶部')
    .fontSize(20)
  Text('中间')
    .fontSize(20)
  Text('底部')
    .fontSize(20)
}
.width('100%')
.height('100%')
.alignItems(HorizontalAlign.Center) // 水平居中对齐
.justifyContent(FlexAlign.Center) // 垂直居中对齐

对齐方式

  • HorizontalAlign.Start:左对齐
  • HorizontalAlign.Center:居中对齐
  • HorizontalAlign.End:右对齐

2. Row水平布局

Row用于水平排列子组件:

Row({ space: 15 }) { // 子组件间距15vp
  Text('左侧')
    .layoutWeight(1) // 权重分配
  Text('中间')
    .layoutWeight(2)
  Text('右侧')
    .layoutWeight(1)
}
.width('100%')
.height(50)
.justifyContent(FlexAlign.SpaceBetween) // 两端对齐

主轴对齐方式

  • FlexAlign.Start:起始位置对齐
  • FlexAlign.Center:居中对齐
  • FlexAlign.End:结束位置对齐
  • FlexAlign.SpaceBetween:两端对齐,中间等距
  • FlexAlign.SpaceAround:等距分布

3. Stack层叠布局

Stack用于层叠显示子组件:

Stack() {
  Image($r('app.media.background'))
    .width('100%')
    .height('100%')
  Text('水印文字')
    .fontSize(16)
    .fontColor('#66000000')
    .position({ x: '50%', y: '50%' }) // 绝对定位
}
.width(300)
.height(200)

position定位

  • x:水平位置,支持百分比或固定值
  • y:垂直位置,支持百分比或固定值

4. Flex弹性布局

Flex提供更灵活的布局方式:

Flex({ direction: FlexDirection.Row, wrap: FlexWrap.Wrap }) {
  Text('项目1')
    .flexGrow(1) // 弹性拉伸
    .backgroundColor('#F5DEB3')
  Text('项目2')
    .flexGrow(2)
    .backgroundColor('#D2B48C')
  Text('项目3')
    .flexGrow(1)
    .backgroundColor('#F5DEB3')
}
.width('100%')
.height(100)
.justifyContent(FlexAlign.SpaceBetween)

Flex属性

  • flexGrow:拉伸比例,剩余空间分配
  • flexShrink:压缩比例,空间不足时压缩
  • flexBasis:基础尺寸

五、通用属性

1. 尺寸设置

Text('示例文本')
  .width(200)           // 固定宽度
  .width('50%')        // 百分比宽度
  .width('100%')       // 占满父容器
  .height(100)
  .minWidth(100)       // 最小宽度
  .maxWidth(300)       // 最大宽度

2. 边距设置

Text('示例文本')
  .margin({           // 外边距
    top: 10,
    right: 15,
    bottom: 10,
    left: 15
  })
  .padding({          // 内边距
    top: 5,
    right: 10,
    bottom: 5,
    left: 10
  })

3. 背景与边框

Text('示例文本')
  .backgroundColor('#F5F5F5') // 背景色
  .borderRadius(8)            // 圆角
  .borderWidth(1)            // 边框宽度
  .borderColor('#E0E0E0')    // 边框颜色
  .backgroundImage($r('app.media.bg')) // 背景图片

4. 透明度与可见性

Text('示例文本')
  .alpha(0.8)                 // 透明度,0-1
  .visibility(Visibility.Visible) // 可见性

可见性选项

  • Visibility.Visible:可见
  • Visibility.Hidden:隐藏但占位
  • Visibility.None:隐藏且不占位

六、实战案例:登录界面

下面是一个完整的登录界面实现:

@Entry
@Component
struct LoginPage {
  @State username: string = ''
  @State password: string = ''

  build() {
    Column({ space: 20 }) {
      // Logo
      Image($r('app.media.logo'))
        .width(120)
        .height(120)
        .margin({ top: 80, bottom: 40 })

      // 用户名输入框
      Column() {
        Text('用户名')
          .fontSize(14)
          .fontColor('#666666')
          .margin({ bottom: 8 })
        TextInput({ text: this.username, placeholder: '请输入用户名' })
          .width('90%')
          .height(50)
          .backgroundColor(Color.White)
          .borderRadius(8)
          .borderWidth(1)
          .borderColor('#E0E0E0')
          .padding({ left: 15, right: 15 })
          .onChange((value: string) => {
            this.username = value
          })
      }
      .width('100%')
      .padding({ left: 20, right: 20 })

      // 密码输入框
      Column() {
        Text('密码')
          .fontSize(14)
          .fontColor('#666666')
          .margin({ bottom: 8 })
        TextInput({ text: this.password, placeholder: '请输入密码' })
          .width('90%')
          .height(50)
          .backgroundColor(Color.White)
          .borderRadius(8)
          .borderWidth(1)
          .borderColor('#E0E0E0')
          .padding({ left: 15, right: 15 })
          .type(InputType.Password)
          .onChange((value: string) => {
            this.password = value
          })
      }
      .width('100%')
      .padding({ left: 20, right: 20 })

      // 登录按钮
      Button('登录')
        .width('90%')
        .height(50)
        .backgroundColor('#007DFF')
        .fontColor(Color.White)
        .fontSize(18)
        .margin({ top: 30 })
        .onClick(() => {
          // 登录逻辑
          console.log('用户名:', this.username)
          console.log('密码:', this.password)
        })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5')
  }
}

七、样式复用与优化

1. 使用@Styles装饰器复用样式

@Styles
function primaryButton() {
  .width('90%')
  .height(50)
  .backgroundColor('#007DFF')
  .fontColor(Color.White)
  .fontSize(18)
  .borderRadius(8)
}

// 使用样式
Button('登录')
  .primaryButton()

2. 使用@Builder装饰器复用UI片段

@Builder
function inputField(label: string, value: string, isPassword: boolean = false) {
  Column() {
    Text(label)
      .fontSize(14)
      .fontColor('#666666')
      .margin({ bottom: 8 })
    TextInput({ text: value, placeholder: `请输入${label}` })
      .width('90%')
      .height(50)
      .backgroundColor(Color.White)
      .borderRadius(8)
      .borderWidth(1)
      .borderColor('#E0E0E0')
      .padding({ left: 15, right: 15 })
      .type(isPassword ? InputType.Password : InputType.Normal)
  }
  .width('100%')
  .padding({ left: 20, right: 20 })
}

// 使用Builder
this.inputField('用户名', this.username)
this.inputField('密码', this.password, true)

八、性能优化建议

1. 布局优化

  • 减少嵌套层级:避免超过3层嵌套布局
  • 使用百分比布局:优先使用百分比而非固定尺寸
  • 合理使用Flex:简单布局优先使用Row/Column,复杂布局使用Flex

2. 图片优化

  • 使用合适尺寸:避免加载过大图片
  • 使用objectFit:合理设置图片填充模式
  • 使用占位图:网络图片加载时显示占位图

3. 组件优化

  • 组件复用:将可复用的UI封装成自定义组件
  • 状态管理:合理使用@State、@Prop、@Link装饰器
  • 条件渲染:使用if条件渲染避免不必要的组件渲染

九、总结与行动建议

核心要点回顾

  1. 声明式UI:采用数据驱动视图的方式,UI随状态自动更新
  2. 基础组件:掌握Text、Image、Button、TextInput等常用组件
  3. 布局系统:熟练使用Column、Row、Stack、Flex进行页面布局
  4. 通用属性:掌握尺寸、边距、背景、边框等样式设置
  5. 样式复用:使用@Styles和@Builder提高代码复用性

行动建议

  1. 动手实践:按照本文示例,独立完成登录界面的开发
  2. 样式调整:尝试修改登录界面的颜色、字体、间距等样式
  3. 组件扩展:在登录界面基础上添加"记住密码"、"忘记密码"等功能
  4. 布局练习:使用不同的布局方式实现相同的界面效果
  5. 查阅文档:遇到问题时,访问华为开发者联盟官网查阅官方文档

通过本篇文章的学习,你已经掌握了HarmonyOS声明式UI开发的核心能力。下一篇文章将深入讲解状态管理,帮助你实现更复杂的交互逻辑和数据同步。

posted @ 2025-12-23 23:14  J_____P  阅读(0)  评论(0)    收藏  举报