第三节 常用组件

图片组件

名称:Image

作用:在页面中用于展示图片

参数:只有一个参数(三种类型string,Resource,media.PixelMap)

  • string
    • 本地路径:需要将自己新建的图片文件夹放在ets目录下面
    • 网络路径:如果在真机上查看网络图片,需要配置网络访问权限,但是预览器和模拟器上不会限制
  • Resource
    • base
      • element:存放一些预先配置好的json
      • media:媒体资源(图片、音频、视频),需要使用r()方法引入,例如Image(r()方法引入,例如Image(r()方法引入,例如Image(r(‘app.media.img’))
      • profile:自定义配置文件
    • rawfile:存放任意格式的原始文件,需要使用rawfile()方法引入,例如Image(rawfile()方法引入,例如Image(rawfile()方法引入,例如Image(rawfile(‘icon.png’))
  • media.PixelMap:图片的像素位图,是一个二维数组,数组中的每个元素就是图片中的一个像素点,像素点包含了坐标和颜色值,通常用于图片的编辑

常用属性:

  • 图片尺寸(width、height)

    三种类型参数:

    • string:包含了单位(百分号%——基于父元素而言;物理像素px——弊端:很难适应不同屏幕素质的设备;虚拟像素vp——相对单位,可以适应不同屏幕素质的设备)
    • number:默认底层会分配vp作为单位
    • Resource:在element目录里面配置宽度和高度值放在json文件里面,使用r()方法调用,例如r()方法调用,例如r()方法调用,例如r(‘app.json文件名.属性名’)
  • 图片缩放:需要使用objectFit()方法设置,传入一个枚举对象(ImageFit)的选项值作为参数

    • None:不做任何缩放,保持图片原尺寸显示
    • Contain:按比例进行缩小或放大,使得图片能刚好装在组件中
    • Cover:按照比例缩小或者放大,使得图片能完全装在组件中
    • Fill:不会按照比例缩小或者放大,使得图片充满在组件中
    • ScaleDown:按照比例缩小或不变(不会放大)
    • Auto:自适应显示
  • 图片插值:在一些出现模糊的图片补充一些像素,需要使用interpolation()方法设置,传入一个枚举对象(ImageInterpolation)的选项值作为参数

    • None:不做插值(默认)
    • Low:低等质量插值
    • Medium:中等质量插值
    • High:高等质量插值

注意事项:

  1. 资源管理:尤其注意图片的大小和分辨率
  2. 布局和显示:注意图片的尺寸和比例
  3. 交互设计:点击和手势操作、动画效果
  4. 兼容性和可访问性:注意在不同的屏幕上有一致的观感效果;网络图片的访问配置

文本组件

名称:Text

作用:在页面中用于显示文本内容

参数:只有一个参数(两种参数类型string、Resource)

  • string:例如Text(‘我是一段文本’)
  • Resource:需要预先在element目录配置好json文件,使用r()方法引入,例如Text(r()方法引入,例如Text(r()方法引入,例如Text(r(‘app.json文件名.字符串名称’))

常用属性:

  • 字体大小:需要使用fontSize()方法设置

    三种类型参数:

    • string:需要带单位(物理像素px、字体像素fp(相对单位))
    • number:底层会分配一个fp作为单位
    • Resource:需要预先在element配置好json
  • 字体粗细:需要使用fontWeight()方法设置

    三种类型参数:

    • string:注意只能写数值和枚举的选项,例如fontWeight(‘100’)或fontWeight(‘bold’)
    • number:注意取值范围[100,900],默认是400
    • fontWeight:Lighter——非常细、Normal——正常、Regular——常规、Medium——中等、Bold——较粗、Bolder——非常粗
  • 字体颜色:需要使用fontColor()方法设置

    四种类型参数:

    • Color:例如Color.Green
    • string:rgb()形式如’rgb(255,0,0)‘;rgba形式如’rgb(255,0,0,0.5)’;十六进制形式如’#ff0000’
    • number:要用十六进制的数字,例如0x008000
    • Resource:需要预先在element配置好json
  • 文本对齐:需要使用textAlign()方法设置,传入一个枚举对象(TextAlign)的选项值作为参数

    • Start:首部对齐
    • Center:居中对齐
    • End:末端对齐
  • 最大行数设置和超长文本处理:需要使用maxLines()方法设置最大行数,textOverflow()方法处理超出部分

    textOverflow()方法需传入一个对象形式的参数:

    • {overflow:TextOverflow.Clip}(裁剪)
    • {overflow:TextOverflow.Ellipsis}(省略号代替)

注意事项:

  1. 文本内容和格式:尤其要注意文本尽量不要超过Text组件的尺寸、
  2. 布局和尺寸:注意不同组件大小和屏幕尺寸
  3. 交互和响应
  4. 性能和资源管理
  5. 可访问性和国际化的问题

按钮组件

名称:Button

作用:用于响应页面中用户的点击操作

参数:两种使用方式

  • 不包含子组件的时候:
    • label:标签
    • options(对象):type按钮类型——赋予一个枚举对象(ButtonType)的选项值(Capsule胶囊状、Circle圆形、Normal直角矩形);stateEffect是否开启点击效果(true/false)
  • 包含子组件的时候:
    • options(对象):type按钮类型——赋予一个枚举对象(ButtonType)的选项值(Capsule胶囊状、Circle圆形、Normal直角矩形);stateEffect是否开启点击效果(true/false)

常用属性:

  • 背景色:需要使用backgroundColor()方法设置

    四种类型参数:

    • Color:例如Color.Green
    • string:rgb()形式如’rgb(255,0,0)‘;rgba形式如’rgb(255,0,0,0.5)’;十六进制形式如’#ff0000’
    • number:要用十六进制的数字,例如0x008000
    • Resource:需要预先在element配置好json
  • 边框圆角:需要使用borderRadius()方法设置,只有当按钮形状是直角矩形的时候才有意义

常用事件:点击事件,需要按钮绑定onClick()方法

注意事项:

  1. 功能设计:注意按钮的用途
  2. 外观设计:注意跟随系统的暗黑模式
  3. 交互设计:点击反馈
  4. 可访问性:注意特殊需求的用户

切换按钮组件

名称:Toggle

作用:在页面中切换不同状态

参数:只有一个参数(对象形式,包含两个属性)

  • type:切换按钮形状,赋值为一个枚举对象(ToggleType)选项值(Switch(开关)、Checkbox(复选框)、Button(按钮,注意加个子组件))
  • isOn:切换按钮的状态(true/false)

常用属性:

  • 激活状态背景色:需要使用selectedColor()方法设置

    参数:

    • Color
    • string:rgb()形式;rgba形式;十六进制形式
    • number:十六进制的数字
    • Resource
  • Switch滑块颜色:需要使用switchPointColor()方法设置

    参数:

    • Color
    • string
    • number
    • Resource

常用事件:change状态改变事件,需要绑定onChange((isOn: boolen) => void)方法

注意事项:

  1. 功能明确性:在切换按钮之前加一些必要的标签
  2. 外观设计
  3. 交互反馈
  4. 可访问性:注意视力障碍的用户以及支持键盘的快捷操作
  5. 性能考虑:避免频繁更新以及内存管理,可以设置防抖和节流

文本输入组件

名称:TextInput

作用:用于接收用户输入的文本内容

参数:只有一个参数(对象形式,包含两个属性)

  • placeholder:占位提示符(string、Resource)
  • text:输入框当前的文本内容(string、Resource)

常用属性:

  • 输入框类型:需要使用type()方法设置,传入一个枚举对象(InputType)的选项值作为参数
    • Normal:基本输入模式,默认
    • password:密码输入模式
    • Number:数字输入模式
  • 光标样式:需要使用caretColor()方法设置(Color、string、number、Resource)
  • placeholder样式:使用placeholderFont()方法设置字体大小、字体粗细等;使用placeholderColor()设置字体颜色
  • 文本样式:fontSize()、fontWeight()、fontColor()

常用事件:

  • change事件:监听输入框内容的改变,需要绑定onChange((value: string) => void)方法
  • 焦点事件:
    • 获得焦点:需要绑定onFocus(() => void)方法
    • 失去焦点:需要绑定onBlur(() => void)方法

注意事项:

  1. 用户体验方面:注意一些必要的提示,例如placeholder和标签,以及自动聚焦
  2. 功能实现方面:注意数据校验和安全性等
  3. 性能优化方面:尤其内存和缓存资源的运用
  4. 可访问性方面:注意视力障碍的用户的使用

进度条组件

名称:Progress

作用:显示各种进度

参数:只有一个参数(对象形式,包含三个属性)

  • value:当前的进度值
  • total:进度条的总值(不要超过100)
  • type:进度条类型,赋予一个枚举对象(ProgressType)的选项值(Linear(线性)、Ring(环形)、ScaleRing(带有刻度的环形)、Eclipse(月食形状)、Capsule(胶囊状))

常用属性:

  • 进度条样式:需要使用style()方法设置,传入一个对象形式的参数
    • strokeWidth:进度条的宽度(粗细),注意只能作用于Linear/Ring/ScaleRing,默认是4vp
    • scaleCount:进度条的刻度数,注意只能作用于ScaleRing,默认有120个刻度
    • scaleWith:刻度线的宽度(粗细),默认是2vp
  • 进度条颜色:通过backgroundColor()设置进度条的背景色,通过color()方法设置进度条的前景色

注意事项:

  1. 功能的准确性:注意进度条范围的设置以及当前进度的更新
  2. 用户体验:尤其注意给一些清晰的标识和反馈信息
  3. 性能优化:尤其要注意一些大量并发任务和长时间运行的任务

弹窗组件

作用:用于在页面中显示重要的信息,提示用户操作或收集用户信息

分类:

  • 消息提示弹窗:

    • 名称:Toast
    • 作用:用于显示一些简短的消息或提示
    • 用法:
      1. 导入promptAction模块
      2. 使用showToast方法显示,需要传入一个对象形式的参数
        • message:提示的消息
        • duration:停留时长,单位是ms,范围是[1500,10000]
        • bottom:距离底部的距离
  • 警告对话框:

    • 名称:AlertDialog

    • 作用:向用户发出警告或者二次确认操作的提示

    • 用法:参考代码

      Button('删除')
        .backgroundColor(Color.Red)
        .onClick(() => {
          AlertDialog.show({
            // 标题
            title: '温馨提示',
            // 内容
            message: '删除之后无法恢复,您是否确认删除?',
            // 确认按钮
            primaryButton: {
              // 标签
              value: '删除',
              // 字体颜色
              fontColor: '#ff0000',
              // 背景色
              backgroundColor: '#fff',
              // 触发逻辑
              action: () => {}
            },
            // 取消按钮
            secondaryButton: {
              // 标签
              value: '取消',
              // 字体颜色
              fontColor: '#0000ff',
              // 背景色
              backgroundColor: '#fff',
              // 触发逻辑
              action: () => {}
            },
            // 位置
            alignment: DialogAlignment.Bottom,
            // 偏移量
            offset: {
              dx: 0,
              dy: -20
            }
        })
      })
      
  • 操作列表弹窗:

    • 名称:ActionSheet

    • 作用:向用户提供一组选项,用户可以进行选择

    • 用法:参考代码

      Button('选择操作')
          .onClick(() => {
            ActionSheet.show({
              // 标题
              title: '文件操作',
              // 内容
              message: '请选择倪要对文件执行的操作:',
              // 按钮
              confirm: {
                // 标签
                value: '取消',
                // 触发事件
                action: () => {
                  console.log('点击取消')
                }
              },
              // 数组
              sheets: [
                {
                  // 标题
                  title: '复制',
                  // 图标
                  icon: $r('app.media.ic_copy'),
                  // 触发事件
                  action: () => {
                    console.log('文件复制操作')
                  }
                },
                {
                  // 标题
                  title: '剪切',
                  // 图标
                  icon: $r('app.media.ic_cut'),
                  // 触发事件
                  action: () => {
                    console.log('文件剪切操作')
                  }
                },
                {
                  // 标题
                  title: '删除',
                  // 图标
                  icon: $r('app.media.ic_delete'),
                  // 触发事件
                  action: () => {
                    console.log('文件删除操作')
                  }
                },
              ]
            })
          })
      
  • 选择器弹窗

    • 作用:让用户从一个列表中选择一个具体的值

    • 分类:

      • 文本滑动选择器弹窗

        • 名称:TextPickerDialog

        • 用法:参考代码

          @Entry
          @Component
          struct Index {
            @State message: string = '苹果'
            @State select: number = 0
            private fruits: string[] = ['苹果', '西瓜', '雪梨', '橙子', '香蕉']
            private fruits_color: string[] = ['#FFC0CB', '#90EE90', '#FFFF00', '#FFA07A', '#FFD700']
          
            build() {
              // 外壳容器
              Column({
                space: 20
              }) {
                Text(this.message)
                  .fontSize(25)
                  .fontWeight(FontWeight.Bold)
                  .fontColor(this.fruits_color[this.select])
          
                Button('选择水果')
                  .onClick(() => {
                    TextPickerDialog.show({
                      // 范围
                      range: this.fruits,
                      // 下标
                      selected: this.select,
                      // 确定方法
                      onAccept: (value: TextPickerResult) => {
                        this.select = Number(value.index)
                        this.message = this.fruits[this.select]
                        console.log('最终选择的值为:', value.value)
                      },
                      // 切换方法
                      onChange: (value: TextPickerResult) => {
                        console.log('当前选择的值为:', value.value)
                      },
                      // 取消方法
                      onCancel: () => {
                        console.log('取消文本选择')
                      }
                    })
                  })
              }
              .width('100%')  // 宽度撑满整个屏幕
              .height('100%')  // 高度撑满整个屏幕
              .justifyContent(FlexAlign.Center)  // 居中
            }
          }
          
      • 日期滑动选择器弹窗

        • 名称:DatePickerDialog

        • 用法:参考代码

          // 导入 dayjs库
          import dayjs from 'dayjs'
          
          @Entry
          @Component
          struct Index {
            @State selectDate: string = dayjs(new Date('2025-1-1')).format('YYYY-MM-DD')
          
            build() {
              // 外壳容器
              Column({
                space: 20
              }) {
                Text(this.selectDate)
                  .fontSize(25)
                  .fontWeight(FontWeight.Bold)
          
                Button('选择日期')
                  .onClick(() => {
                    DatePickerDialog.show({
                      // 开始日期
                      start: new Date('2010-1-1'),
                      // 结束日期
                      end: new Date('2025-12-31'),
                      // 选中的日期
                      selected: new Date(this.selectDate),
                      // 确定方法
                      onAccept: (value: DatePickerResult) => {
                        this.selectDate = dayjs(new Date(this.selectDate).setFullYear(value.year, value.month, value.day)).format('YYYY-MM-DD')
                        console.log('最终选择的日期值为:', this.selectDate)
                      },
                      // 切换的方法
                      onChange: (value: DatePickerResult) => {
                        console.log('切换的日期值为:' + value.year + '-' + value.month + '-' + value.day)
                      },
                      // 取消的方法
                      onCancel: () => {
                        console.log('取消选择')
                      }
                    })
                  })
              }
              .width('100%')  // 宽度撑满整个屏幕
              .height('100%')  // 高度撑满整个屏幕
              .justifyContent(FlexAlign.Center)  // 居中
            }
          }
          
      • 时间滑动选择器弹窗

        • 名称:TimePickerDialog

        • 用法:参考代码

          // 导入 dayjs库
          import dayjs from 'dayjs'
          
          @Entry
          @Component
          struct Index {
            @State selectTime: string = dayjs(new Date('2025-7-15 10:30:00')).format('HH:mm')
          
            build() {
              // 外壳容器
              Column({
                space: 20
              }) {
                Text(this.selectTime)
                  .fontSize(25)
                  .fontWeight(FontWeight.Bold)
          
                Button('选择时间')
                  .onClick(() => {
                    TimePickerDialog.show({
                      // 选择的时间
                      selected: new Date(this.selectTime),
                      // 是否开启24小时制
                      useMilitaryTime: true,
                      // 确定方法
                      onAccept: (time: TimePickerResult) => {
                        this.selectTime = dayjs(new Date().setHours(time.hour, time.minute)).format('HH:mm')
                        console.log('最终选择的时间值为:', this.selectTime)
                      },
                      // 切换的方法
                      onChange: (time: TimePickerResult) => {
                        console.log('切换的时间值为:' + time.hour + ':' + time.minute)
                      },
                      // 取消的方法
                      onCancel: () => {
                        console.log('取消选择')
                      }
                    })
                  })
              }
              .width('100%')  // 宽度撑满整个屏幕
              .height('100%')  // 高度撑满整个屏幕
              .justifyContent(FlexAlign.Center)  // 居中
            }
          }
          
  • 自定义弹窗

    • 作用:适用于一些比较复杂的场景
    • 用法:敬请期待

注意事项:

  1. 功能方面:尤其注意使用的目的以及生命周期管理
  2. 用户体验方面:注意外面和风格的统一性以及可操作性
  3. 可访问性方面:给一些特殊人群添加辅助功能

组件编程技巧

样式复用

  • 概念:当多个组件具有相同样式的时候,可以将些重复的代码单独抽离成一个方法,然后在需要的地方调用即可

  • 方法:

    • @Styles方法:

      • 组件内使用:请看具体案例

        @Entry
        @Component
        struct Index {
          build() {
            // 外壳容器
            Column({
              space: 20
            }) {
              Row({
                space: 50
              }) {
                Button('确认')
                  .type(ButtonType.Normal)
                  .compButtonStyle()  // 调用复用样式
                  .backgroundColor(Color.Green)
                  .onClick(() => {
                    console.log('确认')
                  })
                Button('取消')
                  .type(ButtonType.Normal)
                  .compButtonStyle()  // 调用复用样式
                  .backgroundColor(Color.Gray)
                  .onClick(() => {
                    console.log('取消')
                  })
              }
            }
            .width('100%')  // 宽度撑满整个屏幕
            .height('100%')  // 高度撑满整个屏幕
            .justifyContent(FlexAlign.Center)  // 居中
          }
        
          // 组件内样式复用
          @Styles compButtonStyle() {
            .width(100)
            .height(40)
            .borderRadius(10)
          }
        }
        
      • 全局使用:请看具体案例

        @Entry
        @Component
        struct Index {
          build() {
            // 外壳容器
            Column({
              space: 20
            }) {
              Row({
                space: 50
              }) {
                Button('确认')
                  .type(ButtonType.Normal)
                  .backgroundColor(Color.Green)
                  .globalButtonStyle()  // 样式复用调用
                  .onClick(() => {
                    console.log('确认')
                  })
                Button('取消')
                  .type(ButtonType.Normal)
                  .backgroundColor(Color.Gray)
                  .globalButtonStyle()  // 样式复用调用
                  .onClick(() => {
                    console.log('取消')
                  })
              }
            }
            .width('100%')  // 宽度撑满整个屏幕
            .height('100%')  // 高度撑满整个屏幕
            .justifyContent(FlexAlign.Center)  // 居中
          }
        }
        
        // 全局样式复用
        @Styles function globalButtonStyle() {
          .width(100)
          .height(40)
          .borderRadius(10)
        }
        
      • 注意:

        1. 组件内的@styles方法只能在当前组件内使用,全局的@Styles方法只能在当前的.ets文件中使用
        2. 组件内定义的@styles方法不需要使用function关键字,但是全局的要使用
        3. @styles方法中只能包含通用的属性和通用的事件方法
        4. @styles方法不支持参数
    • @Extend方法

      • 只能全局使用,而且只能用于指定类型的组件:请看具体案例

        @Entry
        @Component
        struct Index {
          build() {
            // 外壳容器
            Column({
              space: 20
            }) {
              Row({
                space: 50
              }) {
                Button('确认')
                  .buttonExtendStyle(Color.Green, () => console.log('确定'))  // 样式复用调用
        
                Button('取消')
                  .buttonExtendStyle(Color.Gray, () => console.log('取消'))  // 样式复用调用
              }
            }
            .width('100%')  // 宽度撑满整个屏幕
            .height('100%')  // 高度撑满整个屏幕
            .justifyContent(FlexAlign.Center)  // 居中
          }
        }
        
        // 全局样式复用
        @Extend(Button) function buttonExtendStyle(color: Color, callback: () => void) {
          .width(100)
          .height(40)
          .borderRadius(10)
          .type(ButtonType.Normal)
          .backgroundColor(color)
          .onClick(callback)
        }
        
      • 注意:

        1. 使用范围只能限于当前的.ets文件
        2. 可包含组件的专有属性和专有事件方法
        3. 支持参数

UI结构复用

  • 概念:当页面有多个相同的UI结构时,可以把相同的结构抽离封装成方法,然后在需要的地方调用即可

  • 方法:@Builder方法

    • 组件内使用:请看具体案例

      @Entry
      @Component
      struct Index {
        build() {
          // 外壳容器
          Column({
            space: 20
          }) {
            Row({
              space: 50
            }) {
              // UI结构调用
              this.compButtonBuilder($r('app.media.ic_edit'), '编辑', () => console.log('编辑'))
              this.compButtonBuilder($r('app.media.ic_share'), '发送', () => console.log('发送'))
            }
          }
          .width('100%')  // 宽度撑满整个屏幕
          .height('100%')  // 高度撑满整个屏幕
          .justifyContent(FlexAlign.Center)  // 居中
        }
      
        // 组件内UI结构复用
        @Builder compButtonBuilder(icon: Resource, text: string, callback: () => void) {
          Button() {
            Row({
              space: 10
            }) {
              Image(icon)
                .width(25)
                .height(25)
                .fillColor(Color.White)
              Text(text)
                .fontSize(25)
                .fontColor(Color.White)
            }
          }
          .width(120)
          .height(50)
          .onClick(callback)
        }
      }
      
    • 全局使用:请看具体案例

      @Entry
      @Component
      struct Index {
        build() {
          // 外壳容器
          Column({
            space: 20
          }) {
            Row({
              space: 50
            }) {
              // UI结构调用
              globalButtonBuilder($r('app.media.ic_edit'), '编辑', () => console.log('编辑'))
              globalButtonBuilder($r('app.media.ic_share'), '发送', () => console.log('发送'))
            }
          }
          .width('100%')  // 宽度撑满整个屏幕
          .height('100%')  // 高度撑满整个屏幕
          .justifyContent(FlexAlign.Center)  // 居中
        }
      }
      
      // 全局UI结构复用
      @Builder function globalButtonBuilder(icon: Resource, text: string, callback: () => void) {
        Button() {
          Row({
            space: 10
          }) {
            Image(icon)
              .width(25)
              .height(25)
              .fillColor(Color.White)
            Text(text)
              .fontSize(25)
              .fontColor(Color.White)
          }
        }
        .width(120)
        .height(50)
        .onClick(callback)
      }
      
    • 注意:

      1. 组件内的@Bulider方法要通过this调用,但是全局则不需要
      2. 组件内的@Bulider方法只能用于当前组件,全局的@Builder方法导出(export)导出,可用于整个应用
    • 参数传递规则:

      • 按值传递
      • 按引用传递:当只有一个参数并且这个参数是对象的形式,优势是如果传递的参数包含了状态变量,则状态变量的变化会触发@Builder方法内部Ul的刷新
      @Entry
      @Component
      struct Index {
        @State count: number = 0
      
        build() {
          // 外壳容器
          Column({
            space: 50
          }) {
            // 按值传递
            valueTextBuilder(this.count)
      
            // 按引用传递
            referenceTextBuilder({ count: this.count })
      
            Row({
              space: 50
            }) {
              Button('-1')
                .onClick(() => {
                  this.count--
                })
              Button('+1')
                .onClick(() => {
                  this.count++
                })
            }
          }
          .width('100%')  // 宽度撑满整个屏幕
          .height('100%')  // 高度撑满整个屏幕
          .justifyContent(FlexAlign.Center)  // 居中
        }
      }
      
      @Builder function valueTextBuilder(count: number) {
        Text(`按值传递:${count}`)
          .fontSize(30)
          .fontWeight(FontWeight.Bold)
      }
      
      interface Obj {
        count: number
      }
      
      @Builder function referenceTextBuilder(obj: Obj) {
        Text(`按引用传递:${obj.count}`)
          .fontSize(30)
          .fontWeight(FontWeight.Bold)
      }
      
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐