Skip to content

ArkUI 声明式语法

ArkUI 采用声明式语法描述 UI,与 HTML/CSS 完全不同。每个页面是一个 .ets 文件,通过 ArkTS 代码描述界面结构。

基本结构

typescript
// Index.ets
@Entry
@Component
struct Index {
  // 1. 状态变量
  @State message: string = 'Hello HarmonyOS'

  // 2. 可选:UI 构建器
  @Builder
  header(text: string) {
    Text(text)
      .fontSize(24)
      .fontWeight(FontWeight.Bold)
  }

  // 3. build() 方法:描述 UI
  build() {
    Column() {
      this.header(this.message)

      Text('这是一段文本')
        .fontSize(16)
        .fontColor('#666666')

      Button('点击我')
        .onClick(() => {
          this.message = '你好,鸿蒙!'
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

关键规则

  1. 文件第一个页面@Entry @Component struct Xxx
  2. 非入口组件@Component struct Yyy(可被复用)
  3. build()有且仅有一个根容器
  4. 组件首字母大写TextButtonImage
  5. 样式用属性方法链(点号连接),不是 CSS

常用组件

文本组件 Text

Text 组件用于显示文本内容,支持丰富的字体样式和排版属性。

完整属性列表

属性类型说明
fontSizenumber / string / Resource字体大小,默认单位 vp
fontColorResourceColor字体颜色
fontWeightFontWeight / number / string字体粗细
fontStyleFontStyle字体样式(正常/斜体)
fontFamilystring / Resource字体族
textAlignTextAlign文本水平对齐方式
textOverflow{ overflow: TextOverflow }文本溢出处理方式
maxLinesnumber最大显示行数
lineHeightnumber / string / Resource行高
letterSpacingnumber / string字符间距
textCaseTextCase文本大小写转换
decoration{ type: TextDecorationType, color?: ResourceColor }文本装饰线
baselineOffsetnumber基线偏移量
minFontSizenumber / string / Resource最小字体大小
maxFontSizenumber / string / Resource最大字体大小
copyOptionCopyOptions文本复制选项
textSelectableboolean文本是否可选中
heightAdaptivePolicyTextHeightAdaptivePolicy文本高度自适应策略

代码示例

typescript
// 基础文本
Text('普通文本')
  .fontSize(16)
  .fontColor('#333333')

// 粗体文本
Text('粗体文本')
  .fontSize(20)
  .fontWeight(FontWeight.Bold)

// 完整字体样式
Text('带样式的文本')
  .fontSize(14)
  .fontColor(Color.Red)
  .fontStyle(FontStyle.Italic)
  .fontWeight(FontWeight.Medium)
  .fontFamily('HarmonyOS Sans')
  .decoration({ type: TextDecorationType.Underline, color: Color.Blue })

// 多行文本 + 省略号
Text('这是一段很长的文本,超出部分会显示省略号,用于演示文本溢出处理效果')
  .fontSize(14)
  .maxLines(2)
  .textOverflow({ overflow: TextOverflow.Ellipsis })
  .width('100%')

// 文本对齐
Text('居中对齐文本')
  .fontSize(16)
  .textAlign(TextAlign.Center)
  .width('100%')

// 行高与字间距
Text('行高与字间距示例\n第二行文本')
  .fontSize(14)
  .lineHeight(24)
  .letterSpacing(2)

// 文本大小写
Text('hello world')
  .fontSize(16)
  .textCase(TextCase.UpperCase)  // 转换为大写

// 使用 font 统一设置
Text('统一字体设置')
  .font({
    size: 20,
    weight: FontWeight.Bold,
    family: 'HarmonyOS Sans',
    style: FontStyle.Normal
  })

Span 子组件(富文本)

typescript
Text() {
  Span('红色文本 ').fontColor(Color.Red).fontSize(16)
  Span('粗体文本 ').fontWeight(FontWeight.Bold).fontSize(16)
  Span('下划线文本').decoration({ type: TextDecorationType.Underline }).fontSize(16)
}

图片组件 Image

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

完整属性列表

属性类型说明
altstring / Resource加载占位图
objectFitImageFit图片填充模式
objectRepeatImageRepeat图片重复模式
interpolationImageInterpolation图片插值模式
renderModeImageRenderMode图片渲染模式
sourceSize{ width: number, height: number }图片源尺寸
matchTextDirectionboolean是否匹配文本方向
fitOriginalSizeboolean是否适应原始尺寸
fillColorResourceColor填充颜色
autoResizeboolean是否自动调整大小
syncLoadboolean是否同步加载
copyOptionCopyOptions复制选项
colorFilterColorFilter颜色滤镜
draggableboolean是否可拖拽
borderRadiusnumber / BorderRadiuses圆角
shadowShadowOptions阴影

ImageFit 枚举值

枚举值说明
ImageFit.Contain保持比例,完整显示
ImageFit.Cover保持比例,填满容器(可能裁剪)
ImageFit.Fill拉伸填满容器
ImageFit.None保持原始尺寸
ImageFit.ScaleDown保持比例,缩小或保持原始尺寸
ImageFit.Matrix使用矩阵变换(Image 组件不支持)

代码示例

typescript
// 本地图片(放 resources/base/media/ 目录)
Image($r('app.media.logo'))
  .width(100)
  .height(100)
  .objectFit(ImageFit.Cover)
  .borderRadius(8)

// 网络图片
Image('https://example.com/image.png')
  .width('100%')
  .height(200)
  .alt($r('app.media.placeholder'))  // 加载占位图
  .objectFit(ImageFit.Cover)

// 完整图片属性
Image($r('app.media.photo'))
  .width(200)
  .height(200)
  .objectFit(ImageFit.Cover)
  .interpolation(ImageInterpolation.High)  // 高质量插值
  .renderMode(ImageRenderMode.Original)     // 原始渲染模式
  .borderRadius({ topLeft: 8, topRight: 8, bottomLeft: 0, bottomRight: 0 })
  .shadow({ radius: 4, color: '#1F000000', offsetX: 0, offsetY: 2 })

// 图片填充模式对比
Column({ space: 10 }) {
  Image($r('app.media.demo'))
    .width(150)
    .height(100)
    .objectFit(ImageFit.Contain)
    .backgroundColor('#F0F0F0')
  
  Image($r('app.media.demo'))
    .width(150)
    .height(100)
    .objectFit(ImageFit.Cover)
    .backgroundColor('#F0F0F0')
    
  Image($r('app.media.demo'))
    .width(150)
    .height(100)
    .objectFit(ImageFit.Fill)
    .backgroundColor('#F0F0F0')
}

按钮组件 Button

Button 组件用于触发点击操作,支持多种类型和样式。

完整属性列表

属性类型说明
typeButtonType按钮类型
stateEffectboolean是否显示按压效果
fontSizenumber / string / Resource字体大小
fontWeightFontWeight / number / string字体粗细
fontColorResourceColor字体颜色
fontStyleFontStyle字体样式
fontFamilystring / Resource字体族
labelStyleLabelStyle标签样式
backgroundColorResourceColor背景颜色
borderRadiusnumber / BorderRadiuses圆角
borderBorderOptions边框

ButtonType 枚举值

枚举值说明
ButtonType.Capsule胶囊型按钮
ButtonType.Circle圆形按钮
ButtonType.Normal普通矩形按钮

代码示例

typescript
// 普通按钮
Button('普通按钮')
  .fontSize(16)
  .backgroundColor('#007DFF')
  .fontColor(Color.White)
  .width('80%')
  .height(40)

// 胶囊按钮
Button('胶囊按钮')
  .type(ButtonType.Capsule)
  .width('80%')
  .backgroundColor('#FF6B6B')
  .fontColor(Color.White)

// 圆形按钮
Button() {
  Image($r('app.media.icon_add'))
    .width(24)
    .height(24)
    .fillColor(Color.White)
}
.type(ButtonType.Circle)
.width(60)
.height(60)
.backgroundColor('#4ECDC4')

// 带图标的按钮
Button() {
  Row({ space: 8 }) {
    Image($r('app.media.icon_search'))
      .width(16)
      .height(16)
      .fillColor(Color.White)
    Text('搜索')
      .fontColor(Color.White)
      .fontSize(14)
  }
}
.width(120)
.height(40)
.backgroundColor('#007DFF')
.borderRadius(20)

// 禁用状态
Button('禁用按钮')
  .enabled(false)
  .backgroundColor('#CCCCCC')
  .fontColor('#999999')

// 点击事件
Button('点击我')
  .onClick(() => {
    console.info('按钮被点击')
  })

输入框 TextInput / TextArea

TextInput 用于单行文本输入,TextArea 用于多行文本输入。

TextInput 完整属性列表

属性类型说明
typeInputType输入类型
placeholderstring / Resource占位提示文本
placeholderColorResourceColor占位文本颜色
textstring输入文本内容
caretColorResourceColor光标颜色
maxLengthnumber最大输入长度
inputFilterstring / Resource输入过滤器(正则)
showPasswordIconboolean是否显示密码可见图标
showErrorstring错误提示文本
fontSizenumber / string / Resource字体大小
fontColorResourceColor字体颜色
fontWeightFontWeight / number / string字体粗细
fontStyleFontStyle字体样式
fontFamilystring / Resource字体族
textAlignTextAlign文本对齐方式
copyOptionCopyOptions复制选项
styleTextInputStyle输入框样式

InputType 枚举值

枚举值说明
InputType.Normal基本输入模式
InputType.Password密码输入模式
InputType.Number数字输入模式
InputType.PhoneNumber电话号码输入模式
InputType.Email邮箱地址输入模式
InputType.NumberDecimal带小数点的数字输入
InputType.NumberPassword数字密码输入
InputType.URLURL 输入模式
InputType.USER_NAME用户名输入模式
InputType.NewPassword新密码输入模式

代码示例

typescript
@State inputValue: string = ''
@State password: string = ''
@State phoneNumber: string = ''

// 基础输入框
TextInput({ placeholder: '请输入内容', text: this.inputValue })
  .type(InputType.Normal)
  .maxLength(20)
  .height(40)
  .backgroundColor('#F5F5F5')
  .borderRadius(8)
  .padding({ left: 12, right: 12 })
  .onChange((value: string) => {
    this.inputValue = value
  })

// 密码输入框
TextInput({ placeholder: '请输入密码', text: this.password })
  .type(InputType.Password)
  .maxLength(16)
  .showPasswordIcon(true)
  .height(40)
  .backgroundColor('#F5F5F5')
  .borderRadius(8)
  .onChange((value: string) => {
    this.password = value
  })

// 手机号输入框
TextInput({ placeholder: '请输入手机号', text: this.phoneNumber })
  .type(InputType.PhoneNumber)
  .maxLength(11)
  .inputFilter('[0-9]*')  // 只允许输入数字
  .height(40)
  .backgroundColor('#F5F5F5')
  .borderRadius(8)
  .onChange((value: string) => {
    this.phoneNumber = value
  })

// 带错误提示的输入框
TextInput({ placeholder: '请输入邮箱', text: $$this.inputValue })
  .type(InputType.Email)
  .showError('邮箱格式不正确')
  .height(40)

// 多行文本输入
TextArea({ placeholder: '请输入描述', text: this.inputValue })
  .height(100)
  .maxLength(200)
  .backgroundColor('#F5F5F5')
  .borderRadius(8)
  .onChange((value: string) => {
    this.inputValue = value
  })

// 输入框焦点事件
TextInput({ placeholder: '焦点事件演示' })
  .onFocus(() => {
    console.info('输入框获得焦点')
  })
  .onBlur(() => {
    console.info('输入框失去焦点')
  })
  .onSubmit((enterKey: EnterKeyType) => {
    console.info(`点击了提交键: ${enterKey}`)
  })

开关组件 Toggle / Switch / Checkbox / Radio

Toggle 组件

Toggle 是一个通用开关组件,可以显示为开关、复选框或单选按钮。

属性类型说明
typeToggleType开关类型
isOnboolean是否选中
selectedColorResourceColor选中状态颜色
switchPointColorResourceColor开关圆点颜色

ToggleType 枚举值

枚举值说明
ToggleType.Switch开关样式
ToggleType.Checkbox复选框样式
ToggleType.Button按钮样式

代码示例

typescript
@State isOn: boolean = false
@State isChecked: boolean = true
@State selectedOption: string = 'option1'

// Switch 开关
Toggle({ type: ToggleType.Switch, isOn: this.isOn })
  .selectedColor('#007DFF')
  .switchPointColor(Color.White)
  .onChange((isOn: boolean) => {
    this.isOn = isOn
    console.info(`开关状态: ${isOn}`)
  })

// Checkbox 复选框
Toggle({ type: ToggleType.Checkbox, isOn: this.isChecked })
  .selectedColor('#007DFF')
  .size({ width: 20, height: 20 })
  .onChange((isOn: boolean) => {
    this.isChecked = isOn
  })

// Checkbox 带文本
Row({ space: 8 }) {
  Toggle({ type: ToggleType.Checkbox, isOn: this.isChecked })
    .selectedColor('#007DFF')
    .size({ width: 20, height: 20 })
  Text('同意用户协议')
    .fontSize(14)
    .fontColor('#666666')
}
.onClick(() => {
  this.isChecked = !this.isChecked
})

// Radio 单选按钮(使用 ToggleType.Button 配合分组)
Column({ space: 12 }) {
  Row({ space: 8 }) {
    Toggle({ type: ToggleType.Button, isOn: this.selectedOption === 'option1' })
      .selectedColor('#007DFF')
      .onChange(() => {
        this.selectedOption = 'option1'
      })
    Text('选项一')
      .fontSize(14)
  }
  
  Row({ space: 8 }) {
    Toggle({ type: ToggleType.Button, isOn: this.selectedOption === 'option2' })
      .selectedColor('#007DFF')
      .onChange(() => {
        this.selectedOption = 'option2'
      })
    Text('选项二')
      .fontSize(14)
  }
}

滑块与进度组件 Slider / Progress / LoadingProgress

Slider 滑块

属性类型说明
valuenumber当前值
minnumber最小值
maxnumber最大值
stepnumber步长
styleSliderStyle滑块样式
directionAxis方向
reverseboolean是否反向
trackColorResourceColor轨道颜色
selectedColorResourceColor已选轨道颜色
blockColorResourceColor滑块颜色
showStepsboolean是否显示步长标记
showTipsboolean是否显示提示

SliderStyle 枚举值

枚举值说明
SliderStyle.OutSet滑块在轨道外
SliderStyle.InSet滑块在轨道内
SliderStyle.None无滑块

Progress 进度条

属性类型说明
valuenumber当前值
totalnumber总值
typeProgressType进度条类型
colorResourceColor进度颜色
styleProgressStyle进度条样式

ProgressType 枚举值

枚举值说明
ProgressType.Linear线性进度条
ProgressType.Ring环形进度条
ProgressType.Eclipse椭圆形进度条
ProgressType.ScaleRing刻度环形进度条
ProgressType.Capsule胶囊型进度条

代码示例

typescript
@State sliderValue: number = 50
@State progressValue: number = 30

// 基础滑块
Slider({ value: this.sliderValue, min: 0, max: 100, step: 1 })
  .width('90%')
  .selectedColor('#007DFF')
  .trackColor('#E0E0E0')
  .blockColor('#007DFF')
  .onChange((value: number, mode: SliderChangeMode) => {
    this.sliderValue = value
    console.info(`滑块值: ${value}, 模式: ${mode}`)
  })

// 带步长标记的滑块
Slider({ value: this.sliderValue, min: 0, max: 100, step: 10 })
  .width('90%')
  .showSteps(true)
  .showTips(true)
  .selectedColor('#007DFF')

// 线性进度条
Progress({ value: this.progressValue, total: 100, type: ProgressType.Linear })
  .width('90%')
  .height(8)
  .color('#007DFF')

// 环形进度条
Progress({ value: this.progressValue, total: 100, type: ProgressType.Ring })
  .width(80)
  .height(80)
  .color('#007DFF')

// 加载进度条
LoadingProgress()
  .width(50)
  .height(50)
  .color('#007DFF')

// 加载进度条带文字
Column({ space: 8 }) {
  LoadingProgress()
    .width(40)
    .height(40)
    .color('#007DFF')
  Text('加载中...')
    .fontSize(14)
    .fontColor('#999999')
}

分隔与空白组件 Divider / Blank / Spacer

Divider 分隔线

属性类型说明
verticalboolean是否为垂直分隔线
colorResourceColor分隔线颜色
strokeWidthnumber / string线宽
lineCapLineCapStyle线端样式

Blank 空白填充

Blank 组件用于在 Row/Column/Flex 中填充空白区域,将其他组件推到一侧。

属性类型说明
colorResourceColor空白区域颜色
minnumber / string最小尺寸

Spacer 固定间距

Spacer 用于在布局中创建固定大小的间距。

代码示例

typescript
// 水平分隔线
Divider()
  .width('100%')
  .height(1)
  .color('#E0E0E0')

// 垂直分隔线
Row({ space: 16 }) {
  Text('左侧内容')
  Divider()
    .vertical(true)
    .width(1)
    .height(20)
    .color('#E0E0E0')
  Text('右侧内容')
}

// 使用 Blank 实现两端对齐
Row() {
  Text('左侧标题')
    .fontSize(16)
  Blank()  // 填充中间空白
  Text('右侧操作')
    .fontSize(14)
    .fontColor('#007DFF')
}
.width('100%')
.padding(16)

// 使用 Spacer 创建固定间距
Column() {
  Text('上方内容')
  Spacer().height(20)  // 20vp 间距
  Text('下方内容')
}

滚动与轮播组件 Scroll / Swiper

Scroll 滚动容器

属性类型说明
scrollableScrollDirection滚动方向
scrollBarBarState滚动条状态
scrollBarColorResourceColor滚动条颜色
scrollBarWidthnumber / string滚动条宽度
edgeEffectEdgeEffect边缘效果
nestedScrollNestedScrollOptions嵌套滚动配置

Swiper 轮播组件

属性类型说明
indexnumber当前索引
autoPlayboolean是否自动播放
intervalnumber自动播放间隔(ms)
loopboolean是否循环
indicatorboolean / DotIndicator / DigitIndicator指示器
durationnumber切换动画时长
verticalboolean是否为纵向
itemSpacenumber / string子组件间距
displayModeSwiperDisplayMode显示模式
cachedCountnumber缓存数量
curveCurve / ICurve动画曲线
disableSwipeboolean是否禁用滑动

代码示例

typescript
@State scrollOffset: number = 0
@State swiperIndex: number = 0

// 垂直滚动
Scroll() {
  Column({ space: 16 }) {
    ForEach([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], (item: number) => {
      Text(`滚动项 ${item}`)
        .width('100%')
        .height(60)
        .backgroundColor('#F5F5F5')
        .textAlign(TextAlign.Center)
    })
  }
  .width('100%')
}
.width('100%')
.height(300)
.scrollBar(BarState.Auto)
.edgeEffect(EdgeEffect.Spring)
.onScroll((xOffset: number, yOffset: number) => {
  console.info(`滚动偏移: x=${xOffset}, y=${yOffset}`)
})

// 横向滚动
Scroll() {
  Row({ space: 16 }) {
    ForEach([1, 2, 3, 4, 5], (item: number) => {
      Text(`标签 ${item}`)
        .width(100)
        .height(40)
        .backgroundColor('#F0F0F0')
        .textAlign(TextAlign.Center)
        .borderRadius(20)
    })
  }
  .padding(16)
}
.scrollable(ScrollDirection.Horizontal)
.scrollBar(BarState.Off)

// 基础轮播
Swiper() {
  ForEach([1, 2, 3, 4], (item: number) => {
    Text(`轮播项 ${item}`)
      .width('100%')
      .height(200)
      .backgroundColor(item % 2 === 0 ? '#007DFF' : '#4ECDC4')
      .textAlign(TextAlign.Center)
      .fontColor(Color.White)
      .fontSize(24)
  })
}
.width('100%')
.height(200)
.autoPlay(true)
.interval(3000)
.loop(true)
.indicator(true)
.duration(500)
.onChange((index: number) => {
  this.swiperIndex = index
  console.info(`当前轮播索引: ${index}`)
})

// 自定义指示器的轮播
Swiper() {
  ForEach([1, 2, 3], (item: number) => {
    Image($r(`app.media.banner${item}`))
      .width('100%')
      .height(200)
      .objectFit(ImageFit.Cover)
  })
}
.width('100%')
.height(200)
.indicator(
  new DotIndicator()
    .itemWidth(8)
    .itemHeight(8)
    .selectedItemWidth(16)
    .selectedItemHeight(8)
    .color('#CCCCCC')
    .selectedColor('#007DFF')
)

列表组件 List / Grid / WaterFlow

List 列表

属性类型说明
spacenumber / string列表项间距
initialIndexnumber初始索引
divider{ strokeWidth: number, color?: ResourceColor, startMargin?: number, endMargin?: number }分隔线
scrollBarBarState滚动条状态
edgeEffectEdgeEffect边缘效果
listDirectionAxis列表方向
cachedCountnumber缓存数量
alignListItemListItemAlign列表项对齐
stickyStickyStyle吸顶样式

Grid 网格

属性类型说明
columnsTemplatestring列模板(如 '1fr 1fr 1fr')
rowsTemplatestring行模板
columnsGapnumber / string列间距
rowsGapnumber / string行间距
scrollBarBarState滚动条状态
scrollBarWidthnumber / string滚动条宽度
edgeEffectEdgeEffect边缘效果
layoutDirectionGridDirection布局方向
maxCountnumber最大显示数量
minCountnumber最小显示数量
cellLengthnumber / string单元格长度
multiSelectableboolean是否多选
supportAnimationboolean是否支持动画
cachedCountnumber缓存数量

WaterFlow 瀑布流

属性类型说明
columnsTemplatestring列模板
rowsTemplatestring行模板
columnsGapnumber / string列间距
rowsGapnumber / string行间距
scrollBarBarState滚动条状态
edgeEffectEdgeEffect边缘效果
layoutDirectionFlexDirection布局方向
itemConstraintSizeConstraintSizeOptions子组件约束尺寸
cachedCountnumber缓存数量

代码示例

typescript
@State listItems: string[] = ['苹果', '香蕉', '橙子', '葡萄', '西瓜', '芒果']
@State gridItems: { title: string, color: string }[] = [
  { title: '项目1', color: '#FF6B6B' },
  { title: '项目2', color: '#4ECDC4' },
  { title: '项目3', color: '#45B7D1' },
  { title: '项目4', color: '#96CEB4' },
  { title: '项目5', color: '#FFEAA7' },
  { title: '项目6', color: '#DDA0DD' }
]

// 基础列表
List({ space: 8 }) {
  ForEach(this.listItems, (item: string, index: number) => {
    ListItem() {
      Row() {
        Text(`${index + 1}. ${item}`)
          .fontSize(16)
        Button('删除')
          .fontSize(12)
          .onClick(() => {
            this.listItems.splice(index, 1)
          })
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceBetween)
      .padding(16)
    }
  }, (item: string) => item)
}
.width('100%')
.divider({ strokeWidth: 1, color: '#EEEEEE' })

// 带吸顶标题的列表
List({ space: 0 }) {
  ListItemGroup({ header: this.GroupHeader('水果') }) {
    ForEach(['苹果', '香蕉', '橙子'], (item: string) => {
      ListItem() {
        Text(item)
          .width('100%')
          .height(50)
          .padding({ left: 16 })
      }
    })
  }
  
  ListItemGroup({ header: this.GroupHeader('蔬菜') }) {
    ForEach(['西红柿', '黄瓜', '白菜'], (item: string) => {
      ListItem() {
        Text(item)
          .width('100%')
          .height(50)
          .padding({ left: 16 })
      }
    })
  }
}
.width('100%')
.sticky(StickyStyle.Header)

@Builder
GroupHeader(title: string) {
  Row() {
    Text(title)
      .fontSize(16)
      .fontWeight(FontWeight.Bold)
  }
  .width('100%')
  .height(40)
  .backgroundColor('#F0F0F0')
  .padding({ left: 16 })
}

// 网格布局
Grid() {
  ForEach(this.gridItems, (item: { title: string, color: string }) => {
    GridItem() {
      Column() {
        Text(item.title)
          .fontSize(16)
          .fontColor(Color.White)
      }
      .width('100%')
      .height(100)
      .backgroundColor(item.color)
      .justifyContent(FlexAlign.Center)
      .borderRadius(8)
    }
  })
}
.width('100%')
.columnsTemplate('1fr 1fr 1fr')  // 三列等宽
.columnsGap(8)
.rowsGap(8)
.padding(8)

// 瀑布流布局
WaterFlow() {
  LazyForEach(this.dataSource, (item: ItemType) => {
    FlowItem() {
      Column() {
        Image(item.image)
          .width('100%')
          .objectFit(ImageFit.Cover)
        Text(item.title)
          .fontSize(14)
          .maxLines(2)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
      }
      .width('100%')
      .backgroundColor('#F5F5F5')
      .borderRadius(8)
    }
  })
}width('100%')
.columnsTemplate('1fr 1fr')
.columnsGap(8)
.rowsGap(8)
.padding(8)

导航组件 Tabs / TabContent / Navigation

Tabs 标签页

属性类型说明
barPositionBarPosition导航栏位置
verticalboolean是否为纵向
scrollableboolean是否可滚动
barModeBarMode导航栏模式
barWidthnumber / string导航栏宽度
barHeightnumber / string导航栏高度
animationDurationnumber动画时长
fadingEdgeboolean是否启用渐隐边缘

BarMode 枚举值

枚举值说明
BarMode.Fixed固定模式,均分显示
BarMode.Scrollable可滚动模式
属性类型说明
titlestring / Resource / CustomBuilder标题
subTitlestring / Resource副标题
hideTitleBarboolean是否隐藏标题栏
hideBackButtonboolean是否隐藏返回按钮
titleModeNavigationTitleMode标题模式
modeNavigationMode导航模式
navBarWidthnumber / string导航栏宽度
navBarPositionNavBarPosition导航栏位置

代码示例

typescript
@State currentIndex: number = 0

// 底部标签页
Tabs({ barPosition: BarPosition.End }) {
  TabContent() {
    Text('首页内容')
      .width('100%')
      .height('100%')
      .textAlign(TextAlign.Center)
  }
  .tabBar(this.TabBuilder('首页', $r('app.media.icon_home'), 0))
  
  TabContent() {
    Text('分类内容')
      .width('100%')
      .height('100%')
      .textAlign(TextAlign.Center)
  }
  .tabBar(this.TabBuilder('分类', $r('app.media.icon_category'), 1))
  
  TabContent() {
    Text('购物车内容')
      .width('100%')
      .height('100%')
      .textAlign(TextAlign.Center)
  }
  .tabBar(this.TabBuilder('购物车', $r('app.media.icon_cart'), 2))
  
  TabContent() {
    Text('我的内容')
      .width('100%')
      .height('100%')
      .textAlign(TextAlign.Center)
  }
  .tabBar(this.TabBuilder('我的', $r('app.media.icon_mine'), 3))
}
.width('100%')
.height('100%')
.barMode(BarMode.Fixed)
.onChange((index: number) => {
  this.currentIndex = index
})

@Builder
TabBuilder(title: string, icon: Resource, index: number) {
  Column({ space: 4 }) {
    Image(icon)
      .width(24)
      .height(24)
      .fillColor(this.currentIndex === index ? '#007DFF' : '#999999')
    Text(title)
      .fontSize(12)
      .fontColor(this.currentIndex === index ? '#007DFF' : '#999999')
  }
  .width('100%')
  .height(56)
  .justifyContent(FlexAlign.Center)
}

// 顶部标签页(可滚动)
Tabs({ barPosition: BarPosition.Start }) {
  ForEach(['推荐', '热点', '科技', '娱乐', '体育', '财经', '游戏'], (item: string, index: number) => {
    TabContent() {
      Text(`${item}内容`)
        .width('100%')
        .height('100%')
    }
    .tabBar(item)
  })
}
.width('100%')
.height('100%')
.barMode(BarMode.Scrollable)

// Navigation 导航
Navigation() {
  Text('页面内容')
    .width('100%')
    .height('100%')
    .textAlign(TextAlign.Center)
}
.title('页面标题')
.subTitle('副标题')
.hideBackButton(false)
.titleMode(NavigationTitleMode.Full)
.mode(NavigationMode.Stack)

Web 网页组件

Web 组件用于在应用中嵌入网页内容。

属性类型说明
srcstring / Resource网页地址
controllerWebviewController控制器
javaScriptAccessboolean是否允许 JavaScript
zoomAccessboolean是否允许缩放
fileAccessboolean是否允许访问文件
mixedModeMixedMode混合内容模式
cacheModeCacheMode缓存模式
darkModeWebDarkMode深色模式

代码示例

typescript
@State webUrl: string = 'https://www.example.com'
controller: WebviewController = new WebviewController()

Web({ src: this.webUrl, controller: this.controller })
  .width('100%')
  .height('100%')
  .javaScriptAccess(true)
  .zoomAccess(true)
  .fileAccess(false)
  .onPageBegin((event) => {
    console.info(`页面开始加载: ${event.url}`)
  })
  .onPageEnd((event) => {
    console.info(`页面加载完成: ${event.url}`)
  })
  .onErrorReceive((event) => {
    console.error(`页面加载错误: ${event.error.getErrorInfo()}`)
  })
  .onProgressChange((event) => {
    console.info(`加载进度: ${event.newProgress}%`)
  })

// Web 控制器方法
// this.controller.loadUrl('https://www.new-url.com')
// this.controller.refresh()
// this.controller.stop()
// this.controller.getUrl()
// this.controller.accessForward()
// this.accessBackward()
// this.controller.forward()
// this.controller.backward()

Video 视频组件

Video 组件用于播放视频文件并控制其播放状态。

属性类型说明
srcstring / Resource视频数据源
currentProgressRatenumber / string / PlaybackSpeed播放倍速
previewUristring / PixelMap / Resource预览图
controllerVideoController视频控制器
mutedboolean是否静音
autoPlayboolean是否自动播放
controlsboolean是否显示控制栏
objectFitImageFit视频填充模式
loopboolean是否循环播放

PlaybackSpeed 枚举值

枚举值说明
PlaybackSpeed.Speed_Forward_0_75_X0.75倍速
PlaybackSpeed.Speed_Forward_1_00_X1倍速
PlaybackSpeed.Speed_Forward_1_25_X1.25倍速
PlaybackSpeed.Speed_Forward_1_75_X1.75倍速
PlaybackSpeed.Speed_Forward_2_00_X2倍速

VideoController 方法

方法说明
start()开始播放
pause()暂停播放
stop()停止播放
setCurrentTime(value: number)设置当前播放位置
requestFullscreen()请求全屏
exitFullscreen()退出全屏

代码示例

typescript
@State videoSrc: string = 'https://www.example.com/video.mp4'
videoController: VideoController = new VideoController()

Video({
  src: this.videoSrc,
  controller: this.videoController,
  previewUri: $r('app.media.video_preview')
})
  .width('100%')
  .height(200)
  .autoPlay(false)
  .controls(true)
  .loop(false)
  .objectFit(ImageFit.Contain)
  .onStart(() => {
    console.info('视频开始播放')
  })
  .onPause(() => {
    console.info('视频暂停')
  })
  .onFinish(() => {
    console.info('视频播放结束')
  })
  .onError(() => {
    console.error('视频播放错误')
  })
  .onPrepared((event) => {
    console.info(`视频准备完成,时长: ${event.duration}秒`)
  })
  .onUpdate((event) => {
    console.info(`当前播放进度: ${event.time}秒`)
  })
  .onFullscreenChange((event) => {
    console.info(`全屏状态变化: ${event.fullscreen}`)
  })

// 自定义控制按钮
Row({ space: 16 }) {
  Button('播放')
    .onClick(() => {
      this.videoController.start()
    })
  Button('暂停')
    .onClick(() => {
      this.videoController.pause()
    })
  Button('全屏')
    .onClick(() => {
      this.videoController.requestFullscreen()
    })
}

布局容器

线性布局 Column / Row

Column 和 Row 是最基础的布局容器,分别用于垂直和水平方向的线性排列。

Column 属性

属性类型说明
spacenumber / string子元素间距
justifyContentFlexAlign主轴对齐方式
alignItemsHorizontalAlign交叉轴对齐方式

Row 属性

属性类型说明
spacenumber / string子元素间距
justifyContentFlexAlign主轴对齐方式
alignItemsVerticalAlign交叉轴对齐方式

FlexAlign 枚举值

枚举值说明
FlexAlign.Start起始对齐
FlexAlign.Center居中对齐
FlexAlign.End末尾对齐
FlexAlign.SpaceBetween两端对齐
FlexAlign.SpaceAround环绕对齐
FlexAlign.SpaceEvenly均匀对齐

代码示例

typescript
// 垂直布局
Column({ space: 12 }) {
  Text('第一行')
  Text('第二行')
  Text('第三行')
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
.padding(16)

// 垂直布局 - 顶部对齐
Column({ space: 8 }) {
  Text('标题')
    .fontSize(20)
    .fontWeight(FontWeight.Bold)
  Text('描述文本')
    .fontSize(14)
    .fontColor('#666666')
  Button('操作按钮')
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Start)
.alignItems(HorizontalAlign.Start)
.padding(16)

// 水平布局
Row({ space: 16 }) {
  Button('取消')
  Button('确定')
    .backgroundColor('#007DFF')
}
.width('100%')
.justifyContent(FlexAlign.End)
.padding(16)

// 水平布局 - 两端对齐
Row() {
  Text('左侧标题')
    .fontSize(16)
    .fontWeight(FontWeight.Bold)
  Blank()
  Text('右侧操作 >')
    .fontSize(14)
    .fontColor('#999999')
}
.width('100%')
.height(56)
.padding({ left: 16, right: 16 })
.backgroundColor(Color.White)

// 水平布局 - 环绕对齐
Row({ space: 8 }) {
  ForEach(['标签1', '标签2', '标签3', '标签4'], (item: string) => {
    Text(item)
      .fontSize(12)
      .padding({ top: 4, bottom: 4, left: 12, right: 12 })
      .backgroundColor('#F0F0F0')
      .borderRadius(12)
  })
}
.width('100%')
.justifyContent(FlexAlign.SpaceEvenly)
.padding(16)

层叠布局 Stack

Stack 用于将子组件按顺序层叠放置,后放置的组件覆盖在先放置的组件之上。

属性类型说明
alignContentAlignment内容对齐方式

Alignment 枚举值

枚举值说明
Alignment.TopStart左上角
Alignment.Top顶部居中
Alignment.TopEnd右上角
Alignment.Start左侧居中
Alignment.Center居中
Alignment.End右侧居中
Alignment.BottomStart左下角
Alignment.Bottom底部居中
Alignment.BottomEnd右下角

代码示例

typescript
// 基础层叠布局
Stack({ alignContent: Alignment.Center }) {
  Image($r('app.media.background'))
    .width('100%')
    .height('100%')

  Text('叠加文字')
    .fontSize(24)
    .fontColor(Color.White)
    .shadow({ radius: 4, color: '#80000000' })
}
.width('100%')
.height(300)

// 带角标的层叠布局
Stack({ alignContent: Alignment.TopEnd }) {
  Image($r('app.media.avatar'))
    .width(80)
    .height(80)
    .borderRadius(40)
  
  Text('3')
    .fontSize(12)
    .fontColor(Color.White)
    .width(20)
    .height(20)
    .backgroundColor(Color.Red)
    .borderRadius(10)
    .textAlign(TextAlign.Center)
}
.width(80)
.height(80)

// 复杂层叠布局
Stack({ alignContent: Alignment.Bottom }) {
  // 底层:背景图
  Image($r('app.media.card_bg'))
    .width('100%')
    .height(200)
    .objectFit(ImageFit.Cover)
    .borderRadius(12)
  
  // 中层:渐变遮罩
  Column()
    .width('100%')
    .height(200)
    .borderRadius(12)
    .backgroundColor('#80000000')
  
  // 顶层:文字内容
  Column({ space: 8 }) {
    Text('标题文本')
      .fontSize(20)
      .fontColor(Color.White)
      .fontWeight(FontWeight.Bold)
    Text('描述文本')
      .fontSize(14)
      .fontColor('#CCCCCC')
  }
  .padding(16)
  .margin({ bottom: 16 })
}
.width('100%')
.height(200)
.padding(16)

弹性布局 Flex

Flex 是更灵活的布局容器,支持方向、换行等高级配置。

属性类型说明
directionFlexDirection主轴方向
wrapFlexWrap换行方式
justifyContentFlexAlign主轴对齐
alignItemsItemAlign交叉轴对齐
alignContentFlexAlign多行对齐

FlexDirection 枚举值

枚举值说明
FlexDirection.Row水平方向
FlexDirection.RowReverse水平反向
FlexDirection.Column垂直方向
FlexDirection.ColumnReverse垂直反向

FlexWrap 枚举值

枚举值说明
FlexWrap.NoWrap不换行
FlexWrap.Wrap换行
FlexWrap.WrapReverse反向换行

ItemAlign 枚举值

枚举值说明
ItemAlign.Auto自动
ItemAlign.Start起始对齐
ItemAlign.Center居中对齐
ItemAlign.End末尾对齐
ItemAlign.Stretch拉伸对齐
ItemAlign.Baseline基线对齐

代码示例

typescript
// 水平弹性布局,自动换行
Flex({ direction: FlexDirection.Row, wrap: FlexWrap.Wrap }) {
  ForEach(this.tags, (tag: string) => {
    Text(tag)
      .fontSize(14)
      .padding({ top: 4, bottom: 4, left: 8, right: 8 })
      .backgroundColor('#F0F0F0')
      .borderRadius(4)
      .margin(4)
  })
}
.width('100%')
.padding(16)

// 垂直弹性布局
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Start }) {
  Text('标题')
    .fontSize(20)
    .fontWeight(FontWeight.Bold)
  Text('描述')
    .fontSize(14)
    .fontColor('#666666')
  Button('按钮')
    .margin({ top: 16 })
}
.width('100%')
.padding(16)

// 弹性布局 - 子元素占比
Flex({ direction: FlexDirection.Row }) {
  Text('左侧')
    .layoutWeight(1)
    .height(50)
    .backgroundColor('#FF6B6B')
    .textAlign(TextAlign.Center)
  
  Text('中间')
    .layoutWeight(2)
    .height(50)
    .backgroundColor('#4ECDC4')
    .textAlign(TextAlign.Center)
  
  Text('右侧')
    .layoutWeight(1)
    .height(50)
    .backgroundColor('#45B7D1')
    .textAlign(TextAlign.Center)
}
.width('100%')

网格布局 Grid / GridItem

Grid 用于创建二维网格布局,可以精确控制行列结构。

代码示例

typescript
// 等分网格
Grid() {
  ForEach([1, 2, 3, 4, 5, 6], (item: number) => {
    GridItem() {
      Text(`${item}`)
        .width('100%')
        .height(100)
        .backgroundColor(item % 2 === 0 ? '#007DFF' : '#4ECDC4')
        .textAlign(TextAlign.Center)
        .fontColor(Color.White)
        .fontSize(24)
    }
  })
}
.width('100%')
.columnsTemplate('1fr 1fr 1fr')  // 三列等分
.rowsTemplate('1fr 1fr')         // 两行等分
.columnsGap(8)
.rowsGap(8)
.padding(8)

// 不等分网格
Grid() {
  GridItem() {
    Text('大图')
      .width('100%')
      .height('100%')
      .backgroundColor('#FF6B6B')
      .textAlign(TextAlign.Center)
      .fontColor(Color.White)
  }
  .rowStart(0)
  .rowEnd(1)
  .columnStart(0)
  .columnEnd(1)
  
  ForEach([2, 3, 4, 5], (item: number) => {
    GridItem() {
      Text(`${item}`)
        .width('100%')
        .height('100%')
        .backgroundColor('#4ECDC4')
        .textAlign(TextAlign.Center)
        .fontColor(Color.White)
    }
  })
}
.width('100%')
.height(300)
.columnsTemplate('1fr 1fr 1fr')
.rowsTemplate('1fr 1fr')
.columnsGap(8)
.rowsGap(8)
.padding(8)

// 使用 LazyForEach 的网格(性能优化)
Grid() {
  LazyForEach(this.dataSource, (item: ItemType) => {
    GridItem() {
      Image(item.image)
        .width('100%')
        .height(120)
        .objectFit(ImageFit.Cover)
      Text(item.title)
        .fontSize(14)
        .maxLines(1)
        .textOverflow({ overflow: TextOverflow.Ellipsis })
    }
  })
}
.width('100%')
.columnsTemplate('1fr 1fr 1fr')
.columnsGap(8)
.rowsGap(8)
.cachedCount(3)  // 缓存数量

列表布局 List / ListItem

List 用于展示大量数据的滚动列表,支持垂直和水平方向。

代码示例

typescript
// 基础列表
List({ space: 8 }) {
  ForEach(this.items, (item: string, index: number) => {
    ListItem() {
      Row() {
        Text(`${index + 1}. ${item}`)
          .fontSize(16)
        Button('删除')
          .fontSize(12)
          .onClick(() => {
            this.items.splice(index, 1)
          })
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceBetween)
      .padding(16)
    }
  }, (item: string) => item)
}
.width('100%')
.divider({ strokeWidth: 1, color: '#EEEEEE' })

// 横向列表
List({ space: 12 }) {
  ForEach(this.categories, (item: string) => {
    ListItem() {
      Text(item)
        .fontSize(14)
        .padding({ top: 8, bottom: 8, left: 16, right: 16 })
        .backgroundColor('#F0F0F0')
        .borderRadius(16)
    }
  })
}
.width('100%')
.height(60)
.listDirection(Axis.Horizontal)
.scrollBar(BarState.Off)

// 带侧滑操作的列表
List({ space: 0 }) {
  ForEach(this.messages, (item: MessageType, index: number) => {
    ListItem() {
      Row() {
        Image(item.avatar)
          .width(48)
          .height(48)
          .borderRadius(24)
        Column({ space: 4 }) {
          Text(item.name)
            .fontSize(16)
            .fontWeight(FontWeight.Bold)
          Text(item.content)
            .fontSize(14)
            .fontColor('#666666')
            .maxLines(1)
            .textOverflow({ overflow: TextOverflow.Ellipsis })
        }
        .layoutWeight(1)
        .alignItems(HorizontalAlign.Start)
        .margin({ left: 12 })
      }
      .width('100%')
      .padding(16)
    }
    .swipeAction({
      end: this.DeleteBuilder(index)
    })
  })
}
.width('100%')
.divider({ strokeWidth: 1, color: '#EEEEEE', startMargin: 80 })

@Builder
DeleteBuilder(index: number) {
  Button() {
    Image($r('app.media.icon_delete'))
      .width(24)
      .height(24)
      .fillColor(Color.White)
  }
  .width(60)
  .height('100%')
  .backgroundColor(Color.Red)
  .onClick(() => {
    this.messages.splice(index, 1)
  })
}

懒加载 LazyForEach

LazyForEach 用于长列表场景,只渲染可视区域内的列表项,大幅提升性能。

typescript
// 数据源需要实现 IDataSource 接口
class MyDataSource implements IDataSource {
  private dataArray: string[] = []
  private listeners: DataChangeListener[] = []

  constructor(data: string[]) {
    this.dataArray = data
  }

  totalCount(): number {
    return this.dataArray.length
  }

  getData(index: number): string {
    return this.dataArray[index]
  }

  registerDataChangeListener(listener: DataChangeListener): void {
    this.listeners.push(listener)
  }

  unregisterDataChangeListener(listener: DataChangeListener): void {
    const index = this.listeners.indexOf(listener)
    if (index !== -1) {
      this.listeners.splice(index, 1)
    }
  }

  // 数据操作方法
  pushData(data: string): void {
    this.dataArray.push(data)
    this.notifyDataAdd(this.dataArray.length - 1)
  }

  deleteData(index: number): void {
    this.dataArray.splice(index, 1)
    this.notifyDataDelete(index)
  }

  private notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataAdd(index)
    })
  }

  private notifyDataDelete(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataDelete(index)
    })
  }
}

// 使用 LazyForEach
@State dataSource: MyDataSource = new MyDataSource(
  Array.from({ length: 1000 }, (_, i) => `项目 ${i + 1}`)
)

List({ space: 8 }) {
  LazyForEach(this.dataSource, (item: string, index: number) => {
    ListItem() {
      Text(item)
        .width('100%')
        .height(60)
        .textAlign(TextAlign.Center)
        .backgroundColor('#F5F5F5')
    }
  }, (item: string, index: number) => index.toString())
}
.width('100%')
.height('100%')
.cachedCount(5)  // 缓存前后各 5 个列表项

// 添加数据
Button('添加项目')
  .onClick(() => {
    this.dataSource.pushData(`新项目 ${Date.now()}`)
  })

轮播布局 Swiper

Swiper 用于创建轮播图效果,支持自动播放、循环、指示器等。

代码示例

typescript
@State swiperIndex: number = 0

// 基础轮播
Swiper() {
  ForEach([1, 2, 3, 4], (item: number) => {
    Text(`轮播项 ${item}`)
      .width('100%')
      .height(200)
      .backgroundColor(item % 2 === 0 ? '#007DFF' : '#4ECDC4')
      .textAlign(TextAlign.Center)
      .fontColor(Color.White)
      .fontSize(24)
  })
}
.width('100%')
.height(200)
.autoPlay(true)
.interval(3000)
.loop(true)
.indicator(true)

// 纵向轮播
Swiper() {
  ForEach(['通知1', '通知2', '通知3'], (item: string) => {
    Text(item)
      .width('100%')
      .height(40)
      .textAlign(TextAlign.Center)
      .backgroundColor('#F0F0F0')
  })
}
.width('100%')
.height(40)
.vertical(true)
.autoPlay(true)
.interval(2000)
.indicator(false)

// 自定义指示器样式
Swiper() {
  ForEach([1, 2, 3], (item: number) => {
    Image($r(`app.media.banner${item}`))
      .width('100%')
      .height(200)
      .objectFit(ImageFit.Cover)
  })
}
.width('100%')
.height(200)
.indicator(
  new DotIndicator()
    .itemWidth(8)
    .itemHeight(8)
    .selectedItemWidth(16)
    .selectedItemHeight(8)
    .color('#CCCCCC')
    .selectedColor('#007DFF')
)
.onChange((index: number) => {
  this.swiperIndex = index
})

// 卡片式轮播
Swiper() {
  ForEach([1, 2, 3, 4, 5], (item: number) => {
    Column() {
      Text(`卡片 ${item}`)
        .fontSize(20)
        .fontColor(Color.White)
    }
    .width('80%')
    .height(180)
    .backgroundColor(item % 2 === 0 ? '#FF6B6B' : '#4ECDC4')
    .borderRadius(12)
    .justifyContent(FlexAlign.Center)
  })
}
.width('100%')
.height(200)
.itemSpace(12)
.displayMode(SwiperDisplayMode.STRETCH)
.indicator(true)

瀑布流 WaterFlow

WaterFlow 用于创建瀑布流布局,子组件可以有不同的尺寸。

代码示例

typescript
// 瀑布流布局
WaterFlow() {
  LazyForEach(this.waterFlowData, (item: WaterFlowItem) => {
    FlowItem() {
      Column({ space: 8 }) {
        Image(item.image)
          .width('100%')
          .height(item.imageHeight)
          .objectFit(ImageFit.Cover)
          .borderRadius({ topLeft: 8, topRight: 8 })
        Text(item.title)
          .fontSize(14)
          .maxLines(2)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
          .padding({ left: 8, right: 8 })
        Text(item.price)
          .fontSize(16)
          .fontColor('#FF6B6B')
          .fontWeight(FontWeight.Bold)
          .padding({ left: 8, right: 8, bottom: 8 })
      }
      .width('100%')
      .backgroundColor(Color.White)
      .borderRadius(8)
      .shadow({ radius: 4, color: '#1F000000', offsetX: 0, offsetY: 2 })
    }
  })
}
.width('100%')
.height('100%')
.columnsTemplate('1fr 1fr')
.columnsGap(8)
.rowsGap(8)
.padding(8)

// 瀑布流数据源
class WaterFlowDataSource implements IDataSource {
  private data: WaterFlowItem[] = []
  private listeners: DataChangeListener[] = []

  constructor() {
    // 初始化数据,每个项目有不同的高度
    for (let i = 0; i < 100; i++) {
      this.data.push({
        id: i,
        image: $r('app.media.goods'),
        imageHeight: 100 + Math.random() * 100,  // 随机高度
        title: `商品标题 ${i + 1}`,
        price: `¥${(Math.random() * 1000).toFixed(2)}`
      })
    }
  }

  totalCount(): number {
    return this.data.length
  }

  getData(index: number): WaterFlowItem {
    return this.data[index]
  }

  registerDataChangeListener(listener: DataChangeListener): void {
    this.listeners.push(listener)
  }

  unregisterDataChangeListener(listener: DataChangeListener): void {
    const index = this.listeners.indexOf(listener)
    if (index !== -1) {
      this.listeners.splice(index, 1)
    }
  }
}

相对布局 RelativeContainer

RelativeContainer 用于创建相对布局,子组件通过锚点关系进行定位。

属性类型说明
alignRulesAlignRuleOption对齐规则
idstring组件标识

AlignRuleOption 属性

属性类型说明
left{ anchor: string, align: HorizontalAlign }左对齐规则
right{ anchor: string, align: HorizontalAlign }右对齐规则
top{ anchor: string, align: VerticalAlign }顶部对齐规则
bottom{ anchor: string, align: VerticalAlign }底部对齐规则
middle{ anchor: string, align: HorizontalAlign }水平居中对齐
center{ anchor: string, align: VerticalAlign }垂直居中对齐

代码示例

typescript
// 相对布局基础用法
RelativeContainer() {
  // 左上角头像
  Image($r('app.media.avatar'))
    .id('avatar')
    .width(60)
    .height(60)
    .borderRadius(30)
    .alignRules({
      top: { anchor: '__container__', align: VerticalAlign.Top },
      left: { anchor: '__container__', align: HorizontalAlign.Start }
    })
    .margin(16)

  // 头像右侧的用户名
  Text('用户名')
    .id('username')
    .fontSize(18)
    .fontWeight(FontWeight.Bold)
    .alignRules({
      top: { anchor: 'avatar', align: VerticalAlign.Top },
      left: { anchor: 'avatar', align: HorizontalAlign.End }
    })
    .margin({ left: 12, top: 4 })

  // 用户名下方的描述
  Text('用户描述信息')
    .id('description')
    .fontSize(14)
    .fontColor('#666666')
    .alignRules({
      top: { anchor: 'username', align: VerticalAlign.Bottom },
      left: { anchor: 'username', align: HorizontalAlign.Start }
    })
    .margin({ top: 4 })

  // 右侧的操作按钮
  Button('关注')
    .id('followBtn')
    .fontSize(14)
    .width(70)
    .height(32)
    .alignRules({
      top: { anchor: 'avatar', align: VerticalAlign.Top },
      right: { anchor: '__container__', align: HorizontalAlign.End }
    })
    .margin({ right: 16, top: 14 })

  // 底部的分隔线
  Divider()
    .id('divider')
    .height(1)
    .color('#EEEEEE')
    .alignRules({
      top: { anchor: 'avatar', align: VerticalAlign.Bottom },
      left: { anchor: '__container__', align: HorizontalAlign.Start },
      right: { anchor: '__container__', align: HorizontalAlign.End }
    })
    .margin({ top: 16 })
}
.width('100%')
.height(100)
.backgroundColor(Color.White)

// 复杂的相对布局
RelativeContainer() {
  // 顶部导航栏
  Row() {
    Image($r('app.media.icon_back'))
      .width(24)
      .height(24)
    Blank()
    Text('页面标题')
      .fontSize(18)
      .fontWeight(FontWeight.Bold)
    Blank()
    Image($r('app.media.icon_more'))
      .width(24)
      .height(24)
  }
  .id('header')
  .width('100%')
  .height(56)
  .padding({ left: 16, right: 16 })
  .alignRules({
    top: { anchor: '__container__', align: VerticalAlign.Top },
    left: { anchor: '__container__', align: HorizontalAlign.Start },
    right: { anchor: '__container__', align: HorizontalAlign.End }
  })

  // 内容区域
  Scroll() {
    Column({ space: 16 }) {
      ForEach([1, 2, 3, 4, 5], (item: number) => {
        Text(`内容项 ${item}`)
          .width('100%')
          .height(80)
          .backgroundColor('#F5F5F5')
          .textAlign(TextAlign.Center)
      })
    }
    .padding(16)
  }
  .id('content')
  .alignRules({
    top: { anchor: 'header', align: VerticalAlign.Bottom },
    left: { anchor: '__container__', align: HorizontalAlign.Start },
    right: { anchor: '__container__', align: HorizontalAlign.End },
    bottom: { anchor: '__container__', align: VerticalAlign.Bottom }
  })
}
.width('100%')
.height('100%')
.backgroundColor(Color.White)

动画系统

属性动画 animation

属性动画通过 animation 方法实现,当组件的属性变化时会自动触发动画。

属性类型说明
durationnumber动画时长(ms)
temponumber动画速率
curveCurve / ICurve动画曲线
delaynumber延迟时间(ms)
iterationsnumber迭代次数(-1 表示无限)
playModePlayMode播放模式
onFinish() => void动画结束回调

Curve 枚举值

枚举值说明
Curve.Linear线性
Curve.Ease缓动
Curve.EaseIn缓入
Curve.EaseOut缓出
Curve.EaseInOut缓入缓出
Curve.FastOutSlowIn快出慢入
Curve.LinearOutSlowIn线性出缓入
Curve.FastOutLinearIn快出线性入
Curve.Decelerate减速
Curve.Bounce弹跳
Curve.Spring弹性

PlayMode 枚举值

枚举值说明
PlayMode.Normal正常播放
PlayMode.Alternate往返播放
PlayMode.Reverse反向播放
PlayMode.AlternateReverse反向往返

代码示例

typescript
@State rotateAngle: number = 0
@State scaleValue: number = 1
@State opacityValue: number = 1
@State translateX: number = 0
@State widthValue: number = 100
@State heightValue: number = 100
@State bgColor: ResourceColor = '#007DFF'

// 旋转动画
Column() {
  Text('旋转')
    .width(100)
    .height(100)
    .backgroundColor('#007DFF')
    .fontColor(Color.White)
    .textAlign(TextAlign.Center)
    .rotate({ angle: this.rotateAngle })
    .animation({
      duration: 1000,
      curve: Curve.EaseInOut,
      iterations: -1,
      playMode: PlayMode.Alternate
    })
}
.onClick(() => {
  this.rotateAngle = 360
})

// 缩放动画
Column() {
  Text('缩放')
    .width(100)
    .height(100)
    .backgroundColor('#4ECDC4')
    .fontColor(Color.White)
    .textAlign(TextAlign.Center)
    .scale({ x: this.scaleValue, y: this.scaleValue })
    .animation({
      duration: 500,
      curve: Curve.Spring,
      iterations: -1,
      playMode: PlayMode.Alternate
    })
}
.onClick(() => {
  this.scaleValue = this.scaleValue === 1 ? 1.5 : 1
})

// 位移动画
Column() {
  Text('位移')
    .width(100)
    .height(100)
    .backgroundColor('#FF6B6B')
    .fontColor(Color.White)
    .textAlign(TextAlign.Center)
    .translate({ x: this.translateX })
    .animation({
      duration: 800,
      curve: Curve.EaseInOut,
      playMode: PlayMode.Normal
    })
}
.onClick(() => {
  this.translateX = this.translateX === 0 ? 200 : 0
})

// 组合动画
Column() {
  Text('组合')
    .width(this.widthValue)
    .height(this.heightValue)
    .backgroundColor(this.bgColor)
    .fontColor(Color.White)
    .textAlign(TextAlign.Center)
    .borderRadius(this.widthValue / 2)
    .animation({
      duration: 1000,
      curve: Curve.EaseInOut
    })
}
.onClick(() => {
  this.widthValue = this.widthValue === 100 ? 150 : 100
  this.heightValue = this.heightValue === 100 ? 150 : 100
  this.bgColor = this.bgColor === '#007DFF' ? '#FF6B6B' : '#007DFF'
})

// 透明度动画
Column() {
  Text('淡入淡出')
    .width(100)
    .height(100)
    .backgroundColor('#96CEB4')
    .fontColor(Color.White)
    .textAlign(TextAlign.Center)
    .opacity(this.opacityValue)
    .animation({
      duration: 1000,
      curve: Curve.EaseInOut,
      iterations: -1,
      playMode: PlayMode.Alternate
    })
}
.onAppear(() => {
  this.opacityValue = 0.2
})

显式动画 animateTo

显式动画通过 animateTo 函数实现,可以同时对多个组件的属性变化应用动画。

属性类型说明
durationnumber动画时长
temponumber动画速率
curveCurve / ICurve动画曲线
delaynumber延迟时间
iterationsnumber迭代次数
playModePlayMode播放模式
onFinish() => void结束回调

代码示例

typescript
@State width1: number = 100
@State height1: number = 100
@State width2: number = 100
@State height2: number = 100
@State color1: ResourceColor = '#007DFF'
@State color2: ResourceColor = '#4ECDC4'

// 同时动画多个组件
Column({ space: 20 }) {
  Text('组件1')
    .width(this.width1)
    .height(this.height1)
    .backgroundColor(this.color1)
    .fontColor(Color.White)
    .textAlign(TextAlign.Center)

  Text('组件2')
    .width(this.width2)
    .height(this.height2)
    .backgroundColor(this.color2)
    .fontColor(Color.White)
    .textAlign(TextAlign.Center)

  Button('触发动画')
    .onClick(() => {
      animateTo({
        duration: 1000,
        curve: Curve.EaseInOut,
        onFinish: () => {
          console.info('动画完成')
        }
      }, () => {
        // 在闭包中修改状态,所有变化会同时动画
        this.width1 = this.width1 === 100 ? 200 : 100
        this.height1 = this.height1 === 100 ? 150 : 100
        this.color1 = this.color1 === '#007DFF' ? '#FF6B6B' : '#007DFF'
        this.width2 = this.width2 === 100 ? 150 : 100
        this.height2 = this.height2 === 100 ? 200 : 100
        this.color2 = this.color2 === '#4ECDC4' ? '#96CEB4' : '#4ECDC4'
      })
    })
}

// 链式动画
Button('链式动画')
  .onClick(() => {
    // 第一步动画
    animateTo({ duration: 500 }, () => {
      this.width1 = 200
    })
    
    // 第二步动画(延迟执行)
    setTimeout(() => {
      animateTo({ duration: 500 }, () => {
        this.height1 = 200
      })
    }, 500)
  })

// 弹性动画
Button('弹性动画')
  .onClick(() => {
    animateTo({
      duration: 1000,
      curve: curves.springCurve(100, 10, 80, 10)  // 弹性曲线参数
    }, () => {
      this.width1 = this.width1 === 100 ? 250 : 100
    })
  })

组件转场动画 transition

组件转场动画用于控制组件插入和移除时的动画效果。

属性类型说明
typeTransitionType转场类型
opacitynumber透明度
translateTranslateOptions位移
scaleScaleOptions缩放
rotateRotateOptions旋转

TransitionType 枚举值

枚举值说明
TransitionType.All插入和移除都使用转场
TransitionType.Insert仅插入使用转场
TransitionType.Delete仅移除使用转场

代码示例

typescript
@State isShow: boolean = false
@State isShowList: boolean[] = [false, false, false]

// 基础转场动画
Column({ space: 20 }) {
  Button(this.isShow ? '隐藏' : '显示')
    .onClick(() => {
      this.isShow = !this.isShow
    })

  if (this.isShow) {
    Text('转场内容')
      .width(200)
      .height(100)
      .backgroundColor('#007DFF')
      .fontColor(Color.White)
      .textAlign(TextAlign.Center)
      .transition(TransitionEffect.OPACITY)  // 淡入淡出
  }
}

// 位移动画
if (this.isShow) {
  Text('从下方滑入')
    .width(200)
    .height(100)
    .backgroundColor('#4ECDC4')
    .fontColor(Color.White)
    .textAlign(TextAlign.Center)
    .transition(TransitionEffect.translate({ y: 100 }))
}

// 缩放动画
if (this.isShow) {
  Text('缩放进入')
    .width(200)
    .height(100)
    .backgroundColor('#FF6B6B')
    .fontColor(Color.White)
    .textAlign(TextAlign.Center)
    .transition(TransitionEffect.scale({ x: 0, y: 0 }))
}

// 组合转场
if (this.isShow) {
  Text('组合效果')
    .width(200)
    .height(100)
    .backgroundColor('#96CEB4')
    .fontColor(Color.White)
    .textAlign(TextAlign.Center)
    .transition(
      TransitionEffect.asymmetric(
        TransitionEffect.OPACITY.combine(TransitionEffect.translate({ x: 100 })),
        TransitionEffect.OPACITY.combine(TransitionEffect.translate({ x: -100 }))
      )
    )
}

// 列表项转场
Column({ space: 8 }) {
  Button('添加/移除')
    .onClick(() => {
      this.isShowList = [...this.isShowList, !this.isShowList[this.isShowList.length - 1]]
    })

  ForEach(this.isShowList, (isShow: boolean, index: number) => {
    if (isShow) {
      Text(`列表项 ${index + 1}`)
        .width('100%')
        .height(60)
        .backgroundColor('#F0F0F0')
        .textAlign(TextAlign.Center)
        .transition(
          TransitionEffect.asymmetric(
            TransitionEffect.translate({ x: 100 }).combine(TransitionEffect.OPACITY),
            TransitionEffect.translate({ x: -100 }).combine(TransitionEffect.OPACITY)
          )
        )
    }
  })
}

页面转场动画 PageTransition

页面转场动画用于控制页面进入和退出时的动画效果。

属性类型说明
PageTransitionEnter页面进入转场配置进入动画
PageTransitionExit页面退出转场配置退出动画

代码示例

typescript
// PageOne.ets
@Entry
@Component
struct PageOne {
  @State scale: number = 1
  @State opacity: number = 1

  // 页面进入转场
  pageTransition() {
    PageTransitionEnter({ type: RouteType.Push, duration: 300 })
      .slide(SlideEffect.Right)
      .opacity(0)
    
    PageTransitionEnter({ type: RouteType.Pop, duration: 300 })
      .slide(SlideEffect.Left)
      .opacity(0)

    // 页面退出转场
    PageTransitionExit({ type: RouteType.Push, duration: 300 })
      .slide(SlideEffect.Left)
      .opacity(0)
    
    PageTransitionExit({ type: RouteType.Pop, duration: 300 })
      .slide(SlideEffect.Right)
      .opacity(0)
  }

  build() {
    Column() {
      Text('页面一')
        .fontSize(24)
      
      Button('跳转到页面二')
        .onClick(() => {
          router.pushUrl({ url: 'pages/PageTwo' })
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

// PageTwo.ets
@Entry
@Component
struct PageTwo {
  // 页面进入转场 - 从底部滑入
  pageTransition() {
    PageTransitionEnter({ type: RouteType.Push, duration: 300 })
      .slide(SlideEffect.Bottom)
      .opacity(0)

    PageTransitionExit({ type: RouteType.Pop, duration: 300 })
      .slide(SlideEffect.Bottom)
      .opacity(0)
  }

  build() {
    Column() {
      Text('页面二')
        .fontSize(24)
      
      Button('返回')
        .onClick(() => {
          router.back()
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .backgroundColor('#F5F5F5')
  }
}

共享元素转场 sharedTransition

共享元素转场用于在两个页面之间对相同元素进行平滑过渡,实现"一镜到底"效果。

属性类型说明
idstring共享元素标识
optionssharedTransitionOptions转场配置

代码示例

typescript
// 页面一:列表页
@Entry
@Component
struct ListPage {
  @State images: string[] = [
    'https://example.com/image1.jpg',
    'https://example.com/image2.jpg',
    'https://example.com/image3.jpg'
  ]

  build() {
    List() {
      ForEach(this.images, (image: string, index: number) => {
        ListItem() {
          Image(image)
            .width('100%')
            .height(200)
            .objectFit(ImageFit.Cover)
            .borderRadius(8)
            // 共享元素标识
            .sharedTransition(`image_${index}`, {
              duration: 500,
              curve: Curve.EaseInOut,
              delay: 0
            })
            .onClick(() => {
              router.pushUrl({
                url: 'pages/DetailPage',
                params: { imageIndex: index, imageUrl: image }
              })
            })
        }
      })
    }
    .padding(16)
  }
}

// 页面二:详情页
@Entry
@Component
struct DetailPage {
  @State imageIndex: number = 0
  @State imageUrl: string = ''

  aboutToAppear() {
    const params = router.getParams() as Record<string, string>
    this.imageIndex = Number(params['imageIndex'])
    this.imageUrl = params['imageUrl']
  }

  build() {
    Column() {
      Image(this.imageUrl)
        .width('100%')
        .height(300)
        .objectFit(ImageFit.Cover)
        // 相同的共享元素标识
        .sharedTransition(`image_${this.imageIndex}`, {
          duration: 500,
          curve: Curve.EaseInOut,
          delay: 0
        })
        .onClick(() => {
          router.back()
        })

      Text('详情内容')
        .fontSize(16)
        .margin(16)
    }
    .width('100%')
    .height('100%')
  }
}

路径动画 motionPath

路径动画用于让组件沿着指定路径运动。

属性类型说明
pathstring运动路径(SVG path 格式)
fromnumber起始比例(0-1)
tonumber结束比例(0-1)
rotatableboolean是否跟随路径旋转

代码示例

typescript
@State progress: number = 0

// 路径动画
Column() {
  Button('开始路径动画')
    .onClick(() => {
      animateTo({
        duration: 3000,
        curve: Curve.Linear,
        iterations: -1
      }, () => {
        this.progress = 1
      })
    })

  // 沿着路径运动的组件
  Text('运动')
    .width(50)
    .height(50)
    .backgroundColor('#007DFF')
    .fontColor(Color.White)
    .textAlign(TextAlign.Center)
    .borderRadius(25)
    .motionPath({
      path: 'M100,200 C100,100 250,100 250,200 S400,300 400,200',
      from: 0,
      to: 1,
      rotatable: true
    })
    .animation({ duration: 3000, curve: Curve.Linear, iterations: -1 })
}
.width('100%')
.height('100%')

// 自定义路径动画
Column() {
  // 圆形路径
  Text('圆')
    .width(40)
    .height(40)
    .backgroundColor('#FF6B6B')
    .fontColor(Color.White)
    .textAlign(TextAlign.Center)
    .borderRadius(20)
    .motionPath({
      path: 'M200,200 m-100,0 a100,100 0 1,0 200,0 a100,100 0 1,0 -200,0',
      from: 0,
      to: 1,
      rotatable: false
    })
}

手势系统

点击手势 TapGesture

TapGesture 用于识别点击操作,支持单击、双击和多击。

属性类型说明
countnumber点击次数(默认 1)
fingersnumber手指数量(默认 1)

代码示例

typescript
@State tapCount: number = 0
@State doubleTapCount: number = 0

// 单击手势
Text('单击我')
  .width(200)
  .height(100)
  .backgroundColor('#007DFF')
  .fontColor(Color.White)
  .textAlign(TextAlign.Center)
  .gesture(
    TapGesture({ count: 1 })
      .onAction((event: GestureEvent) => {
        this.tapCount++
        console.info(`单击次数: ${this.tapCount}`)
        console.info(`点击位置: x=${event.fingerList[0].localX}, y=${event.fingerList[0].localY}`)
      })
  )

// 双击手势
Text('双击我')
  .width(200)
  .height(100)
  .backgroundColor('#4ECDC4')
  .fontColor(Color.White)
  .textAlign(TextAlign.Center)
  .gesture(
    TapGesture({ count: 2 })
      .onAction(() => {
        this.doubleTapCount++
        console.info(`双击次数: ${this.doubleTapCount}`)
      })
  )

// 多指点击
Text('双指点击')
  .width(200)
  .height(100)
  .backgroundColor('#FF6B6B')
  .fontColor(Color.White)
  .textAlign(TextAlign.Center)
  .gesture(
    TapGesture({ count: 1, fingers: 2 })
      .onAction(() => {
        console.info('双指点击')
      })
  )

长按手势 LongPressGesture

LongPressGesture 用于识别长按操作。

属性类型说明
fingersnumber手指数量
repeatboolean是否重复触发
durationnumber长按触发时长(ms,默认 500)

代码示例

typescript
@State isLongPress: boolean = false
@State pressDuration: number = 0

// 基础长按
Text('长按我')
  .width(200)
  .height(100)
  .backgroundColor(this.isLongPress ? '#FF6B6B' : '#007DFF')
  .fontColor(Color.White)
  .textAlign(TextAlign.Center)
  .gesture(
    LongPressGesture({ duration: 500 })
      .onAction((event: GestureEvent) => {
        this.isLongPress = true
        console.info('长按触发')
      })
      .onActionEnd(() => {
        this.isLongPress = false
        console.info('长按结束')
      })
  )

// 重复触发长按
Text('持续长按')
  .width(200)
  .height(100)
  .backgroundColor('#96CEB4')
  .fontColor(Color.White)
  .textAlign(TextAlign.Center)
  .gesture(
    LongPressGesture({ repeat: true, duration: 500 })
      .onAction((event: GestureEvent) => {
        this.pressDuration += 500
        console.info(`长按时长: ${this.pressDuration}ms`)
      })
  )

拖动手势 PanGesture

PanGesture 用于识别拖拽操作。

属性类型说明
fingersnumber手指数量
directionPanDirection拖动方向
distancenumber最小识别距离(vp)

PanDirection 枚举值

枚举值说明
PanDirection.Left向左
PanDirection.Right向右
PanDirection.Up向上
PanDirection.Down向下
PanDirection.Horizontal水平方向
PanDirection.Vertical垂直方向
PanDirection.All所有方向

代码示例

typescript
@State offsetX: number = 0
@State offsetY: number = 0
@State positionX: number = 100
@State positionY: number = 100

// 自由拖动
Text('拖动我')
  .width(100)
  .height(100)
  .backgroundColor('#007DFF')
  .fontColor(Color.White)
  .textAlign(TextAlign.Center)
  .position({ x: this.positionX, y: this.positionY })
  .gesture(
    PanGesture({ fingers: 1, direction: PanDirection.All })
      .onActionStart((event: GestureEvent) => {
        console.info('拖动开始')
      })
      .onActionUpdate((event: GestureEvent) => {
        this.offsetX = event.offsetX
        this.offsetY = event.offsetY
        this.positionX += event.offsetX
        this.positionY += event.offsetY
      })
      .onActionEnd((event: GestureEvent) => {
        console.info('拖动结束')
        this.offsetX = 0
        this.offsetY = 0
      })
  )

// 水平拖动
Text('水平拖动')
  .width(200)
  .height(60)
  .backgroundColor('#4ECDC4')
  .fontColor(Color.White)
  .textAlign(TextAlign.Center)
  .translate({ x: this.offsetX })
  .gesture(
    PanGesture({ direction: PanDirection.Horizontal })
      .onActionUpdate((event: GestureEvent) => {
        this.offsetX = event.offsetX
      })
      .onActionEnd(() => {
        // 回弹效果
        animateTo({ duration: 300, curve: Curve.Spring }, () => {
          this.offsetX = 0
        })
      })
  )

捏合手势 PinchGesture

PinchGesture 用于识别双指捏合操作,常用于缩放。

属性类型说明
fingersnumber手指数量(默认 2)
distancenumber最小识别距离

代码示例

typescript
@State scale: number = 1
@State pinchValue: number = 1

// 图片缩放
Image($r('app.media.photo'))
  .width(300)
  .height(300)
  .objectFit(ImageFit.Cover)
  .scale({ x: this.scale, y: this.scale })
  .gesture(
    PinchGesture()
      .onActionStart((event: GestureEvent) => {
        console.info('捏合开始')
      })
      .onActionUpdate((event: GestureEvent) => {
        this.scale = this.pinchValue * event.scale
      })
      .onActionEnd(() => {
        this.pinchValue = this.scale
        // 限制缩放范围
        if (this.scale < 0.5) {
          animateTo({ duration: 300 }, () => {
            this.scale = 0.5
            this.pinchValue = 0.5
          })
        } else if (this.scale > 3) {
          animateTo({ duration: 300 }, () => {
            this.scale = 3
            this.pinchValue = 3
          })
        }
      })
  )

旋转手势 RotationGesture

RotationGesture 用于识别双指旋转操作。

属性类型说明
fingersnumber手指数量(默认 2)
anglenumber最小识别角度

代码示例

typescript
@State rotateAngle: number = 0
@State currentAngle: number = 0

// 图片旋转
Image($r('app.media.photo'))
  .width(300)
  .height(300)
  .objectFit(ImageFit.Cover)
  .rotate({ angle: this.rotateAngle })
  .gesture(
    RotationGesture()
      .onActionStart((event: GestureEvent) => {
        console.info('旋转开始')
      })
      .onActionUpdate((event: GestureEvent) => {
        this.rotateAngle = this.currentAngle + event.angle
      })
      .onActionEnd(() => {
        this.currentAngle = this.rotateAngle
      })
  )

滑动手势 SwipeGesture

SwipeGesture 用于识别快速滑动操作。

属性类型说明
fingersnumber手指数量
directionSwipeDirection滑动方向
speednumber最小识别速度(vp/s)

SwipeDirection 枚举值

枚举值说明
SwipeDirection.Horizontal水平方向
SwipeDirection.Vertical垂直方向
SwipeDirection.All所有方向
SwipeDirection.Left向左
SwipeDirection.Right向右
SwipeDirection.Up向上
SwipeDirection.Down向下

代码示例

typescript
@State swipeResult: string = ''

// 滑动识别
Text(`滑动方向: ${this.swipeResult}`)
  .width(300)
  .height(200)
  .backgroundColor('#007DFF')
  .fontColor(Color.White)
  .textAlign(TextAlign.Center)
  .gesture(
    SwipeGesture({ direction: SwipeDirection.All, speed: 100 })
      .onAction((event: GestureEvent) => {
        const angle = event.angle
        if (angle >= -45 && angle < 45) {
          this.swipeResult = '向右滑动'
        } else if (angle >= 45 && angle < 135) {
          this.swipeResult = '向下滑动'
        } else if (angle >= -135 && angle < -45) {
          this.swipeResult = '向上滑动'
        } else {
          this.swipeResult = '向左滑动'
        }
        console.info(`滑动速度: ${event.speed} vp/s`)
      })
  )

// 左滑删除示例
List() {
  ForEach(this.items, (item: string, index: number) => {
    ListItem() {
      Text(item)
        .width('100%')
        .height(60)
        .backgroundColor('#F5F5F5')
    }
    .gesture(
      SwipeGesture({ direction: SwipeDirection.Left, speed: 100 })
        .onAction(() => {
          this.items.splice(index, 1)
        })
    )
  })
}

组合手势 GestureGroup

GestureGroup 用于将多个手势组合在一起,支持顺序识别、并行识别和互斥识别。

模式说明
GestureMode.Sequence顺序识别,按顺序触发
GestureMode.Parallel并行识别,同时触发
GestureMode.Exclusive互斥识别,只触发一个

代码示例

typescript
@State scale: number = 1
@State rotateAngle: number = 0
@State offsetX: number = 0
@State offsetY: number = 0

// 顺序手势:长按后拖动
Text('长按后拖动')
  .width(200)
  .height(100)
  .backgroundColor('#007DFF')
  .fontColor(Color.White)
  .textAlign(TextAlign.Center)
  .translate({ x: this.offsetX, y: this.offsetY })
  .gesture(
    GestureGroup(GestureMode.Sequence,
      LongPressGesture({ duration: 500 }),
      PanGesture()
        .onActionUpdate((event: GestureEvent) => {
          this.offsetX += event.offsetX
          this.offsetY += event.offsetY
        })
    )
      .onCancel(() => {
        console.info('手势序列取消')
      })
  )

// 并出手势:缩放和旋转同时进行
Image($r('app.media.photo'))
  .width(300)
  .height(300)
  .objectFit(ImageFit.Cover)
  .scale({ x: this.scale, y: this.scale })
  .rotate({ angle: this.rotateAngle })
  .gesture(
    GestureGroup(GestureMode.Parallel,
      PinchGesture()
        .onActionUpdate((event: GestureEvent) => {
          this.scale = event.scale
        }),
      RotationGesture()
        .onActionUpdate((event: GestureEvent) => {
          this.rotateAngle = event.angle
        })
    )
  )

// 互斥手势:单击或长按
Text('单击或长按')
  .width(200)
  .height(100)
  .backgroundColor('#4ECDC4')
  .fontColor(Color.White)
  .textAlign(TextAlign.Center)
  .gesture(
    GestureGroup(GestureMode.Exclusive,
      TapGesture()
        .onAction(() => {
          console.info('单击触发')
        }),
      LongPressGesture({ duration: 500 })
        .onAction(() => {
          console.info('长按触发')
        })
    )
  )

绘制与自定义

Canvas 绘制

Canvas 组件用于自定义绘制图形、文本、图片等。

属性类型说明
contextCanvasRenderingContext2D2D 绘制上下文

CanvasRenderingContext2D 常用方法

方法说明
fillRect(x, y, w, h)填充矩形
strokeRect(x, y, w, h)描边矩形
clearRect(x, y, w, h)清除矩形区域
beginPath()开始路径
moveTo(x, y)移动到
lineTo(x, y)连线到
arc(x, y, r, startAngle, endAngle)绘制圆弧
fill()填充路径
stroke()描边路径
fillText(text, x, y)填充文本
drawImage(image, x, y, w, h)绘制图片

代码示例

typescript
// 基础 Canvas 绘制
@Entry
@Component
struct CanvasDemo {
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(
    new RenderingContextSettings(true)
  )

  build() {
    Column({ space: 20 }) {
      // 绘制矩形
      Canvas(this.context)
        .width(300)
        .height(200)
        .backgroundColor('#F5F5F5')
        .onReady(() => {
          // 填充矩形
          this.context.fillStyle = '#007DFF'
          this.context.fillRect(20, 20, 100, 80)

          // 描边矩形
          this.context.strokeStyle = '#FF6B6B'
          this.context.lineWidth = 3
          this.context.strokeRect(140, 20, 100, 80)

          // 清除区域
          this.context.clearRect(50, 40, 60, 40)
        })

      // 绘制路径
      Canvas(this.context)
        .width(300)
        .height(200)
        .backgroundColor('#F5F5F5')
        .onReady(() => {
          // 绘制三角形
          this.context.beginPath()
          this.context.moveTo(150, 30)
          this.context.lineTo(100, 150)
          this.context.lineTo(200, 150)
          this.context.closePath()
          this.context.fillStyle = '#4ECDC4'
          this.context.fill()

          // 绘制圆形
          this.context.beginPath()
          this.context.arc(80, 100, 40, 0, 2 * Math.PI)
          this.context.fillStyle = '#FF6B6B'
          this.context.fill()
          this.context.strokeStyle = '#333333'
          this.context.lineWidth = 2
          this.context.stroke()
        })

      // 绘制文本
      Canvas(this.context)
        .width(300)
        .height(100)
        .backgroundColor('#F5F5F5')
        .onReady(() => {
          this.context.font = '24px sans-serif'
          this.context.fillStyle = '#333333'
          this.context.fillText('Hello Canvas', 20, 40)

          this.context.font = '16px sans-serif'
          this.context.fillStyle = '#666666'
          this.context.fillText('ArkUI 自定义绘制', 20, 70)
        })

      // 绘制渐变
      Canvas(this.context)
        .width(300)
        .height(100)
        .backgroundColor('#F5F5F5')
        .onReady(() => {
          const gradient = this.context.createLinearGradient(0, 0, 300, 0)
          gradient.addColorStop(0, '#007DFF')
          gradient.addColorStop(0.5, '#4ECDC4')
          gradient.addColorStop(1, '#FF6B6B')

          this.context.fillStyle = gradient
          this.context.fillRect(0, 0, 300, 100)
        })
    }
    .padding(16)
  }
}

Shape 形状绘制

Shape 组件提供了预定义的形状绘制能力。

形状说明
Rect矩形
Circle圆形
Ellipse椭圆
Path路径
Line直线
Polyline折线
Polygon多边形

代码示例

typescript
// 矩形
Rect()
  .width(100)
  .height(80)
  .fill('#007DFF')
  .stroke('#333333')
  .strokeWidth(2)
  .radius(8)  // 圆角

// 圆形
Circle()
  .width(100)
  .height(100)
  .fill('#4ECDC4')
  .stroke('#333333')
  .strokeWidth(2)

// 椭圆
Ellipse()
  .width(150)
  .height(100)
  .fill('#FF6B6B')
  .stroke('#333333')
  .strokeWidth(2)

// 路径
Path()
  .width(100)
  .height(100)
  .commands('M0 0 L100 0 L50 100 Z')
  .fill('#96CEB4')
  .stroke('#333333')
  .strokeWidth(2)

// 直线
Line()
  .width(100)
  .height(2)
  .startPoint([0, 0])
  .endPoint([100, 0])
  .stroke('#333333')
  .strokeWidth(2)

// 组合形状
Shape() {
  Rect()
    .width(100)
    .height(80)
    .fill('#007DFF')
    .radius(8)
  
  Circle()
    .width(40)
    .height(40)
    .fill('#FFFFFF')
    .offset({ x: 30, y: 20 })
}
.width(100)
.height(80)

自定义组件生命周期

自定义组件拥有完整的生命周期,可以在不同阶段执行特定逻辑。

生命周期说明
aboutToAppear()组件即将出现时调用
aboutToDisappear()组件即将销毁时调用
onPageShow()页面显示时调用
onPageHide()页面隐藏时调用
onBackPress()用户点击返回键时调用

代码示例

typescript
@Entry
@Component
struct LifecycleDemo {
  @State message: string = ''

  aboutToAppear() {
    // 组件创建时初始化数据
    this.message = '组件已创建'
    console.info('aboutToAppear: 组件即将出现')
  }

  aboutToDisappear() {
    // 组件销毁时清理资源
    console.info('aboutToDisappear: 组件即将销毁')
  }

  onPageShow() {
    // 页面显示时刷新数据
    console.info('onPageShow: 页面显示')
  }

  onPageHide() {
    // 页面隐藏时暂停操作
    console.info('onPageHide: 页面隐藏')
  }

  onBackPress() {
    // 返回键处理
    console.info('onBackPress: 用户点击返回')
    return false  // 返回 false 允许默认返回行为
  }

  build() {
    Column() {
      Text(this.message)
        .fontSize(20)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

@BuilderParam 构建参数

@BuilderParam 用于在自定义组件中接收外部传入的 UI 构建函数,实现组件的高度可定制。

代码示例

typescript
// 定义带构建参数的自定义组件
@Component
struct Card {
  @BuilderParam header: () => void
  @BuilderParam content: () => void
  @BuilderParam footer: () => void = this.DefaultFooter

  @Builder
  DefaultFooter() {
    Text('默认底部')
      .fontSize(12)
      .fontColor('#999999')
  }

  build() {
    Column({ space: 12 }) {
      // 头部区域
      if (this.header) {
        this.header()
      }
      
      // 内容区域
      if (this.content) {
        this.content()
      }
      
      // 底部区域
      if (this.footer) {
        this.footer()
      }
    }
    .width('100%')
    .padding(16)
    .backgroundColor(Color.White)
    .borderRadius(12)
    .shadow({ radius: 4, color: '#1F000000', offsetX: 0, offsetY: 2 })
  }
}

// 使用自定义组件
@Entry
@Component
struct BuilderParamDemo {
  @Builder
  CustomHeader() {
    Row() {
      Image($r('app.media.avatar'))
        .width(40)
        .height(40)
        .borderRadius(20)
      Column({ space: 4 }) {
        Text('用户名')
          .fontSize(16)
          .fontWeight(FontWeight.Bold)
        Text('2小时前')
          .fontSize(12)
          .fontColor('#999999')
      }
      .alignItems(HorizontalAlign.Start)
      .margin({ left: 12 })
    }
    .width('100%')
  }

  @Builder
  CustomContent() {
    Text('这是卡片的内容区域,可以包含任意内容。')
      .fontSize(14)
      .fontColor('#333333')
      .lineHeight(20)
  }

  @Builder
  CustomFooter() {
    Row({ space: 16 }) {
      Text('点赞')
        .fontSize(14)
        .fontColor('#666666')
      Text('评论')
        .fontSize(14)
        .fontColor('#666666')
      Text('分享')
        .fontSize(14)
        .fontColor('#666666')
    }
  }

  build() {
    Column({ space: 16 }) {
      // 使用自定义头部和内容
      Card({
        header: this.CustomHeader,
        content: this.CustomContent,
        footer: this.CustomFooter
      })

      // 只使用内容,使用默认底部
      Card({
        content: () => {
          Text('简化卡片')
            .fontSize(16)
        }
      })
    }
    .padding(16)
    .backgroundColor('#F5F5F5')
  }
}

@CustomDialog 自定义弹窗

@CustomDialog 用于创建自定义弹窗组件。

属性类型说明
controllerCustomDialogController弹窗控制器
autoCancelboolean点击遮罩是否关闭
alignmentDialogAlignment弹窗对齐方式
offsetOffset偏移量
customStyleboolean是否自定义样式
maskColorResourceColor遮罩颜色

代码示例

typescript
// 自定义弹窗组件
@CustomDialog
struct ConfirmDialog {
  controller: CustomDialogController
  title: string = '提示'
  message: string = ''
  confirmText: string = '确定'
  cancelText: string = '取消'
  onConfirm: () => void = () => {}
  onCancel: () => void = () => {}

  build() {
    Column({ space: 20 }) {
      Text(this.title)
        .fontSize(18)
        .fontWeight(FontWeight.Bold)

      Text(this.message)
        .fontSize(14)
        .fontColor('#666666')
        .textAlign(TextAlign.Center)

      Row({ space: 16 }) {
        Button(this.cancelText)
          .width(100)
          .backgroundColor('#F0F0F0')
          .fontColor('#666666')
          .onClick(() => {
            this.onCancel()
            this.controller.close()
          })

        Button(this.confirmText)
          .width(100)
          .backgroundColor('#007DFF')
          .onClick(() => {
            this.onConfirm()
            this.controller.close()
          })
      }
    }
    .width(280)
    .padding(24)
    .backgroundColor(Color.White)
    .borderRadius(12)
  }
}

// 使用自定义弹窗
@Entry
@Component
struct DialogDemo {
  dialogController: CustomDialogController = new CustomDialogController({
    builder: ConfirmDialog({
      title: '确认删除',
      message: '确定要删除这条记录吗?删除后无法恢复。',
      confirmText: '删除',
      cancelText: '取消',
      onConfirm: () => {
        console.info('用户确认删除')
      },
      onCancel: () => {
        console.info('用户取消删除')
      }
    }),
    autoCancel: true,
    alignment: DialogAlignment.Center,
    maskColor: '#80000000'
  })

  // 底部弹窗控制器
  bottomDialogController: CustomDialogController = new CustomDialogController({
    builder: ConfirmDialog({
      title: '选择操作',
      message: '请选择要执行的操作',
      confirmText: '确认',
      cancelText: '关闭'
    }),
    alignment: DialogAlignment.Bottom,
    offset: { dx: 0, dy: -20 }
  })

  build() {
    Column({ space: 20 }) {
      Button('显示确认弹窗')
        .onClick(() => {
          this.dialogController.open()
        })

      Button('显示底部弹窗')
        .onClick(() => {
          this.bottomDialogController.open()
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

样式系统

通用属性

尺寸与位置

属性类型说明
widthLength宽度
heightLength高度
size{ width: Length, height: Length }同时设置宽高
paddingLength / Padding内边距
marginLength / Margin外边距
layoutWeightnumber布局权重
constraintSizeConstraintSizeOptions约束尺寸
aspectRationumber宽高比

位置属性

属性类型说明
positionPosition绝对定位
offsetPosition相对偏移
markAnchorPosition锚点标记
alignAlignment对齐方式
alignRulesAlignRuleOption相对布局规则

变换属性

属性类型说明
translateTranslateOptions平移变换
scaleScaleOptions缩放变换
rotateRotateOptions旋转变换
transformMatrix4矩阵变换

背景属性

属性类型说明
backgroundColorResourceColor背景颜色
backgroundImagestring / Resource背景图片
backgroundImageSizeImageSize / SizeOptions背景图片尺寸
backgroundImagePositionAlignment / Position背景图片位置
linearGradientLinearGradientOptions线性渐变
radialGradientRadialGradientOptions径向渐变
sweepGradientSweepGradientOptions扫描渐变

边框属性

属性类型说明
borderBorderOptions边框
borderRadiusLength / BorderRadiuses圆角
borderWidthLength / EdgeWidths边框宽度
borderColorResourceColor / EdgeColors边框颜色
borderStyleBorderStyle / EdgeStyles边框样式

阴影与效果

属性类型说明
shadowShadowOptions阴影
blurnumber模糊度
backdropBlurnumber背景模糊
clipboolean / Shape裁剪
maskProgressMask遮罩

代码示例

typescript
// 尺寸与位置
Text('尺寸示例')
  .width(200)
  .height(60)
  .layoutWeight(1)
  .constraintSize({ minWidth: 100, maxWidth: 300 })

// 位置示例
Text('绝对定位')
  .width(100)
  .height(40)
  .position({ x: 50, y: 100 })

Text('相对偏移')
  .width(100)
  .height(40)
  .offset({ x: 20, y: 10 })

// 变换示例
Text('缩放')
  .scale({ x: 1.5, y: 1.5 })

Text('旋转')
  .rotate({ angle: 45, centerX: '50%', centerY: '50%' })

Text('位移')
  .translate({ x: 50, y: 20 })

// 背景示例
Column()
  .width(200)
  .height(100)
  .backgroundColor('#007DFF')

Column()
  .width(200)
  .height(100)
  .linearGradient({
    angle: 90,
    colors: [['#007DFF', 0], ['#4ECDC4', 1]]
  })

Column()
  .width(200)
  .height(100)
  .radialGradient({
    center: ['50%', '50%'],
    radius: '50%',
    colors: [['#FF6B6B', 0], ['#4ECDC4', 1]]
  })

// 边框示例
Text('圆角边框')
  .width(150)
  .height(50)
  .border({ width: 2, color: '#007DFF', radius: 25 })

Text('单边边框')
  .width(150)
  .height(50)
  .borderWidth({ bottom: 2 })
  .borderColor({ bottom: '#007DFF' })

// 阴影示例
Column()
  .width(200)
  .height(100)
  .backgroundColor(Color.White)
  .borderRadius(8)
  .shadow({
    radius: 8,
    color: '#40000000',
    offsetX: 0,
    offsetY: 4
  })

// 裁剪示例
Image($r('app.media.photo'))
  .width(100)
  .height(100)
  .clip(true)  // 裁剪为矩形
  .borderRadius(50)  // 圆形裁剪

Image($r('app.media.photo'))
  .width(100)
  .height(100)
  .clip(new Circle({ width: 100, height: 100 }))  // 圆形裁剪

响应式布局

媒体查询

媒体查询用于根据设备特征应用不同的样式。

特征说明
width / height视口宽高
min-width / min-height最小宽高
max-width / max-height最大宽高
orientation屏幕方向
device-type设备类型
round-screen是否圆形屏幕
dark-mode是否深色模式

断点系统

断点系统将屏幕宽度划分为不同范围,根据断点调整布局。

断点范围设备类型
xs< 320vp超小屏
sm320vp - 520vp小屏(手机)
md520vp - 840vp中屏(折叠屏展开)
lg840vp - 1280vp大屏(平板)
xl>= 1280vp超大屏(2in1)

代码示例

typescript
// 媒体查询
@Entry
@Component
struct MediaQueryDemo {
  @State isWide: boolean = false
  listener: mediaquery.MediaQueryListener = mediaquery.matchMediaSync('(min-width: 600vp)')

  aboutToAppear() {
    this.listener.on('change', (mediaQueryResult: mediaquery.MediaQueryResult) => {
      this.isWide = mediaQueryResult.matches
    })
  }

  aboutToDisappear() {
    this.listener.off('change')
  }

  build() {
    if (this.isWide) {
      // 宽屏布局
      Row({ space: 16 }) {
        Column() {
          Text('侧边栏')
            .width(200)
            .height('100%')
            .backgroundColor('#F0F0F0')
        }
        Column() {
          Text('主内容区')
            .width('100%')
            .height('100%')
            .backgroundColor('#FFFFFF')
        }
        .layoutWeight(1)
      }
      .width('100%')
      .height('100%')
    } else {
      // 窄屏布局
      Column() {
        Text('主内容区')
          .width('100%')
          .height('100%')
          .backgroundColor('#FFFFFF')
      }
      .width('100%')
      .height('100%')
    }
  }
}

// 断点系统
@Entry
@Component
struct BreakpointDemo {
  @StorageProp('currentBreakpoint') currentBreakpoint: string = 'sm'

  build() {
    GridRow({
      columns: { xs: 1, sm: 2, md: 4, lg: 6 },
      gutter: { x: 12, y: 12 }
    }) {
      ForEach([1, 2, 3, 4, 5, 6], (item: number) => {
        GridCol({
          span: { xs: 1, sm: 1, md: 2, lg: 2 }
        }) {
          Text(`项目 ${item}`)
            .width('100%')
            .height(100)
            .backgroundColor('#007DFF')
            .fontColor(Color.White)
            .textAlign(TextAlign.Center)
        }
      })
    }
    .padding(16)
  }
}

// 响应式尺寸
@Entry
@Component
struct ResponsiveSizeDemo {
  @StorageProp('currentBreakpoint') currentBreakpoint: string = 'sm'

  getFontSize(): number {
    switch (this.currentBreakpoint) {
      case 'xs':
      case 'sm':
        return 14
      case 'md':
        return 16
      case 'lg':
      case 'xl':
        return 18
      default:
        return 14
    }
  }

  getPadding(): number {
    switch (this.currentBreakpoint) {
      case 'xs':
      case 'sm':
        return 12
      case 'md':
        return 16
      case 'lg':
      case 'xl':
        return 24
      default:
        return 12
    }
  }

  build() {
    Column() {
      Text('响应式文本')
        .fontSize(this.getFontSize())
        .padding(this.getPadding())
    }
    .width('100%')
    .height('100%')
  }
}

主题和暗色模式

ArkUI 支持主题系统和暗色模式适配。

属性说明
@ohos.arkui.theme主题管理
Theme主题对象
WithTheme作用域主题

代码示例

typescript
// 暗色模式适配
@Entry
@Component
struct DarkModeDemo {
  @StorageProp('currentColorMode') currentColorMode: number = 0

  getBgColor(): ResourceColor {
    return this.currentColorMode === 0 ? '#FFFFFF' : '#1A1A1A'
  }

  getTextColor(): ResourceColor {
    return this.currentColorMode === 0 ? '#333333' : '#FFFFFF'
  }

  getCardBgColor(): ResourceColor {
    return this.currentColorMode === 0 ? '#F5F5F5' : '#2A2A2A'
  }

  build() {
    Column({ space: 16 }) {
      Text('暗色模式适配')
        .fontSize(20)
        .fontColor(this.getTextColor())

      Column() {
        Text('卡片内容')
          .fontSize(14)
          .fontColor(this.getTextColor())
      }
      .width('100%')
      .height(100)
      .backgroundColor(this.getCardBgColor())
      .borderRadius(8)
      .padding(16)

      Text(`当前模式: ${this.currentColorMode === 0 ? '浅色' : '深色'}`)
        .fontSize(14)
        .fontColor(this.getTextColor())
    }
    .width('100%')
    .height('100%')
    .backgroundColor(this.getBgColor())
    .padding(16)
  }
}

// 使用系统颜色资源
@Entry
@Component
struct SystemColorDemo {
  build() {
    Column({ space: 16 }) {
      // 使用系统颜色,自动适配深浅色模式
      Text('主要文本')
        .fontColor($r('sys.color.font_primary'))

      Text('次要文本')
        .fontColor($r('sys.color.font_secondary'))

      Column() {
        Text('强调色背景')
          .fontColor($r('sys.color.font_on_primary'))
      }
      .width(200)
      .height(50)
      .backgroundColor($r('sys.color.background_primary'))
      .borderRadius(8)
      .justifyContent(FlexAlign.Center)

      Column() {
        Text('卡片背景')
      }
      .width(200)
      .height(50)
      .backgroundColor($r('sys.color.background_secondary'))
      .borderRadius(8)
      .justifyContent(FlexAlign.Center)
    }
    .width('100%')
    .height('100%')
    .padding(16)
  }
}

// 自定义主题
@Entry
@Component
struct CustomThemeDemo {
  @State primaryColor: ResourceColor = '#007DFF'
  @State secondaryColor: ResourceColor = '#4ECDC4'

  build() {
    WithTheme({ theme: {
      colors: {
        brand: this.primaryColor,
        warning: '#FF6B6B',
        success: '#4ECDC4'
      }
    }}) {
      Column({ space: 16 }) {
        Button('主要按钮')
          .backgroundColor($r('sys.color.brand'))

        Button('成功按钮')
          .backgroundColor($r('sys.color.success'))

        Button('警告按钮')
          .backgroundColor($r('sys.color.warning'))
      }
      .padding(16)
    }
  }
}

条件渲染

typescript
@State isLogin: boolean = false
@State isLoading: boolean = true

build() {
  Column() {
    if (this.isLoading) {
      LoadingProgress()
        .width(50)
        .height(50)
    } else if (this.isLogin) {
      Text('欢迎回来')
        .fontSize(20)
    } else {
      Button('登录')
        .onClick(() => {
          this.isLogin = true
        })
    }
  }
}

列表渲染

typescript
@State items: string[] = ['苹果', '香蕉', '橙子', '葡萄']

build() {
  List({ space: 8 }) {
    ForEach(this.items, (item: string, index: number) => {
      ListItem() {
        Row() {
          Text(`${index + 1}. ${item}`)
            .fontSize(16)
          Button('删除')
            .fontSize(12)
            .onClick(() => {
              this.items.splice(index, 1)
            })
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceBetween)
        .padding(16)
      }
    }, (item: string) => item)
  }
  .width('100%')
  .divider({ strokeWidth: 1, color: '#EEEEEE' })
}

样式复用

@Styles 通用样式

typescript
@Styles
function cardStyle() {
  .padding(16)
  .borderRadius(8)
  .backgroundColor(Color.White)
  .shadow({ radius: 4, color: '#1F000000', offsetX: 0, offsetY: 2 })
}

// 使用
Column() {
  Text('卡片内容')
}
.cardStyle()
.width('90%')

@Extend 组件扩展

typescript
@Extend(Text)
function title(size: number, color: ResourceColor) {
  .fontSize(size)
  .fontColor(color)
  .fontWeight(FontWeight.Bold)
}

// 使用
Text('标题').title(24, '#333333')
Text('副标题').title(18, '#666666')

@Builder UI 复用

typescript
@Builder
function InfoCard(title: string, content: string) {
  Column() {
    Text(title)
      .fontSize(16)
      .fontWeight(FontWeight.Bold)
    Text(content)
      .fontSize(14)
      .fontColor('#666666')
      .margin({ top: 8 })
  }
  .width('100%')
  .padding(16)
  .backgroundColor('#F5F5F5')
  .borderRadius(8)
}

// 使用
build() {
  Column() {
    InfoCard('标题1', '内容1')
    InfoCard('标题2', '内容2')
  }
}

单位系统

单位说明示例
vp虚拟像素(默认)width(100)
fp字体像素fontSize('16fp')
px物理像素width('100px')
lpx逻辑像素width('100lpx')
%百分比width('100%')

数字默认使用 vp,百分比用字符串表示。