鸿蒙ArkUI基础组件开发详解

在鸿蒙应用开发中,ArkUI框架提供了丰富的基础组件库,这些组件是构建用户界面的基石。本文将系统地介绍鸿蒙ArkUI中最常用的基础组件,包括Text、Button、Image、TextInput、Slider等的使用方法、样式定制和最佳实践,帮助开发者快速掌握鸿蒙UI开发的核心技能。

通过本文的学习,您将能够:

  • 掌握鸿蒙ArkUI基础组件的基本使用方法
  • 了解各组件的样式定制和高级特性
  • 学习组件组合使用的技巧和最佳实践
  • 构建出美观、高效的鸿蒙应用界面

前言

随着鸿蒙操作系统生态的快速发展,ArkTS作为鸿蒙应用开发的主要语言,其声明式UI框架ArkUI凭借简洁的语法和强大的功能,成为开发者构建应用界面的首选方案。ArkUI提供了一套完整的基础组件库,涵盖了文本显示、按钮交互、图片展示、数据输入等常用功能,使开发者能够以声明式的方式构建出美观、高效且易于维护的用户界面。

本文将系统地介绍鸿蒙ArkUI中最核心的基础组件,结合实际案例展示它们的使用方法和高级特性,帮助开发者快速掌握鸿蒙UI开发的关键技能。

一、文本显示:Text组件

Text组件是ArkTS中最基础也是最常用的UI组件之一,用于在界面上显示文本内容。在鸿蒙开发中,Text组件支持丰富的样式设置、内容格式化以及交互事件处理,可以满足从简单标签到复杂富文本的各种文本展示需求。作为构建用户界面的基础元素,几乎所有应用界面都离不开文本展示,掌握Text组件的使用对于构建良好的用户体验至关重要。

1.1 Text组件的基本用法

Text组件的基本用法非常直观,只需要在组件中传入要显示的文本内容即可。Text组件支持静态文本展示和基于状态变量的动态文本更新,是构建用户界面的基础元素。

typescript 复制代码
@Entry
@Component
struct TextBasicExample {
  // 定义状态变量用于动态更新文本
  @State dynamicText: string = '动态更新的文本';
  
  build() {
    Column() {
      // 基本静态文本显示
      Text('Hello ArkTS!')
        .margin(10)
      
      // 使用状态变量实现动态文本显示
      Text(this.dynamicText)
        .fontSize(18)
        .margin(10)
    }
    .padding(20)
    .width('100%')
  }
}

在上述示例中,我们演示了Text组件的两种常见用法:

  1. 直接传入字符串字面量展示静态文本
  2. 结合@State装饰器使用状态变量展示动态文本

通过状态变量,当数据发生变化时,Text组件会自动更新界面上显示的内容,无需手动操作DOM,这充分体现了ArkTS声明式UI的优势。在实际应用开发中,开发者可以根据需要为Text组件添加更多样式属性,如字体大小、颜色、粗细等,以满足不同的UI设计需求。

1.2 文本样式设置

Text组件提供了丰富的样式属性,用于控制文本的视觉表现。通过这些样式属性,开发者可以灵活定制文本的字体、颜色、大小、对齐方式等,以满足不同的UI设计需求。以下是Text组件常用样式设置的示例:

typescript 复制代码
@Entry
@Component
struct TextStyleExample {
  build() {
    Column() {
      // 设置字体大小和粗细
      Text('大号粗体文本')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin(10)
      
      // 设置字体颜色和样式
      Text('彩色斜体文本')
        .fontColor(Color.Blue)
        .fontStyle(FontStyle.Italic)
        .margin(10)
      
      // 设置文本对齐方式
      Text('居中对齐的文本')
        .textAlign(TextAlign.Center)
        .width('100%')
        .margin(10)
      
      // 设置文本行高
      Text('这是一个设置了行高的长文本示例,用于展示文本在多行情况下的显示效果。')
        .lineHeight(30)
        .margin(10)
      
      // 设置文本最大行数和省略方式
      Text('这是一个非常长的文本,需要限制显示行数,超出部分将显示省略号。这是一个非常长的文本,需要限制显示行数,超出部分将显示省略号。')
        .maxLines(2)
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .margin(10)
        
      // 组合多种样式设置
      Text('组合样式示例')
        .fontSize(20)
        .fontColor(Color.Purple)
        .fontWeight(FontWeight.Bold)
        .fontStyle(FontStyle.Italic)
        .textAlign(TextAlign.Center)
        .width('100%')
        .margin(10)
    }
    .padding(20)
    .width('100%')
  }
}

在鸿蒙ArkTS中,Text组件的样式设置遵循声明式语法,开发者可以通过链式调用的方式将多种样式属性应用到同一个组件上。这种设计使得样式设置直观且易于维护。

常用的文本样式属性包括:

  • fontSize: 设置字体大小,可接受数值或资源引用
  • fontColor: 设置文本颜色,支持Color对象、十六进制色值等
  • fontWeight: 设置字体粗细,如Normal、Medium、Bold等
  • fontStyle: 设置字体风格,可选Normal或Italic
  • textAlign: 设置文本对齐方式,如Left、Center、Right等
  • lineHeight: 设置文本行高,影响多行文本的行间距
  • maxLines: 设置文本最大显示行数
  • textOverflow: 设置文本溢出时的处理方式,如Ellipsis(省略号)
  • decoration: 设置文本装饰线,如下划线、删除线等

通过合理组合这些样式属性,开发者可以创建出符合设计规范、视觉效果良好的文本显示效果,提升应用的整体用户体验。

1.3 富文本显示

ArkTS中的Text组件还支持富文本显示,可以在同一段文本中应用不同的样式:

typescript 复制代码
@Entry
@Component
struct RichTextExample {
  build() {
    Column() {
      // 富文本示例
      Text() {
        Span('普通文本').fontSize(16)
        Span(' 粗体文本 ').fontSize(16).fontWeight(FontWeight.Bold)
        Span('彩色文本').fontSize(16).fontColor(Color.Red)
      }
      .margin(10)
      
      // 嵌套富文本
      Text() {
        Span('标题: ').fontWeight(FontWeight.Bold)
        Span('这是一个富文本标题').fontColor(Color.Blue)
        Span(' (带链接效果)')
          .decoration({ type: TextDecorationType.Underline })
          .fontColor(Color.Purple)
      }
      .margin(10)
    }
    .padding(20)
    .width('100%')
  }
}

1.4 文本交互

Text组件也支持一些基本的交互功能,如点击事件:

typescript 复制代码
@Entry
@Component
struct TextInteractionExample {
  @State clickCount: number = 0;
  
  build() {
    Column() {
      // 可点击的文本
      Text(`点击我,已点击 ${this.clickCount} 次`)
        .fontSize(18)
        .fontColor(Color.Blue)
        .onClick(() => {
          this.clickCount++;
          console.log(`文本被点击,当前计数: ${this.clickCount}`);
        })
        .margin(10)
        
      // 长按文本
      Text('长按此文本')
        .fontSize(18)
        .fontColor(Color.Orange)
        .onLongPress(() => {
          console.log('文本被长按');
          // 这里可以添加长按后的处理逻辑,如显示菜单等
        })
        .margin(10)
    }
    .padding(20)
    .width('100%')
  }
}

Text组件作为ArkTS中最基础的显示组件,掌握其用法对于构建良好的用户界面至关重要。通过灵活运用各种样式属性和交互功能,开发者可以创建出丰富多样的文本显示效果。

二、按钮:Button组件

Button组件是用户界面中用于触发操作的核心交互组件。在ArkTS中,Button组件提供了多种样式和状态,以适应不同的应用场景需求。

2.1 Button组件的基本用法

Button组件的基本用法非常简单,可以直接在括号中传入文本内容:

typescript 复制代码
@Entry
@Component
struct ButtonBasicExample {
  build() {
    Column() {
      // 基本按钮
      Button('点击我')
        .margin(10)
        .onClick(() => {
          console.log('按钮被点击了');
        })
    }
    .padding(20)
    .width('100%')
  }
}

2.2 按钮样式和类型

ArkTS中的Button组件支持多种预设样式和类型,可以通过type属性进行设置:

typescript 复制代码
@Entry
@Component
struct ButtonTypeExample {
  build() {
    Column() {
      // 默认按钮
      Button('默认按钮')
        .margin(10)
      
      // 主按钮(突出显示)
      Button('主按钮', { type: ButtonType.Capsule })
        .backgroundColor(Color.Blue)
        .margin(10)
      
      // 文本按钮
      Button('文本按钮', { type: ButtonType.Normal })
        .fontColor(Color.Blue)
        .margin(10)
      
      // 圆形按钮
      Button('圆形按钮')
        .type(ButtonType.Circle)
        .backgroundColor(Color.Green)
        .padding({ left: 20, right: 20 })
        .margin(10)
      
      // 描边按钮
      Button('描边按钮', { type: ButtonType.Outline })
        .borderColor(Color.Red)
        .fontColor(Color.Red)
        .margin(10)
    }
    .padding(20)
    .width('100%')
  }
}

2.3 按钮状态管理

Button组件可以通过enabled属性控制其启用状态,以及通过stateEffect属性控制点击效果:

typescript 复制代码
@Entry
@Component
struct ButtonStateExample {
  @State isEnabled: boolean = true;
  @State showStateEffect: boolean = true;
  
  build() {
    Column() {
      // 启用/禁用状态切换
      Button('启用/禁用按钮')
        .enabled(this.isEnabled)
        .backgroundColor(this.isEnabled ? Color.Blue : Color.Gray)
        .margin(10)
        .onClick(() => {
          console.log('按钮被点击,但可能被禁用');
        })
      
      // 切换按钮状态的开关
      Button(this.isEnabled ? '禁用按钮' : '启用按钮')
        .margin(10)
        .onClick(() => {
          this.isEnabled = !this.isEnabled;
        })
      
      // 有无点击效果的按钮
      Button('点击效果按钮')
        .stateEffect(this.showStateEffect)
        .margin(10)
        .onClick(() => {
          console.log('点击效果按钮被点击');
        })
      
      // 切换点击效果的开关
      Button(this.showStateEffect ? '禁用点击效果' : '启用点击效果')
        .margin(10)
        .onClick(() => {
          this.showStateEffect = !this.showStateEffect;
        })
    }
    .padding(20)
    .width('100%')
  }
}

2.4 带图标的按钮

Button组件可以结合图标使用,增强视觉效果和可识别性:

typescript 复制代码
@Entry
@Component
struct ButtonWithIconExample {
  build() {
    Column() {
      // 左侧带图标的按钮
      Button({
        type: ButtonType.Capsule,
        stateEffect: true
      })
      {
        Row() {
          Image($r('app.media.ic_public_add'))
            .width(20)
            .height(20)
          Text('添加')
            .margin({ left: 5 })
        }
      }
      .width('200px')
      .height('40px')
      .backgroundColor(Color.Green)
      .margin(10)
      
      // 右侧带图标的按钮
      Button({
        type: ButtonType.Capsule
      })
      {
        Row() {
          Text('分享')
          Image($r('app.media.ic_public_share'))
            .width(20)
            .height(20)
            .margin({ left: 5 })
        }
      }
      .width('200px')
      .height('40px')
      .backgroundColor(Color.Orange)
      .margin(10)
      
      // 纯图标按钮
      Button({
        type: ButtonType.Circle
      })
      {
        Image($r('app.media.ic_public_delete'))
          .width(24)
          .height(24)
      }
      .width('50px')
      .height('50px')
      .backgroundColor(Color.Red)
      .margin(10)
    }
    .padding(20)
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}

2.5 按钮尺寸和布局

可以通过设置widthheightpadding等属性调整按钮的尺寸和布局:

typescript 复制代码
@Entry
@Component
struct ButtonSizeExample {
  build() {
    Column() {
      // 不同宽度的按钮
      Button('窄按钮')
        .width('120px')
        .margin(10)
      
      Button('中等宽度按钮')
        .width('200px')
        .margin(10)
      
      Button('全宽按钮')
        .width('100%')
        .margin(10)
      
      // 不同高度的按钮
      Button('矮按钮')
        .height('32px')
        .margin(10)
      
      Button('标准按钮')
        .height('48px')
        .margin(10)
      
      Button('高按钮')
        .height('60px')
        .margin(10)
      
      // 自定义内边距
      Button('自定义内边距')
        .padding({ left: 30, right: 30, top: 10, bottom: 10 })
        .margin(10)
    }
    .padding(20)
    .width('100%')
  }
}

Button组件作为用户交互的主要入口,其使用方式灵活多样。通过合理设置按钮样式、状态和布局,开发者可以创建出既美观又易于使用的交互界面。

三、图片:Image组件

Image组件用于在应用界面中显示图片,是构建视觉丰富界面的重要元素。ArkTS中的Image组件支持多种图片来源和显示模式,可以灵活满足不同的设计需求。

3.1 Image组件的基本用法

Image组件的基本用法是指定图片源,支持本地资源和网络图片:

typescript 复制代码
@Entry
@Component
struct ImageBasicExample {
  build() {
    Column() {
      // 使用本地资源图片
      Text('本地资源图片')
        .fontSize(16)
        .margin({ bottom: 10 })
      
      Image($r('app.media.logo'))
        .width(200)
        .height(200)
        .margin(10)
      
      // 使用网络图片
      Text('网络图片')
        .fontSize(16)
        .margin({ bottom: 10 })
      
      Image('https://example.com/image.jpg')
        .width(200)
        .height(200)
        .margin(10)
      
      // 使用base64编码图片
      Text('Base64编码图片')
        .fontSize(16)
        .margin({ bottom: 10 })
      
      // 注意:实际使用时需要提供有效的base64字符串
      Image('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==')
        .width(200)
        .height(200)
        .margin(10)
    }
    .padding(20)
    .width('100%')
  }
}

3.2 图片尺寸和缩放模式

Image组件提供了多种缩放模式,用于控制图片如何适应组件的宽高:

typescript 复制代码
@Entry
@Component
struct ImageScaleExample {
  build() {
    Column() {
      // 原图大小(无缩放)
      Text('Original(原图大小)')
        .fontSize(14)
        .margin({ bottom: 5 })
      Image($r('app.media.example_image'))
        .width(200)
        .height(200)
        .objectFit(ImageFit.Original)
        .border({ width: 1, color: Color.Gray })
        .margin(10)
      
      // 适应组件大小,保持宽高比
      Text('Contain(保持宽高比适应)')
        .fontSize(14)
        .margin({ bottom: 5 })
      Image($r('app.media.example_image'))
        .width(200)
        .height(200)
        .objectFit(ImageFit.Contain)
        .border({ width: 1, color: Color.Gray })
        .margin(10)
      
      // 填满组件,保持宽高比,可能裁剪图片
      Text('Cover(覆盖填充)')
        .fontSize(14)
        .margin({ bottom: 5 })
      Image($r('app.media.example_image'))
        .width(200)
        .height(200)
        .objectFit(ImageFit.Cover)
        .border({ width: 1, color: Color.Gray })
        .margin(10)
      
      // 拉伸图片以填满组件,不保持宽高比
      Text('Fill(拉伸填充)')
        .fontSize(14)
        .margin({ bottom: 5 })
      Image($r('app.media.example_image'))
        .width(200)
        .height(200)
        .objectFit(ImageFit.Fill)
        .border({ width: 1, color: Color.Gray })
        .margin(10)
      
      // 适应组件宽度,保持宽高比
      Text('WidthFix(宽度固定)')
        .fontSize(14)
        .margin({ bottom: 5 })
      Image($r('app.media.example_image'))
        .width(200)
        .objectFit(ImageFit.WidthFix)
        .border({ width: 1, color: Color.Gray })
        .margin(10)
      
      // 适应组件高度,保持宽高比
      Text('HeightFix(高度固定)')
        .fontSize(14)
        .margin({ bottom: 5 })
      Image($r('app.media.example_image'))
        .height(200)
        .objectFit(ImageFit.HeightFix)
        .border({ width: 1, color: Color.Gray })
        .margin(10)
    }
    .padding(20)
    .width('100%')
  }
}

3.3 图片加载状态和缓存

处理图片加载过程中的不同状态,以及设置图片缓存策略:

typescript 复制代码
@Entry
@Component
struct ImageStateExample {
  @State imageSrc: string = 'https://example.com/large-image.jpg';
  @State loadStatus: string = 'loading';
  
  build() {
    Column() {
      // 显示加载状态
      Text(`图片加载状态: ${this.loadStatus}`)
        .fontSize(16)
        .margin({ bottom: 10 })
      
      // 设置图片加载过程的回调
      Image(this.imageSrc)
        .width(300)
        .height(200)
        .objectFit(ImageFit.Cover)
        .margin(10)
        .onComplete(() => {
          this.loadStatus = 'success';
          console.log('图片加载成功');
        })
        .onError(() => {
          this.loadStatus = 'error';
          console.log('图片加载失败');
        })
      
      // 加载失败时显示的占位图
      if (this.loadStatus === 'error') {
        Image($r('app.media.placeholder'))
          .width(300)
          .height(200)
          .objectFit(ImageFit.Cover)
          .margin(10)
      }
      
      // 重试按钮
      Button('重新加载')
        .onClick(() => {
          this.loadStatus = 'loading';
          // 实际应用中,可能需要添加防抖动逻辑
          setTimeout(() => {
            this.imageSrc = 'https://example.com/large-image.jpg?' + Date.now(); // 强制重新加载
          }, 100);
        })
        .margin(10)
    }
    .padding(20)
    .width('100%')
  }
}

3.4 图片裁剪和形状设置

可以通过样式属性设置图片的裁剪区域和显示形状:

typescript 复制代码
@Entry
@Component
struct ImageShapeExample {
  build() {
    Column() {
      // 圆形图片
      Text('圆形图片')
        .fontSize(16)
        .margin({ bottom: 5 })
      Image($r('app.media.avatar'))
        .width(150)
        .height(150)
        .borderRadius(75)
        .margin(10)
      
      // 圆角矩形图片
      Text('圆角矩形图片')
        .fontSize(16)
        .margin({ bottom: 5 })
      Image($r('app.media.example_image'))
        .width(300)
        .height(200)
        .borderRadius(20)
        .margin(10)
      
      // 带边框的图片
      Text('带边框的图片')
        .fontSize(16)
        .margin({ bottom: 5 })
      Image($r('app.media.example_image'))
        .width(300)
        .height(200)
        .border({ width: 3, color: Color.Blue, style: BorderStyle.Dashed })
        .margin(10)
      
      // 阴影效果
      Text('带阴影的图片')
        .fontSize(16)
        .margin({ bottom: 5 })
      Image($r('app.media.example_image'))
        .width(300)
        .height(200)
        .shadow({ radius: 10, color: Color.Gray, offsetX: 5, offsetY: 5 })
        .margin(10)
    }
    .padding(20)
    .width('100%')
  }
}

3.5 图片交互

为Image组件添加交互功能,如点击、长按等:

typescript 复制代码
@Entry
@Component
struct ImageInteractionExample {
  @State isExpanded: boolean = false;
  @State rotationAngle: number = 0;
  
  build() {
    Column() {
      // 可点击和旋转的图片
      Image($r('app.media.example_image'))
        .width(this.isExpanded ? 400 : 200)
        .height(this.isExpanded ? 300 : 150)
        .transform({ rotate: this.rotationAngle })
        .objectFit(ImageFit.Cover)
        .margin(10)
        .onClick(() => {
          // 点击切换大小
          this.isExpanded = !this.isExpanded;
          console.log('图片被点击,切换大小');
        })
        .onLongPress(() => {
          // 长按旋转
          this.rotationAngle += 90;
          console.log('图片被长按,旋转90度');
        })
      
      // 操作提示
      Text('点击切换大小,长按旋转')
        .fontSize(14)
        .fontColor(Color.Gray)
        .margin(10)
    }
    .padding(20)
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}

Image组件是构建视觉丰富界面的关键组件,通过合理设置图片的大小、缩放模式、形状和交互,开发者可以创建出吸引力强、用户体验良好的应用界面。在实际开发中,需要根据应用的具体需求选择合适的图片处理方式,同时注意图片加载性能和内存占用问题。

四、输入框:TextInput组件

TextInput组件用于接收用户输入的文本信息,是构建表单和用户交互界面的重要组件。ArkTS中的TextInput提供了丰富的配置选项,可以满足不同的输入需求。

4.1 TextInput基本用法

TextInput的基本用法是创建一个可编辑的文本框,并绑定状态变量以存储用户输入:

typescript 复制代码
@Entry
@Component
struct TextInputBasicExample {
  @State inputValue: string = '';
  
  build() {
    Column() {
      Text('基本文本输入')
        .fontSize(16)
        .margin({ bottom: 10 })
      
      TextInput({
        placeholder: '请输入文字...',
        text: this.inputValue
      })
        .onChange((value: string) => {
          this.inputValue = value;
        })
        .width('100%')
        .height(40)
        .padding(10)
        .backgroundColor('#f5f5f5')
        .borderRadius(4)
        .margin({ bottom: 20 })
      
      // 显示输入内容
      Text(`输入内容: ${this.inputValue}`)
        .fontSize(14)
        .fontColor(Color.Blue)
    }
    .padding(20)
    .width('100%')
  }
}

4.2 输入类型和键盘配置

TextInput支持多种输入类型,可以根据需要配置不同的键盘类型和输入行为:

typescript 复制代码
@Entry
@Component
struct TextInputTypeExample {
  @State textValue: string = '';
  @State numberValue: string = '';
  @State passwordValue: string = '';
  @State emailValue: string = '';
  
  build() {
    Column() {
      // 普通文本输入
      Text('普通文本')
        .fontSize(14)
        .margin({ bottom: 5 })
      TextInput({
        placeholder: '请输入普通文本',
        text: this.textValue
      })
        .onChange((value: string) => {
          this.textValue = value;
        })
        .type(InputType.Normal)
        .width('100%')
        .height(40)
        .padding(10)
        .backgroundColor('#f5f5f5')
        .margin({ bottom: 15 })
      
      // 数字输入
      Text('数字输入')
        .fontSize(14)
        .margin({ bottom: 5 })
      TextInput({
        placeholder: '请输入数字',
        text: this.numberValue
      })
        .onChange((value: string) => {
          this.numberValue = value;
        })
        .type(InputType.Number)
        .width('100%')
        .height(40)
        .padding(10)
        .backgroundColor('#f5f5f5')
        .margin({ bottom: 15 })
      
      // 密码输入
      Text('密码输入')
        .fontSize(14)
        .margin({ bottom: 5 })
      TextInput({
        placeholder: '请输入密码',
        text: this.passwordValue
      })
        .onChange((value: string) => {
          this.passwordValue = value;
        })
        .type(InputType.Password)
        .width('100%')
        .height(40)
        .padding(10)
        .backgroundColor('#f5f5f5')
        .margin({ bottom: 15 })
      
      // 邮箱输入(带自动补全)
      Text('邮箱输入')
        .fontSize(14)
        .margin({ bottom: 5 })
      TextInput({
        placeholder: '请输入邮箱地址',
        text: this.emailValue
      })
        .onChange((value: string) => {
          this.emailValue = value;
        })
        .type(InputType.Email)
        .width('100%')
        .height(40)
        .padding(10)
        .backgroundColor('#f5f5f5')
        .margin({ bottom: 15 })
    }
    .padding(20)
    .width('100%')
  }
}

4.3 输入框样式和装饰

可以为TextInput添加各种样式和装饰,使其更加美观和易用:

typescript 复制代码
@Entry
@Component
struct TextInputStyleExample {
  @State inputValue: string = '';
  
  build() {
    Column() {
      // 带边框的输入框
      Text('带边框的输入框')
        .fontSize(14)
        .margin({ bottom: 5 })
      TextInput({
        placeholder: '请输入文字'
      })
        .width('100%')
        .height(40)
        .padding(10)
        .border({ width: 1, color: Color.Blue })
        .borderRadius(4)
        .margin({ bottom: 15 })
      
      // 带圆角和阴影的输入框
      Text('带圆角和阴影的输入框')
        .fontSize(14)
        .margin({ bottom: 5 })
      TextInput({
        placeholder: '请输入文字'
      })
        .width('100%')
        .height(40)
        .padding(10)
        .borderRadius(20)
        .shadow({ radius: 3, color: Color.Gray, offsetX: 0, offsetY: 2 })
        .margin({ bottom: 15 })
      
      // 带图标前缀的输入框
      Text('带图标前缀的输入框')
        .fontSize(14)
        .margin({ bottom: 5 })
      Flex({
        alignItems: ItemAlign.Center
      })
        .width('100%')
        .height(40)
        .padding({ left: 10, right: 10 })
        .backgroundColor('#f5f5f5')
        .borderRadius(4)
      {
        Image($r('app.media.ic_user'))
          .width(20)
          .height(20)
          .margin({ right: 8 })
        
        TextInput({
          placeholder: '请输入用户名'
        })
          .width('100%')
          .height('100%')
          .backgroundColor(Color.Transparent)
          .padding(0)
      }
      .margin({ bottom: 15 })
    }
    .padding(20)
    .width('100%')
  }
}

4.4 输入验证和格式化

对用户输入进行验证和格式化,提升用户体验:

typescript 复制代码
@Entry
@Component
struct TextInputValidationExample {
  @State phoneNumber: string = '';
  @State isValidPhone: boolean = true;
  @State formattedPhone: string = '';
  
  // 验证手机号并格式化
  validateAndFormatPhone(phone: string) {
    // 移除非数字字符
    const cleaned = phone.replace(/\D/g, '');
    
    // 简单的中国大陆手机号验证(11位数字)
    if (/^1[3-9]\d{9}$/.test(cleaned)) {
      this.isValidPhone = true;
      // 格式化为 xxx-xxxx-xxxx 格式
      this.formattedPhone = cleaned.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3');
    } else {
      this.isValidPhone = cleaned.length > 0;
      this.formattedPhone = phone;
    }
    
    this.phoneNumber = cleaned;
  }
  
  build() {
    Column() {
      Text('手机号输入(带验证和格式化)')
        .fontSize(14)
        .margin({ bottom: 5 })
      
      TextInput({
        placeholder: '请输入11位手机号码',
        text: this.formattedPhone
      })
        .onChange((value: string) => {
          this.validateAndFormatPhone(value);
        })
        .width('100%')
        .height(40)
        .padding(10)
        .backgroundColor('#f5f5f5')
        .borderRadius(4)
        .border({ 
          width: 1, 
          color: this.isValidPhone ? '#f5f5f5' : Color.Red 
        })
        .margin({ bottom: 10 })
      
      // 错误提示
      if (!this.isValidPhone && this.phoneNumber.length > 0) {
        Text('请输入有效的11位手机号码')
          .fontSize(12)
          .fontColor(Color.Red)
          .margin({ bottom: 20 })
      }
      
      // 提交按钮(根据验证结果启用/禁用)
      Button('提交')
        .onClick(() => {
          if (this.isValidPhone && this.phoneNumber.length === 11) {
            console.log('提交手机号:', this.phoneNumber);
            // 执行提交操作
          }
        })
        .enabled(this.isValidPhone && this.phoneNumber.length === 11)
        .width('100%')
    }
    .padding(20)
    .width('100%')
  }
}

五、滑块:Slider组件

Slider组件用于让用户通过滑动来选择一个数值,常用于音量调节、亮度设置等场景。ArkTS中的Slider组件支持水平和垂直两种方向,以及多种样式定制。

5.1 Slider基本用法

创建一个基本的滑块组件,获取用户选择的值:

typescript 复制代码
@Entry
@Component
struct SliderBasicExample {
  @State progressValue: number = 50;
  
  build() {
    Column() {
      Text('基本滑块')
        .fontSize(16)
        .margin({ bottom: 10 })
      
      Slider({
        value: this.progressValue,
        min: 0,
        max: 100,
        step: 1
      })
        .onChange((value: number, mode: SliderChangeMode) => {
          console.log('滑块当前值:', value);
          if (mode === SliderChangeMode.End) {
            // 滑动结束后更新值
            this.progressValue = value;
          }
        })
        .width('100%')
        .height(40)
        .margin({ bottom: 10 })
      
      // 显示当前值
      Text(`当前值: ${this.progressValue}`)
        .fontSize(14)
        .fontColor(Color.Blue)
    }
    .padding(20)
    .width('100%')
  }
}

5.2 Slider样式和方向

自定义Slider的样式和方向,以适应不同的界面设计需求:

typescript 复制代码
@Entry
@Component
struct SliderStyleExample {
  @State horizontalValue: number = 30;
  @State verticalValue: number = 60;
  
  build() {
    Column() {
      // 水平滑块(自定义颜色)
      Text('水平滑块(自定义颜色)')
        .fontSize(14)
        .margin({ bottom: 5 })
      Slider({
        value: this.horizontalValue,
        min: 0,
        max: 100,
        step: 1
      })
        .onChange((value: number) => {
          this.horizontalValue = value;
        })
        .width('100%')
        .blockColor(Color.Blue)        // 滑块颜色
        .trackColor(Color.LightGray)   // 轨道颜色
        .selectedColor(Color.Green)    // 已选择部分颜色
        .height(40)
        .margin({ bottom: 30 })
      
      // 垂直滑块
      Flex({
        justifyContent: FlexAlign.Center
      })
        .width('100%')
        .height(200)
      {
        Column() {
          Text('垂直滑块')
            .fontSize(14)
            .margin({ bottom: 10 })
          
          Slider({
            value: this.verticalValue,
            min: 0,
            max: 100,
            step: 1,
            mode: SliderMode.OutSet
          })
            .onChange((value: number) => {
              this.verticalValue = value;
            })
            .direction(Axis.Vertical)
            .width(40)
            .height(150)
            .blockColor(Color.Red)
            .trackColor(Color.LightGray)
            .selectedColor(Color.Orange)
        }
      }
    }
    .padding(20)
    .width('100%')
  }
}

5.3 Slider交互模式

Slider提供了不同的交互模式,以满足不同的用户体验需求:

typescript 复制代码
@Entry
@Component
struct SliderInteractionExample {
  @State value1: number = 25;
  @State value2: number = 50;
  @State value3: number = 75;
  
  build() {
    Column() {
      // 内嵌模式(滑块在轨道内)
      Text('内嵌模式')
        .fontSize(14)
        .margin({ bottom: 5 })
      Slider({
        value: this.value1,
        min: 0,
        max: 100,
        step: 5,
        mode: SliderMode.InSet
      })
        .onChange((value: number) => {
          this.value1 = value;
        })
        .width('100%')
        .height(40)
        .margin({ bottom: 20 })
      
      // 外嵌模式(滑块在轨道外)
      Text('外嵌模式')
        .fontSize(14)
        .margin({ bottom: 5 })
      Slider({
        value: this.value2,
        min: 0,
        max: 100,
        step: 5,
        mode: SliderMode.OutSet
      })
        .onChange((value: number) => {
          this.value2 = value;
        })
        .width('100%')
        .height(40)
        .margin({ bottom: 20 })
      
      // 刻度模式
      Text('刻度模式')
        .fontSize(14)
        .margin({ bottom: 5 })
      Slider({
        value: this.value3,
        min: 0,
        max: 100,
        step: 25,
        mode: SliderMode.OutSet
      })
        .onChange((value: number) => {
          this.value3 = value;
        })
        .width('100%')
        .height(40)
        .showTips(true)           // 显示提示值
        .showSteps(true)          // 显示刻度
        .showTickMarks(true)      // 显示刻度标记
        .margin({ bottom: 20 })
    }
    .padding(20)
    .width('100%')
  }
}

5.4 Slider实际应用示例

展示一个结合Slider的实际应用场景,如音量调节:

typescript 复制代码
@Entry
@Component
struct VolumeControlExample {
  @State volume: number = 70;
  @State isMuted: boolean = false;
  
  // 获取音量图标
  getVolumeIcon(): Resource {
    if (this.isMuted || this.volume === 0) {
      return $r('app.media.ic_mute');
    } else if (this.volume < 30) {
      return $r('app.media.ic_volume_low');
    } else if (this.volume < 70) {
      return $r('app.media.ic_volume_medium');
    } else {
      return $r('app.media.ic_volume_high');
    }
  }
  
  // 切换静音状态
  toggleMute() {
    this.isMuted = !this.isMuted;
    // 在实际应用中,这里会调用系统API来控制音量
    console.log(this.isMuted ? '静音' : '取消静音');
  }
  
  build() {
    Column() {
      Text('音量调节示例')
        .fontSize(16)
        .margin({ bottom: 20 })
      
      Flex({
        alignItems: ItemAlign.Center,
        justifyContent: FlexAlign.Center
      })
        .width('100%')
        .margin({ bottom: 20 })
      {
        // 音量图标按钮
        Button()
          .onClick(() => {
            this.toggleMute();
          })
          .type(ButtonType.Circle)
          .size({ width: 48, height: 48 })
          .margin({ right: 10 })
        {
          Image(this.getVolumeIcon())
            .width(24)
            .height(24)
        }
        
        // 音量滑块
        Slider({
          value: this.volume,
          min: 0,
          max: 100,
          step: 1
        })
          .onChange((value: number) => {
            this.volume = value;
            // 如果设置了音量,自动取消静音状态
            if (value > 0 && this.isMuted) {
              this.isMuted = false;
            }
            console.log('音量设置为:', value);
          })
          .width('80%')
          .blockColor(Color.Blue)
          .selectedColor(Color.Blue)
          .trackColor(Color.LightGray)
          .showTips(true)
      }
      
      // 预设音量按钮
      Flex({
        justifyContent: FlexAlign.SpaceAround
      })
        .width('100%')
      {
        Button('低')
          .onClick(() => {
            this.volume = 30;
            this.isMuted = false;
          })
          .width(60)
        
        Button('中')
          .onClick(() => {
            this.volume = 60;
            this.isMuted = false;
          })
          .width(60)
        
        Button('高')
          .onClick(() => {
            this.volume = 90;
            this.isMuted = false;
          })
          .width(60)
      }
    }
    .padding(20)
    .width('100%')
    .height(200)
    .justifyContent(FlexAlign.Center)
  }
}

TextInput和Slider组件是构建交互式应用界面的重要基础组件。TextInput用于文本输入,支持多种输入类型和样式定制;Slider用于数值选择,提供了灵活的方向和交互模式。通过合理使用这些组件,开发者可以创建出用户友好的输入和选择界面,提升应用的交互体验。

六、组件组合使用的示例和最佳实践

在实际应用开发中,我们很少单独使用某个组件,而是将多个组件组合在一起,构建完整的用户界面。下面,我们将通过一个用户信息编辑表单的示例,展示如何在ArkTS中组合使用各种基础组件。

6.1 用户信息编辑表单示例

typescript 复制代码
@Entry
@Component
struct UserProfileFormExample {
  // 用户信息状态
  @State username: string = '张小明';
  @State email: string = 'zhangxm@example.com';
  @State bio: string = '热爱鸿蒙开发的技术爱好者';
  @State avatarPath: string = $r('app.media.avatar_default');
  
  // 隐私设置
  @State privacyLevel: number = 75; // 隐私级别(0-100)
  @State receiveNotifications: boolean = true;
  @State enableDarkMode: boolean = false;
  
  // 表单验证状态
  @State isFormValid: boolean = true;
  @State errorMessage: string = '';
  
  // 验证表单数据
  validateForm(): boolean {
    if (!this.username || this.username.trim().length < 2) {
      this.errorMessage = '用户名至少需要2个字符';
      return false;
    }
    
    // 简单邮箱验证
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!this.email || !emailRegex.test(this.email)) {
      this.errorMessage = '请输入有效的邮箱地址';
      return false;
    }
    
    this.errorMessage = '';
    return true;
  }
  
  // 保存用户信息
  saveUserInfo() {
    if (this.validateForm()) {
      console.log('保存用户信息:', {
        username: this.username,
        email: this.email,
        bio: this.bio,
        privacyLevel: this.privacyLevel,
        receiveNotifications: this.receiveNotifications,
        enableDarkMode: this.enableDarkMode
      });
      // 在实际应用中,这里会调用API保存数据到服务器
      this.isFormValid = true;
    } else {
      this.isFormValid = false;
    }
  }
  
  // 处理头像上传(模拟)
  handleAvatarUpload() {
    // 模拟选择新头像
    console.log('选择新头像');
    // 在实际应用中,这里会调用系统的图片选择器
    // this.avatarPath = newAvatarPath;
  }
  
  build() {
    Scroll() {
      Column() {
        // 页面标题
        Text('编辑个人信息')
          .fontSize(24)
          .fontWeight(FontWeight.Bold)
          .margin({ bottom: 20 })
          
        // 错误提示
        if (!this.isFormValid) {
          Text(this.errorMessage)
            .fontSize(14)
            .fontColor(Color.Red)
            .margin({ bottom: 15 })
        }
        
        // 头像区域
        Column() {
          Image(this.avatarPath)
            .width(100)
            .height(100)
            .objectFit(ImageFit.Cover)
            .borderRadius(50)
            .margin({ bottom: 10 })
            
          Button('更换头像')
            .onClick(() => {
              this.handleAvatarUpload();
            })
            .type(ButtonType.Capsule)
            .width(120)
            .height(36)
            .fontSize(14)
        }
        .margin({ bottom: 30 })
        
        // 基本信息表单
        Text('基本信息')
          .fontSize(18)
          .fontWeight(FontWeight.Medium)
          .margin({ bottom: 15 })
          
        // 用户名输入
        Column() {
          Text('用户名')
            .fontSize(14)
            .margin({ bottom: 5 })
          TextInput({
            placeholder: '请输入用户名',
            text: this.username
          })
            .onChange((value: string) => {
              this.username = value;
            })
            .width('100%')
            .height(48)
            .padding(10)
            .borderRadius(8)
            .backgroundColor('#f5f5f5')
        }
        .margin({ bottom: 20 })
        
        // 邮箱输入
        Column() {
          Text('邮箱')
            .fontSize(14)
            .margin({ bottom: 5 })
          TextInput({
            placeholder: '请输入邮箱地址',
            text: this.email
          })
            .onChange((value: string) => {
              this.email = value;
            })
            .type(InputType.Email)
            .width('100%')
            .height(48)
            .padding(10)
            .borderRadius(8)
            .backgroundColor('#f5f5f5')
        }
        .margin({ bottom: 20 })
        
        // 个人简介输入
        Column() {
          Text('个人简介')
            .fontSize(14)
            .margin({ bottom: 5 })
          TextInput({
            placeholder: '介绍一下自己吧...',
            text: this.bio
          })
            .onChange((value: string) => {
              this.bio = value;
            })
            .width('100%')
            .height(100)
            .padding(10)
            .borderRadius(8)
            .backgroundColor('#f5f5f5')
            .textAlign(TextAlign.Start)
            .placeholderColor('#999')
        }
        .margin({ bottom: 30 })
        
        // 偏好设置
        Text('偏好设置')
          .fontSize(18)
          .fontWeight(FontWeight.Medium)
          .margin({ bottom: 15 })
          
        // 隐私级别设置(使用Slider)
        Column() {
          Text('隐私保护级别')
            .fontSize(14)
            .margin({ bottom: 5 })
            
          Row() {
            Text('低')
              .fontSize(12)
              .margin({ right: 5 })
              
            Slider({
              value: this.privacyLevel,
              min: 0,
              max: 100,
              step: 1
            })
              .onChange((value: number) => {
                this.privacyLevel = value;
              })
              .width('80%')
              .height(40)
              .showTips(true)
              
            Text('高')
              .fontSize(12)
              .margin({ left: 5 })
          }
          
          // 隐私级别描述
          Text(this.privacyLevel < 33 ? '公开模式:所有人可见您的个人信息' : 
               this.privacyLevel < 66 ? '标准模式:好友可见您的个人信息' : 
               '私密模式:仅自己可见详细个人信息')
            .fontSize(12)
            .fontColor(Color.Gray)
            .margin({ top: 5 })
        }
        .margin({ bottom: 20 })
        
        // 通知设置(使用开关)
        Row() {
          Text('接收推送通知')
            .fontSize(14)
            .margin({ right: 'auto' })
            
          Toggle()
            .onChange((isOn: boolean) => {
              this.receiveNotifications = isOn;
            })
            .selected(this.receiveNotifications)
        }
        .margin({ bottom: 15 })
        
        // 深色模式设置(使用开关)
        Row() {
          Text('启用深色模式')
            .fontSize(14)
            .margin({ right: 'auto' })
            
          Toggle()
            .onChange((isOn: boolean) => {
              this.enableDarkMode = isOn;
              // 在实际应用中,这里会切换应用的主题模式
            })
            .selected(this.enableDarkMode)
        }
        .margin({ bottom: 30 })
        
        // 操作按钮
        Flex({
          justifyContent: FlexAlign.SpaceBetween
        })
          .width('100%')
          .margin({ bottom: 30 })
        {
          Button('取消')
            .onClick(() => {
              // 重置表单或返回上一页
              console.log('取消编辑');
            })
            .type(ButtonType.Capsule)
            .backgroundColor(Color.White)
            .fontColor(Color.Blue)
            .border({ width: 1, color: Color.Blue })
            .width('48%')
            .height(44)
          
          Button('保存')
            .onClick(() => {
              this.saveUserInfo();
            })
            .type(ButtonType.Capsule)
            .backgroundColor(Color.Blue)
            .width('48%')
            .height(44)
        }
      }
      .padding(20)
      .width('100%')
      .alignItems(HorizontalAlign.Start)
    }
    .backgroundColor(Color.White)
  }
}

6.2 组件组合使用的最佳实践

在组合使用鸿蒙基础组件时,遵循以下最佳实践可以帮助你构建出高性能、易维护且用户体验良好的应用:

6.2.1 状态管理
  • 集中管理相关状态:将相关的状态变量组织在一起,例如将表单的所有字段状态放在一个结构体中
  • 避免不必要的状态更新:确保状态更新只会在必要时触发UI重新渲染
  • 使用计算属性:对于基于其他状态派生的值,考虑使用计算属性而非直接状态
typescript 复制代码
// 优化前
@State username: string = '';
@State email: string = '';
@State isValid: boolean = false;

// 每次更新username或email时都需要手动更新isValid

// 优化后
class UserForm {
  username: string = '';
  email: string = '';
}

@State userForm: UserForm = new UserForm();

// 使用computed计算属性
@Computed get isValid(): boolean {
  return this.userForm.username.length > 0 && 
         /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.userForm.email);
}
6.2.2 组件布局和样式
  • 使用容器组件组织布局:合理使用Column、Row、Flex等容器组件来构建整体布局
  • 避免嵌套过深:过多的嵌套会影响渲染性能,尽量保持组件树扁平化
  • 样式复用:对于重复的样式,考虑使用样式类或常量进行复用
typescript 复制代码
// 样式复用示例
const commonInputStyle = {
  width: '100%',
  height: 48,
  padding: 10,
  borderRadius: 8,
  backgroundColor: '#f5f5f5'
};

// 使用方式
TextInput()
  .width(commonInputStyle.width)
  .height(commonInputStyle.height)
  .padding(commonInputStyle.padding)
  .borderRadius(commonInputStyle.borderRadius)
  .backgroundColor(commonInputStyle.backgroundColor);
6.2.3 性能优化
  • 图片资源优化:合理设置图片大小和加载策略,避免不必要的大图加载
  • 懒加载和按需渲染:对于列表和长内容,使用懒加载和按需渲染技术
  • 避免在build方法中进行复杂计算:将复杂计算移到生命周期方法或事件处理器中
typescript 复制代码
// 优化前
build() {
  Column() {
    // 在build中进行复杂计算
    const result = this.performHeavyCalculation();
    Text(`计算结果: ${result}`)
  }
}

// 优化后
@State calculationResult: number = 0;

onPageShow() {
  // 在生命周期方法中进行计算
  this.calculationResult = this.performHeavyCalculation();
}

build() {
  Column() {
    Text(`计算结果: ${this.calculationResult}`)
  }
}
6.2.4 用户体验优化
  • 提供即时反馈:表单验证、按钮点击等操作应提供明确的视觉反馈
  • 考虑可访问性:为组件添加适当的语义标签和无障碍属性
  • 处理边缘情况:考虑网络错误、空数据等边缘情况,并提供友好的提示
typescript 复制代码
// 添加加载状态和错误处理
@State isLoading: boolean = false;
@State error: string = '';

async submitForm() {
  if (!this.validateForm()) return;
  
  this.isLoading = true;
  this.error = '';
  
  try {
    await this.api.submitUserData(this.userData);
    // 成功处理
  } catch (err) {
    this.error = '提交失败,请重试';
    console.error('提交错误:', err);
  } finally {
    this.isLoading = false;
  }
}

build() {
  // ...
  Button(this.isLoading ? '提交中...' : '提交')
    .onClick(() => this.submitForm())
    .enabled(!this.isLoading)
    
  if (this.error) {
    Text(this.error)
      .fontColor(Color.Red)
  }
  // ...
}
6.2.5 代码组织和可维护性
  • 组件拆分:将复杂的UI拆分为更小、更专注的组件
  • 逻辑和视图分离:将业务逻辑和视图渲染分离,提高代码可维护性
  • 添加适当的注释:对于复杂的逻辑和组件交互,添加清晰的注释
typescript 复制代码
// 组件拆分示例
@Entry
@Component
struct UserProfileScreen {
  build() {
    Column() {
      ProfileHeader()
      ProfileForm()
      PreferenceSettings()
      ActionButtons()
    }
  }
}

// 各个子组件
@Component
struct ProfileHeader { /* ... */ }
@Component
struct ProfileForm { /* ... */ }
@Component
struct PreferenceSettings { /* ... */ }
@Component
struct ActionButtons { /* ... */ }

6.3 鸿蒙组件使用技巧

  1. 掌握组件生命周期:了解组件的生命周期方法(onPageShow、onPageHide等),合理利用它们进行初始化和清理工作

  2. 使用状态变量的双向绑定:对于表单输入等场景,充分利用@State和onChange实现数据的双向绑定

  3. 组件样式的优先级:了解不同样式设置方式的优先级,避免样式冲突

  4. 使用主题和颜色资源:通过资源文件统一管理颜色、字体等资源,便于主题切换和维护

  5. 组件的响应式布局:使用Flex、Grid等布局组件,创建适应不同屏幕尺寸的响应式界面

  6. 手势和事件处理:掌握ArkTS的手势识别和事件处理机制,实现流畅的交互体验

  7. 性能监控和优化:使用开发工具的性能分析功能,监控组件渲染性能,及时发现和解决性能瓶颈

通过合理组合使用鸿蒙基础组件,并遵循这些最佳实践,你可以构建出既美观又高效的应用界面,为用户提供良好的使用体验。在实际开发过程中,还需要根据具体的应用场景和需求,灵活调整组件的使用方式和实现策略。

相关推荐
猫林老师2 小时前
Flutter for HarmonyOS开发指南(八):国际化与本地化深度实践
flutter·华为·harmonyos
夏文强3 小时前
HarmonyOS开发-ArkWeb开发指导
华为·harmonyos
Georgewu5 小时前
【HarmonyOS 6】SpeechKit中的朗读控件,初始化前不进行窗口舞台的设置,也不会报错,与文档描述不符。
harmonyos
Georgewu5 小时前
【HarmonyOS 6】静态和动态添加应用快捷方式详解
harmonyos
爱笑的眼睛117 小时前
HarmonyOS preview 预览文件 Kit 的入门讲解(配套后端代码)
华为·harmonyos
挠到秃头的涛某11 小时前
华为防火墙web配置SSL-在外人员访问内网资源
运维·网络·网络协议·tcp/ip·华为·ssl·防火墙
爱笑的眼睛1113 小时前
HarmonyOS后台代理提醒机制深度解析:从架构设计到场景化实践
华为·harmonyos
猫林老师15 小时前
Flutter for HarmonyOS开发指南(七):插件开发与平台能力桥接
flutter·华为·harmonyos
kirk_wang15 小时前
HarmonyOS 6.0 服务卡片实战:把「轻食刻」装进桌面,让轻断食一眼可控
华为·harmonyos