系列文章:鸿蒙NEXT开发实战系列 -- 第2篇(共5篇) 适合人群:有基础ArkTS语法知识,想系统掌握ArkUI组件库的开发者 开发环境:DevEco Studio 5.0.5+ | HarmonyOS NEXT (API 14) 阅读时长:约40分钟

上一篇:鸿蒙NEXT开发从零到一 | 下一篇:状态管理一文通


目录


一、引言:为什么ArkUI是鸿蒙开发的核心?

在鸿蒙NEXT(HarmonyOS NEXT)的应用开发中,ArkUI 是构建用户界面的核心框架。它采用声明式UI范式,让开发者可以用简洁直观的代码描述界面结构和交互逻辑。与传统的命令式UI相比,ArkUI的声明式写法不仅大幅减少了代码量,还让UI状态管理变得更加清晰可控。

对于从Android或iOS转过来的开发者,ArkUI的学习曲线并不陡峭。它的设计理念融合了Flutter的声明式语法和React的状态驱动思想,同时针对鸿蒙的分布式能力做了深度优化。掌握ArkUI组件库,就等于拿到了鸿蒙应用开发的"入场券"。

本文将从基础组件讲起,逐步深入到布局系统自定义组件样式主题动画效果,配合完整可运行的实战代码和踩坑记录,帮助你系统掌握ArkUI的核心能力。无论你是鸿蒙新手还是有经验的开发者,这篇文章都值得收藏备用。


二、常用基础组件详解

2.1 Text 文本组件

Text 是最基础的组件,用于展示文字内容。它支持富文本、文本样式定制和文本溢出处理。

// TextComponent.ets
@Entry
@Component
struct TextExample {
  build() {
    Column({ space: 16 }) {
      // 基础文本
      Text('Hello HarmonyOS NEXT!')

      // 带样式的文本
      Text('这是一段带样式的文本')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .fontColor('#FF6B35')
        .fontFamily('HarmonyOS Sans')

      // 富文本(通过Span拼接不同样式)
      Text() {
        Span('鸿蒙NEXT ')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor('#007DFF')
        Span('是华为推出的')
          .fontSize(16)
          .fontColor('#333333')
        Span('全场景分布式操作系统')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor('#FF6B35')
      }
      .textArea({ overflow: TextOverflow.Ellipsis, maxLines: 2 })

      // 文本溢出处理
      Text('这是一段很长很长的文本,用于演示文本溢出处理效果,当文本超出指定行数时会显示省略号')
        .fontSize(14)
        .maxLines(2)
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .width('90%')
    }
    .width('100%')
    .padding(16)
  }
}

关键属性说明:

属性

说明

示例值

fontSize

字体大小

16'16fp'

fontWeight

字体粗细

FontWeight.BoldFontWeight.Normal

fontColor

字体颜色

'#FF6B35'Color.Red

fontFamily

字体族

'HarmonyOS Sans'

maxLines

最大行数

23

textOverflow

溢出处理

{ overflow: TextOverflow.Ellipsis }

textDecoration

文本装饰

TextDecoration.Underline

letterSpacing

字间距

2'2vp'


2.2 Image 图片组件

Image 组件用于展示图片,支持本地资源、网络图片和Base64数据。

// ImageExample.ets
@Entry
@Component
struct ImageExample {
  build() {
    Column({ space: 16 }) {
      // 本地资源图片
      Image($r('app.media.icon'))
        .width(100)
        .height(100)
        .borderRadius(12)

      // 网络图片
      Image('https://developer.huawei.com/images/icon/harmonyos.png')
        .width(200)
        .height(120)
        .objectFit(ImageFit.Cover)      // 填充模式
        .borderRadius(8)
        .shadow({                       // 阴影效果
          radius: 10,
          color: 'rgba(0,0,0,0.3)',
          offsetX: 2,
          offsetY: 2
        })

      // SVG矢量图片
      Image($r('app.media.ic_arrow_right'))
        .width(24)
        .height(24)
        .fillColor('#007DFF')           // SVG着色

      // 带占位图和错误图的网络图片
      Image('https://example.com/image.png')
        .width(200)
        .height(150)
        .alt($r('app.media.placeholder'))   // 加载失败显示
        .objectFit(ImageFit.Contain)
        .onComplete(() => {
          console.info('图片加载成功')
        })
        .onError(() => {
          console.error('图片加载失败')
        })
    }
    .width('100%')
    .padding(16)
  }
}

objectFit 填充模式对比:

模式

说明

适用场景

ImageFit.Cover

等比缩放,填满容器,可能裁切

头图、封面

ImageFit.Contain

等比缩放,完整显示,可能留白

详情图

ImageFit.Fill

拉伸填满,可能变形

不推荐

ImageFit.None

保持原始尺寸

小图标


2.3 Button 按钮组件

Button 组件支持多种样式和状态,是交互的核心组件。

// ButtonExample.ets
@Entry
@Component
struct ButtonExample {
  @State isLoading: boolean = false

  build() {
    Column({ space: 20 }) {
      // 基础按钮
      Button('点击我')
        .onClick(() => {
          console.info('按钮被点击')
        })

      // 主要按钮(强调色)
      Button('主要操作')
        .type(ButtonType.Capsule)       // 胶囊形状
        .stateEffect(true)              // 点击态效果
        .backgroundColor('#007DFF')
        .fontColor(Color.White)
        .fontSize(16)
        .fontWeight(FontWeight.Medium)
        .width('90%')
        .height(48)
        .borderRadius(24)

      // 次要按钮(描边样式)
      Button('次要操作')
        .type(ButtonType.Capsule)
        .backgroundColor(Color.Transparent)
        .fontColor('#007DFF')
        .border({
          width: 1,
          color: '#007DFF',
          style: BorderStyle.Solid
        })
        .width('90%')
        .height(48)

      // 危险操作按钮
      Button('删除')
        .type(ButtonType.Capsule)
        .backgroundColor('#FF4D4F')
        .fontColor(Color.White)
        .width('90%')
        .height(48)

      // 带图标的按钮
      Button({ type: ButtonType.Capsule, stateEffect: true }) {
        Row({ space: 8 }) {
          Image($r('app.media.ic_add'))
            .width(18)
            .height(18)
            .fillColor(Color.White)
          Text('新建')
            .fontSize(16)
            .fontColor(Color.White)
        }
      }
      .backgroundColor('#007DFF')
      .width('90%')
      .height(48)

      // 加载状态按钮
      Button(this.isLoading ? '加载中...' : '提交订单')
        .type(ButtonType.Capsule)
        .backgroundColor(this.isLoading ? '#CCCCCC' : '#007DFF')
        .fontColor(Color.White)
        .width('90%')
        .height(48)
        .enabled(!this.isLoading)
        .onClick(() => {
          this.isLoading = true
          // 模拟异步操作
          setTimeout(() => {
            this.isLoading = false
          }, 2000)
        })
    }
    .width('100%')
    .padding(16)
  }
}

2.4 TextInput 输入框组件

// TextInputExample.ets
@Entry
@Component
struct TextInputExample {
  @State username: string = ''
  @State password: string = ''
  @State searchKey: string = ''

  build() {
    Column({ space: 16 }) {
      // 普通输入框
      TextInput({ placeholder: '请输入用户名', text: this.username })
        .type(InputType.Normal)
        .maxLength(20)
        .onChange((value: string) => {
          this.username = value
        })
        .width('90%')
        .height(48)
        .padding({ left: 16, right: 16 })
        .borderRadius(8)
        .backgroundColor('#F5F5F5')

      // 密码输入框
      TextInput({ placeholder: '请输入密码', text: this.password })
        .type(InputType.Password)
        .maxLength(20)
        .onChange((value: string) => {
          this.password = value
        })
        .width('90%')
        .height(48)
        .padding({ left: 16, right: 16 })
        .borderRadius(8)
        .backgroundColor('#F5F5F5')

      // 带搜索图标的输入框
      Row() {
        Image($r('app.media.ic_search'))
          .width(20)
          .height(20)
          .margin({ left: 12 })
          .fillColor('#999999')
        TextInput({ placeholder: '搜索内容', text: this.searchKey })
          .type(InputType.Normal)
          .onChange((value: string) => {
            this.searchKey = value
          })
          .layoutWeight(1)
          .height(40)
          .backgroundColor(Color.Transparent)
          .padding({ left: 8, right: 12 })
      }
      .width('90%')
      .height(48)
      .borderRadius(24)
      .backgroundColor('#F5F5F5')
      .alignItems(VerticalAlign.Center)

      // 多行文本输入
      TextInput({ placeholder: '请输入详细描述...' })
        .type(InputType.Normal)
        .width('90%')
        .height(120)
        .padding(16)
        .borderRadius(8)
        .backgroundColor('#F5F5F5')
        .textAlign(TextAlign.Start)
    }
    .width('100%')
    .padding(16)
  }
}

2.5 Toggle 开关组件

// ToggleExample.ets
@Entry
@Component
struct ToggleExample {
  @State isOn: boolean = true
  @State isNotify: boolean = false

  build() {
    Row({ space: 16 }) {
      // 开关样式
      Toggle({ type: ToggleType.Switch, isOn: this.isOn })
        .selectedColor('#007DFF')
        .switchPointColor(Color.White)
        .onChange((isOn: boolean) => {
          this.isOn = isOn
          console.info(`开关状态: ${isOn ? '开启' : '关闭'}`)
        })

      Text(this.isOn ? '已开启' : '已关闭')
        .fontSize(16)
        .fontColor(this.isOn ? '#007DFF' : '#999999')
    }
    .width('100%')
    .padding(16)
  }
}

2.6 Progress 进度条组件

// ProgressExample.ets
@Entry
@Component
struct ProgressExample {
  @State progressValue: number = 40

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

      // 模糊进度条(不确定进度)
      Progress({ value: 0, total: 100, type: ProgressType.ScaleRing })
        .width(80)
        .height(80)
        .color('#007DFF')

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

      Text(`当前进度: ${this.progressValue}%`)
        .fontSize(16)

      Button('增加进度')
        .onClick(() => {
          if (this.progressValue < 100) {
            this.progressValue += 10
          }
        })
    }
    .width('100%')
    .padding(16)
  }
}

2.7 List 列表组件

List 是高性能滚动容器,适用于长列表场景。

// ListExample.ets
interface ListItemData {
  id: number
  title: string
  description: string
  icon: Resource
}

@Entry
@Component
struct ListExample {
  private items: ListItemData[] = Array.from({ length: 20 }, (_, i) => ({
    id: i,
    title: `列表项 ${i + 1}`,
    description: `这是第 ${i + 1} 个列表项的描述信息`,
    icon: $r('app.media.ic_list_item')
  }))

  build() {
    List({ space: 8 }) {
      ForEach(this.items, (item: ListItemData) => {
        ListItem() {
          Row({ space: 12 }) {
            Image(item.icon)
              .width(48)
              .height(48)
              .borderRadius(8)

            Column({ space: 4 }) {
              Text(item.title)
                .fontSize(16)
                .fontWeight(FontWeight.Medium)
                .fontColor('#333333')

              Text(item.description)
                .fontSize(14)
                .fontColor('#999999')
                .maxLines(1)
                .textOverflow({ overflow: TextOverflow.Ellipsis })
            }
            .alignItems(HorizontalAlign.Start)
            .layoutWeight(1)

            Image($r('app.media.ic_arrow_right'))
              .width(16)
              .height(16)
              .fillColor('#CCCCCC')
          }
          .width('100%')
          .padding(12)
          .backgroundColor(Color.White)
          .borderRadius(12)
        }
      }, (item: ListItemData) => item.id.toString())
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5')
    .padding({ left: 16, right: 16, top: 8, bottom: 8 })
    .divider({                       // 分割线
      strokeWidth: 0.5,
      color: '#E8E8E8',
      startMargin: 60,
      endMargin: 16
    })
  }
}

三、布局系统详解

ArkUI提供了多种布局容器,掌握它们的特性和使用场景是构建复杂界面的关键。

3.1 Column 线性布局(垂直)

Column 将子组件从上到下依次排列。

// ColumnExample.ets
@Entry
@Component
struct ColumnExample {
  build() {
    Column({ space: 12 }) {
      // space 控制子组件间距
      Text('Header')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)

      Text('Content Area')
        .fontSize(16)

      Text('Footer')
        .fontSize(14)
        .fontColor('#999999')
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.SpaceBetween)   // 两端对齐
    .alignItems(HorizontalAlign.Center)       // 水平居中
    .padding(16)
    .backgroundColor('#FFFFFF')
  }
}

justifyContent 取值说明:

说明

效果

FlexAlign.Start

靠起始端

子组件顶部对齐

FlexAlign.Center

居中

子组件垂直居中

FlexAlign.End

靠末尾端

子组件底部对齐

FlexAlign.SpaceBetween

两端对齐

首尾贴边,中间均分

FlexAlign.SpaceAround

等间距

每个子组件两侧等间距

FlexAlign.SpaceEvenly

均匀分布

所有间距相等


3.2 Row 线性布局(水平)

Row 将子组件从左到右依次排列。

// RowExample.ets
@Entry
@Component
struct RowExample {
  build() {
    Column({ space: 16 }) {
      // 基础水平布局
      Row({ space: 12 }) {
        Text('标签1')
          .fontSize(14)
          .padding({ left: 12, right: 12, top: 6, bottom: 6 })
          .backgroundColor('#E8F4FD')
          .borderRadius(16)
          .fontColor('#007DFF')

        Text('标签2')
          .fontSize(14)
          .padding({ left: 12, right: 12, top: 6, bottom: 6 })
          .backgroundColor('#FFF1E8')
          .borderRadius(16)
          .fontColor('#FF6B35')

        Text('标签3')
          .fontSize(14)
          .padding({ left: 12, right: 12, top: 6, bottom: 6 })
          .backgroundColor('#E8F8E8')
          .borderRadius(16)
          .fontColor('#52C41A')
      }

      // 卡片布局示例
      Row({ space: 12 }) {
        Image($r('app.media.avatar'))
          .width(60)
          .height(60)
          .borderRadius(30)

        Column({ space: 4 }) {
          Text('张三')
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
          Text('鸿蒙开发工程师')
            .fontSize(14)
            .fontColor('#666666')
        }
        .alignItems(HorizontalAlign.Start)
        .layoutWeight(1)    // 占据剩余空间

        Button('关注')
          .type(ButtonType.Capsule)
          .fontSize(14)
          .height(32)
          .backgroundColor('#007DFF')
      }
      .width('100%')
      .padding(16)
      .backgroundColor(Color.White)
      .borderRadius(12)
    }
    .width('100%')
    .padding(16)
  }
}

3.3 Stack 层叠布局

Stack 将子组件层叠显示,后添加的组件在上层,适合实现叠加效果。

// StackExample.ets
@Entry
@Component
struct StackExample {
  build() {
    Column({ space: 16 }) {
      // 图片上叠加文字
      Stack({ alignContent: Alignment.BottomStart }) {
        Image($r('app.media.banner'))
          .width('100%')
          .height(200)
          .borderRadius(12)
          .objectFit(ImageFit.Cover)

        // 渐变遮罩 + 文字
        Column() {
          Text('鸿蒙NEXT应用开发实战')
            .fontSize(20)
            .fontWeight(FontWeight.Bold)
            .fontColor(Color.White)
          Text('2026年最新教程')
            .fontSize(14)
            .fontColor('rgba(255,255,255,0.8)')
        }
        .width('100%')
        .padding(16)
        .linearGradient({
          direction: GradientDirection.Bottom,
          colors: [['rgba(0,0,0,0)', 0], ['rgba(0,0,0,0.7)', 1]]
        })
      }
      .width('100%')
      .height(200)

      // 角标示例
      Stack({ alignContent: Alignment.TopEnd }) {
        Image($r('app.media.ic_message'))
          .width(40)
          .height(40)

        Text('99+')
          .fontSize(10)
          .fontColor(Color.White)
          .backgroundColor('#FF4D4F')
          .borderRadius(10)
          .padding({ left: 6, right: 6, top: 2, bottom: 2 })
          .translate({ x: 8, y: -8 })   // 微调位置
      }
      .width(56)
      .height(56)
    }
    .width('100%')
    .padding(16)
  }
}

3.4 Flex 弹性布局

Flex 是最灵活的布局容器,支持主轴方向、换行、对齐等丰富配置。

// FlexExample.ets
@Entry
@Component
struct FlexExample {
  build() {
    Column({ space: 20 }) {
      Text('Flex Wrap 示例(标签流式布局)')
        .fontSize(16)
        .fontWeight(FontWeight.Medium)

      // 流式标签布局
      Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.Start, space: { main: LengthMetrics.vp(8), cross: LengthMetrics.vp(8) } }) {
        ForEach(['鸿蒙', 'ArkUI', 'HarmonyOS', 'NEXT', '声明式UI', '分布式', '原子化服务', '元服务'], (tag: string) => {
          Text(tag)
            .fontSize(14)
            .fontColor('#007DFF')
            .padding({ left: 16, right: 16, top: 8, bottom: 8 })
            .border({
              width: 1,
              color: '#007DFF',
              style: BorderStyle.Solid
            })
            .borderRadius(20)
        })
      }
      .width('100%')

      Text('Flex Direction 示例(水平分布)')
        .fontSize(16)
        .fontWeight(FontWeight.Medium)

      // 均匀分布的按钮组
      Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceEvenly }) {
        Button('取消')
          .type(ButtonType.Capsule)
          .backgroundColor('#F5F5F5')
          .fontColor('#666666')
          .layoutWeight(1)
          .margin({ right: 8 })

        Button('确认')
          .type(ButtonType.Capsule)
          .backgroundColor('#007DFF')
          .fontColor(Color.White)
          .layoutWeight(1)
          .margin({ left: 8 })
      }
      .width('100%')
    }
    .width('100%')
    .padding(16)
  }
}

3.5 Grid 网格布局

Grid 适用于九宫格、商品列表等网格排列场景。

// GridExample.ets
@Entry
@Component
struct GridExample {
  private gridItems: string[] = [
    '美食', '电影', '酒店', 'KTV',
    '外卖', '打车', '充值', '更多'
  ]

  build() {
    Column() {
      Text('服务入口')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 16 })

      Grid() {
        ForEach(this.gridItems, (item: string, index: number) => {
          GridItem() {
            Column({ space: 8 }) {
              Image($r(`app.media.ic_grid_${index}`))
                .width(40)
                .height(40)
              Text(item)
                .fontSize(12)
                .fontColor('#333333')
            }
            .width('100%')
            .height('100%')
            .justifyContent(FlexAlign.Center)
          }
        })
      }
      .columnsTemplate('1fr 1fr 1fr 1fr')     // 4列等宽
      .rowsTemplate('1fr 1fr')                 // 2行等高
      .columnsGap(8)
      .rowsGap(8)
      .width('100%')
      .height(200)
      .backgroundColor(Color.White)
      .borderRadius(12)
      .padding(16)
    }
    .width('100%')
    .padding(16)
  }
}

3.6 RelativeContainer 相对布局

RelativeContainer 允许子组件通过相对关系定位,适合复杂界面。

// RelativeContainerExample.ets
@Entry
@Component
struct RelativeContainerExample {
  build() {
    RelativeContainer() {
      // 居中的标题
      Text('相对布局示例')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .id('title')
        .alignRules({
          center: { anchor: '__container__', align: VerticalAlign.Center },
          middle: { anchor: '__container__', align: HorizontalAlign.Center }
        })

      // 左侧返回按钮
      Image($r('app.media.ic_back'))
        .width(24)
        .height(24)
        .id('backBtn')
        .alignRules({
          left: { anchor: '__container__', align: HorizontalAlign.Start },
          center: { anchor: 'title', align: VerticalAlign.Center }
        })
        .margin({ left: 16 })

      // 右侧操作按钮
      Image($r('app.media.ic_more'))
        .width(24)
        .height(24)
        .id('moreBtn')
        .alignRules({
          right: { anchor: '__container__', align: HorizontalAlign.End },
          center: { anchor: 'title', align: VerticalAlign.Center }
        })
        .margin({ right: 16 })
    }
    .width('100%')
    .height(56)
    .backgroundColor(Color.White)
  }
}

四、自定义组件开发教程

4.1 组件基础结构

ArkUI的自定义组件通过 @Component 装饰器定义,支持参数传递和生命周期管理。

// CustomCard.ets
@Component
export struct CustomCard {
  // 组件参数(外部传入)
  @Prop title: string = ''
  @Prop content: string = ''
  @Prop icon: Resource = $r('app.media.ic_default')

  // 内部状态
  @State isExpanded: boolean = false

  // 事件回调
  onCardClick?: () => void

  build() {
    Column({ space: 8 }) {
      Row({ space: 12 }) {
        Image(this.icon)
          .width(32)
          .height(32)

        Text(this.title)
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
          .layoutWeight(1)

        Image(this.isExpanded ? $r('app.media.ic_up') : $r('app.media.ic_down'))
          .width(16)
          .height(16)
      }
      .width('100%')

      if (this.isExpanded) {
        Text(this.content)
          .fontSize(14)
          .fontColor('#666666')
          .width('100%')
          .transition({
            type: TransitionType.Insert,
            opacity: 0,
            translate: { y: -20 }
          })
      }
    }
    .width('100%')
    .padding(16)
    .backgroundColor(Color.White)
    .borderRadius(12)
    .shadow({ radius: 4, color: 'rgba(0,0,0,0.08)', offsetY: 2 })
    .onClick(() => {
      this.isExpanded = !this.isExpanded
      this.onCardClick?.()
    })
  }
}

// 使用示例
@Entry
@Component
struct CustomCardDemo {
  build() {
    Column({ space: 12 }) {
      CustomCard({
        title: '鸿蒙NEXT特性',
        content: 'ArkUI声明式开发范式,让UI开发更简洁高效。支持一次开发多端部署,覆盖手机、平板、智慧屏等设备。',
        icon: $r('app.media.ic_feature')
      })

      CustomCard({
        title: '开发工具',
        content: 'DevEco Studio提供完整的开发、调试、测试环境,支持模拟器和真机调试。',
        icon: $r('app.media.ic_tool')
      })
    }
    .width('100%')
    .padding(16)
    .backgroundColor('#F5F5F5')
  }
}

4.2 @Builder 自定义构建函数

@Builder 用于定义可复用的UI片段,避免代码重复。

// BuilderExample.ets
@Entry
@Component
struct BuilderExample {
  @State activeTab: number = 0

  // 底部导航栏 Builder
  @Builder
  TabBarBuilder() {
    Row({ space: 0 }) {
      this.TabItemBuilder('首页', $r('app.media.ic_home'), 0)
      this.TabItemBuilder('发现', $r('app.media.ic_discover'), 1)
      this.TabItemBuilder('消息', $r('app.media.ic_message'), 2)
      this.TabItemBuilder('我的', $r('app.media.ic_profile'), 3)
    }
    .width('100%')
    .height(56)
    .backgroundColor(Color.White)
    .border({ width: { top: 0.5 }, color: '#E8E8E8' })
  }

  // 单个Tab项 Builder
  @Builder
  TabItemBuilder(label: string, icon: Resource, index: number) {
    Column({ space: 4 }) {
      Image(icon)
        .width(24)
        .height(24)
        .fillColor(this.activeTab === index ? '#007DFF' : '#999999')
      Text(label)
        .fontSize(10)
        .fontColor(this.activeTab === index ? '#007DFF' : '#999999')
    }
    .layoutWeight(1)
    .justifyContent(FlexAlign.Center)
    .height('100%')
    .onClick(() => {
      this.activeTab = index
    })
  }

  build() {
    Column() {
      // 内容区域
      Column() {
        Text(`当前页面: ${this.activeTab}`)
          .fontSize(20)
      }
      .layoutWeight(1)
      .justifyContent(FlexAlign.Center)

      // 底部导航
      this.TabBarBuilder()
    }
    .width('100%')
    .height('100%')
  }
}

4.3 @Extend 扩展原生组件

@Extend 用于扩展原生组件的样式和能力。

// ExtendExample.ets
// 扩展 Text 组件 - 标题样式
@Extend(Text)
function titleStyle() {
  .fontSize(24)
  .fontWeight(FontWeight.Bold)
  .fontColor('#1A1A1A')
  .lineHeight(32)
}

// 扩展 Text 组件 - 副标题样式
@Extend(Text)
function subtitleStyle() {
  .fontSize(16)
  .fontWeight(FontWeight.Medium)
  .fontColor('#666666')
  .lineHeight(24)
}

// 扩展 Text 组件 - 辅助文字样式
@Extend(Text)
function captionStyle() {
  .fontSize(12)
  .fontColor('#999999')
  .lineHeight(18)
}

// 扩展 Image 组件 - 圆形头像
@Extend(Image)
function avatarStyle(size: number = 48) {
  .width(size)
  .height(size)
  .borderRadius(size / 2)
  .clip(true)
}

// 扩展 Button 组件 - 主要按钮
@Extend(Button)
function primaryButtonStyle() {
  .type(ButtonType.Capsule)
  .backgroundColor('#007DFF')
  .fontColor(Color.White)
  .fontSize(16)
  .fontWeight(FontWeight.Medium)
  .height(48)
  .width('100%')
}

// 使用示例
@Entry
@Component
struct ExtendDemo {
  build() {
    Column({ space: 16 }) {
      Text('文章标题')        // 应用 titleStyle
        .titleStyle()

      Text('文章副标题')      // 应用 subtitleStyle
        .subtitleStyle()

      Image($r('app.media.avatar'))
        .avatarStyle(64)      // 应用 avatarStyle,传入自定义尺寸

      Text('发布时间:2026-05-07')  // 应用 captionStyle
        .captionStyle()

      Button('立即体验')      // 应用 primaryButtonStyle
        .primaryButtonStyle()
    }
    .width('100%')
    .padding(16)
  }
}

4.4 @Styles 通用样式复用

@Styles 用于提取通用的样式属性。

// StylesExample.ets
@Styles
function cardStyle() {
  .width('100%')
  .padding(16)
  .backgroundColor(Color.White)
  .borderRadius(12)
  .shadow({ radius: 4, color: 'rgba(0,0,0,0.06)', offsetY: 2 })
}

@Styles
function sectionTitleStyle() {
  .fontSize(18)
  .fontWeight(FontWeight.Bold)
  .fontColor('#1A1A1A')
  .margin({ bottom: 12 })
}

@Entry
@Component
struct StylesDemo {
  build() {
    Column({ space: 16 }) {
      Column() {
        Text('订单信息')
          .sectionTitleStyle()

        Text('订单号: HM20260507001')
          .fontSize(14)
          .fontColor('#666666')
      }
      .cardStyle()

      Column() {
        Text('收货地址')
          .sectionTitleStyle()

        Text('广东省深圳市龙岗区华为基地')
          .fontSize(14)
          .fontColor('#666666')
      }
      .cardStyle()
    }
    .width('100%')
    .padding(16)
    .backgroundColor('#F5F5F5')
  }
}

4.5 @CustomDecoration 自定义装饰器(实战案例)

接下来我们通过一个完整的实战案例,综合运用上述知识点,开发一个商品卡片组件

// ProductCard.ets
// 自定义商品卡片组件

// 样式复用
@Styles
function cardShadow() {
  .shadow({ radius: 8, color: 'rgba(0,0,0,0.08)', offsetY: 4 })
}

// 扩展文本样式
@Extend(Text)
function priceStyle() {
  .fontSize(20)
  .fontWeight(FontWeight.Bold)
  .fontColor('#FF4D4F')
}

@Extend(Text)
function originalPriceStyle() {
  .fontSize(12)
  .fontColor('#999999')
  .decoration({ type: TextDecorationType.LineThrough })
}

// 评分星星 Builder
@Component
struct StarRating {
  @Prop rating: number = 0

  @Builder
  StarIcon(filled: boolean) {
    Text(filled ? '★' : '☆')
      .fontSize(14)
      .fontColor(filled ? '#FFD700' : '#CCCCCC')
  }

  build() {
    Row({ space: 2 }) {
      ForEach([1, 2, 3, 4, 5], (star: number) => {
        this.StarIcon(star <= this.rating)
      })

      Text(`(${this.rating}.0)`)
        .fontSize(12)
        .fontColor('#999999')
        .margin({ left: 4 })
    }
  }
}

// 商品卡片组件
@Component
export struct ProductCard {
  @Prop productName: string = ''
  @Prop productImage: Resource = $r('app.media.ic_product')
  @Prop currentPrice: number = 0
  @Prop originalPrice: number = 0
  @Prop salesCount: number = 0
  @Prop rating: number = 5

  // 事件回调
  onProductClick?: () => void
  onAddCart?: () => void

  // 计算折扣
  private get discount(): string {
    if (this.originalPrice > 0 && this.currentPrice > 0) {
      return (this.currentPrice / this.originalPrice * 10).toFixed(1)
    }
    return '10.0'
  }

  build() {
    Column({ space: 8 }) {
      // 商品图片
      Stack({ alignContent: Alignment.TopLeft }) {
        Image(this.productImage)
          .width('100%')
          .height(180)
          .objectFit(ImageFit.Cover)
          .borderRadius({ topLeft: 12, topRight: 12 })

        // 折扣标签
        if (parseFloat(this.discount) < 10) {
          Text(`${this.discount}折`)
            .fontSize(10)
            .fontColor(Color.White)
            .backgroundColor('#FF4D4F')
            .padding({ left: 8, right: 8, top: 2, bottom: 2 })
            .borderRadius({ topLeft: 12, bottomRight: 8 })
        }
      }
      .width('100%')

      // 商品信息
      Column({ space: 4 }) {
        Text(this.productName)
          .fontSize(14)
          .fontWeight(FontWeight.Medium)
          .fontColor('#333333')
          .maxLines(2)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
          .lineHeight(20)

        // 评分
        StarRating({ rating: this.rating })

        // 价格区域
        Row({ space: 8 }) {
          Text(`¥${this.currentPrice}`)
            .priceStyle()

          Text(`¥${this.originalPrice}`)
            .originalPriceStyle()

          Blank()

          Text(`已售${this.salesCount > 10000 ? (this.salesCount / 10000).toFixed(1) + '万' : this.salesCount}件`)
            .fontSize(12)
            .fontColor('#999999')
        }
        .width('100%')

        // 加入购物车按钮
        Button('加入购物车')
          .type(ButtonType.Capsule)
          .fontSize(12)
          .height(28)
          .backgroundColor('#FF6B35')
          .fontColor(Color.White)
          .width('100%')
          .margin({ top: 4 })
          .onClick(() => {
            this.onAddCart?.()
          })
      }
      .padding({ left: 8, right: 8, bottom: 8 })
    }
    .width('100%')
    .backgroundColor(Color.White)
    .borderRadius(12)
    .cardShadow()
    .onClick(() => {
      this.onProductClick?.()
    })
  }
}

// 使用示例
@Entry
@Component
struct ProductCardDemo {
  build() {
    Column({ space: 12 }) {
      Text('热门商品')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .padding({ left: 16, right: 16 })

      Row({ space: 12 }) {
        ProductCard({
          productName: '鸿蒙NEXT开发实战指南(2026版)',
          productImage: $r('app.media.product_book'),
          currentPrice: 79.9,
          originalPrice: 129.0,
          salesCount: 15800,
          rating: 5,
          onProductClick: () => {
            console.info('点击了商品1')
          },
          onAddCart: () => {
            console.info('商品1加入购物车')
          }
        })
        .layoutWeight(1)

        ProductCard({
          productName: 'HarmonyOS智能手表运动版',
          productImage: $r('app.media.product_watch'),
          currentPrice: 1299,
          originalPrice: 1599,
          salesCount: 8600,
          rating: 4,
          onProductClick: () => {
            console.info('点击了商品2')
          },
          onAddCart: () => {
            console.info('商品2加入购物车')
          }
        })
        .layoutWeight(1)
      }
      .padding({ left: 16, right: 16 })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5')
  }
}

五、样式与主题系统

5.1 内联样式

最直接的样式设置方式,适合一次性使用的样式。

// InlineStyleExample.ets
@Entry
@Component
struct InlineStyleExample {
  build() {
    Column({ space: 16 }) {
      // 完整的内联样式示例
      Text('内联样式示例')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .fontColor('#1A1A1A')
        .textAlign(TextAlign.Center)
        .width('100%')
        .padding({ left: 16, right: 16, top: 12, bottom: 12 })
        .backgroundColor('#FFFFFF')
        .borderRadius(12)
        .shadow({ radius: 4, color: 'rgba(0,0,0,0.08)', offsetY: 2 })

      // 链式调用顺序很重要
      Text('注意链式调用顺序')
        .fontSize(16)
        .width('100%')
        .height(80)
        .backgroundColor('#E8F4FD')
        .borderRadius(8)
        .textAlign(TextAlign.Center)
    }
    .width('100%')
    .padding(16)
  }
}

5.2 全局样式复用

通过 @Styles 和资源文件管理全局样式。

// styles/CommonStyles.ets
// 全局通用样式定义

@Styles
function statusBarHeight() {
  .padding({ top: 'statusBarHeight' })
}

@Styles
function pageBackground() {
  .width('100%')
  .height('100%')
  .backgroundColor('#F5F5F5')
}

@Styles
function whiteCard() {
  .width('100%')
  .padding(16)
  .backgroundColor(Color.White)
  .borderRadius(12)
  .margin({ bottom: 12 })
}

@Styles
function primaryText() {
  .fontSize(16)
  .fontWeight(FontWeight.Medium)
  .fontColor('#1A1A1A')
}

@Styles
function secondaryText() {
  .fontSize(14)
  .fontColor('#666666')
}

@Styles
function captionText() {
  .fontSize(12)
  .fontColor('#999999')
}

// 使用示例
@Entry
@Component
struct StyleReuseDemo {
  build() {
    Column() {
      Column() {
        Text('主标题')
          .primaryText()
        Text('副标题描述')
          .secondaryText()
        Text('辅助说明文字')
          .captionText()
      }
      .whiteCard()

      Column() {
        Text('另一个卡片')
          .primaryText()
        Text('卡片内容')
          .secondaryText()
      }
      .whiteCard()
    }
    .pageBackground()
    .statusBarHeight()
    .padding(16)
  }
}

5.3 深色模式适配

鸿蒙NEXT支持系统级深色模式,需要做好适配。

// DarkModeExample.ets
@Entry
@Component
struct DarkModeExample {
  @StorageProp('currentColorMode') colorMode: number = 0 // 0: 浅色, 1: 深色

  // 根据模式获取颜色
  private getBgColor(): ResourceColor {
    return this.colorMode === 1 ? '#1A1A1A' : '#FFFFFF'
  }

  private getTextColor(): ResourceColor {
    return this.colorMode === 1 ? '#E8E8E8' : '#1A1A1A'
  }

  private getSubTextColor(): ResourceColor {
    return this.colorMode === 1 ? '#AAAAAA' : '#666666'
  }

  private getCardBgColor(): ResourceColor {
    return this.colorMode === 1 ? '#2A2A2A' : '#FFFFFF'
  }

  build() {
    Column({ space: 12 }) {
      // 使用 $r 获取系统资源颜色(推荐方式)
      Text('系统资源颜色适配')
        .fontSize(20)
        .fontColor($r('sys.color.font_primary'))
        .width('100%')
        .padding(16)
        .backgroundColor($r('sys.color.background_primary'))
        .borderRadius(12)

      // 手动适配深色模式
      Text('手动适配深色模式')
        .fontSize(16)
        .fontColor(this.getTextColor())
        .width('100%')
        .padding(16)
        .backgroundColor(this.getCardBgColor())
        .borderRadius(12)

      Text('使用条件渲染适配不同主题')
        .fontSize(14)
        .fontColor(this.getSubTextColor())
        .padding({ left: 16 })
    }
    .width('100%')
    .height('100%')
    .backgroundColor(this.getBgColor())
    .padding(16)
  }
}

最佳实践:

  • 优先使用 $r('sys.color.xxx') 系统资源颜色

  • 避免硬编码颜色值,使用主题变量

  • 图片资源提供深色模式版本($r('app.media.ic_icon_dark')


六、动画效果实现

6.1 属性动画

通过 animation 属性为状态变化添加动画效果。

// AnimationExample.ets
@Entry
@Component
struct AnimationExample {
  @State isExpanded: boolean = false
  @State rotateAngle: number = 0
  @State opacityValue: number = 1
  @State scaleValue: number = 1

  build() {
    Column({ space: 24 }) {
      // 1. 尺寸动画
      Column() {
        Text('点击展开/收起')
          .fontSize(16)
          .fontColor(Color.White)
          .textAlign(TextAlign.Center)
      }
      .width(this.isExpanded ? '100%' : '50%')
      .height(this.isExpanded ? 200 : 80)
      .backgroundColor('#007DFF')
      .borderRadius(this.isExpanded ? 16 : 8)
      .animation({
        duration: 300,
        curve: Curve.EaseInOut
      })
      .onClick(() => {
        this.isExpanded = !this.isExpanded
      })

      // 2. 旋转动画
      Image($r('app.media.ic_refresh'))
        .width(48)
        .height(48)
        .rotate({ angle: this.rotateAngle })
        .animation({
          duration: 1000,
          curve: Curve.Linear
        })
        .onClick(() => {
          this.rotateAngle += 360
        })

      // 3. 透明度动画
      Text('淡入淡出效果')
        .fontSize(20)
        .opacity(this.opacityValue)
        .animation({
          duration: 500,
          curve: Curve.EaseIn
        })
        .onClick(() => {
          this.opacityValue = this.opacityValue === 1 ? 0 : 1
        })

      // 4. 缩放动画
      Text('缩放动画')
        .fontSize(18)
        .scale({ x: this.scaleValue, y: this.scaleValue })
        .animation({
          duration: 300,
          curve: Curve.FastOutSlowIn
        })
        .onClick(() => {
          this.scaleValue = this.scaleValue === 1 ? 1.2 : 1
        })
    }
    .width('100%')
    .padding(24)
    .alignItems(HorizontalAlign.Center)
  }
}

动画曲线说明:

曲线

说明

适用场景

Curve.Linear

匀速

旋转、进度条

Curve.EaseIn

先慢后快

淡出、收起

Curve.EaseOut

先快后慢

淡入、展开

Curve.EaseInOut

两头慢中间快

通用过渡

Curve.FastOutSlowIn

Material Design标准曲线

大多数动画

Curve.Spring

弹簧效果

有弹性的交互


6.2 显式动画

使用 animateTo 函数精确控制动画触发。

// ExplicitAnimationExample.ets
@Entry
@Component
struct ExplicitAnimationExample {
  @State boxWidth: number = 100
  @State boxHeight: number = 100
  @State boxColor: string = '#007DFF'
  @State borderRadius: number = 0

  build() {
    Column({ space: 32 }) {
      // 动画目标元素
      Column() {
        Text('动画')
          .fontSize(16)
          .fontColor(Color.White)
          .textAlign(TextAlign.Center)
      }
      .width(this.boxWidth)
      .height(this.boxHeight)
      .backgroundColor(this.boxColor)
      .borderRadius(this.borderRadius)

      // 控制按钮
      Column({ space: 12 }) {
        Button('变大')
          .width('80%')
          .onClick(() => {
            animateTo({
              duration: 500,
              curve: Curve.FastOutSlowIn,
              onFinish: () => {
                console.info('动画完成')
              }
            }, () => {
              this.boxWidth = 200
              this.boxHeight = 200
              this.boxColor = '#FF6B35'
              this.borderRadius = 100
            })
          })

        Button('还原')
          .width('80%')
          .onClick(() => {
            animateTo({
              duration: 300,
              curve: Curve.EaseInOut
            }, () => {
              this.boxWidth = 100
              this.boxHeight = 100
              this.boxColor = '#007DFF'
              this.borderRadius = 0
            })
          })
      }
    }
    .width('100%')
    .padding(32)
    .alignItems(HorizontalAlign.Center)
  }
}

6.3 转场动画

组件插入/删除时的过渡动画。

// TransitionExample.ets
@Entry
@Component
struct TransitionExample {
  @State showElement: boolean = true

  build() {
    Column({ space: 24 }) {
      Button(this.showElement ? '隐藏元素' : '显示元素')
        .onClick(() => {
          this.showElement = !this.showElement
        })

      if (this.showElement) {
        // 内置过渡效果
        Text('内置淡入过渡')
          .fontSize(18)
          .padding(24)
          .backgroundColor('#E8F4FD')
          .borderRadius(12)
          .transition(TransitionEffect.OPACITY)  // 淡入淡出

        // 自定义过渡效果
        Column() {
          Text('自定义滑入过渡')
            .fontSize(18)
            .fontColor(Color.White)
        }
        .padding(24)
        .backgroundColor('#007DFF')
        .borderRadius(12)
        .transition(
          TransitionEffect.asymmetric({
            appear: TransitionEffect.move({ x: -200 }).animation({ duration: 500 }),
            disappear: TransitionEffect.move({ x: 200 }).animation({ duration: 500 })
          })
        )

        // 组合过渡效果
        Text('组合动画过渡')
          .fontSize(18)
          .padding(24)
          .backgroundColor('#52C41A')
          .borderRadius(12)
          .fontColor(Color.White)
          .transition(
            TransitionEffect.combine(
              TransitionEffect.OPACITY.animation({ duration: 300 }),
              TransitionEffect.scale({ x: 0.5, y: 0.5 }).animation({ duration: 400 }),
              TransitionEffect.rotate({ angle: 15 }).animation({ duration: 500 })
            )
          )
      }
    }
    .width('100%')
    .padding(24)
    .alignItems(HorizontalAlign.Center)
  }
}

6.4 路径动画与粒子动画

鸿蒙NEXT还支持更高级的动画效果。

// PathAnimationExample.ets
@Entry
@Component
struct PathAnimationExample {
  @State pathProgress: number = 0

  build() {
    Column({ space: 32 }) {
      // 沿路径动画
      Stack() {
        // 路径轨迹(可视化)
        Path()
          .width(300)
          .height(200)
          .commands('M 20 100 C 80 20, 160 180, 280 100')
          .stroke('#E8E8E8')
          .strokeWidth(2)
          .fill('none')

        // 运动的元素
        Circle({ width: 20, height: 20 })
          .fill('#007DFF')
          .offset({
            x: this.pathProgress * 260 + 10,
            y: Math.sin(this.pathProgress * Math.PI) * 80 + 90
          })
          .animation({
            duration: 2000,
            curve: Curve.Linear,
            iterations: -1   // 无限循环
          })
      }
      .width(300)
      .height(200)

      Button('开始路径动画')
        .onClick(() => {
          this.pathProgress = 1
        })

      // 加载动画示例
      Row({ space: 4 }) {
        ForEach([0, 1, 2], (index: number) => {
          Circle({ width: 12, height: 12 })
            .fill('#007DFF')
            .opacity(0.3)
            .scale({ x: 0.8, y: 0.8 })
            .animation({
              duration: 600,
              delay: index * 200,
              iterations: -1,
              curve: Curve.EaseInOut
            })
        })
      }
    }
    .width('100%')
    .padding(32)
    .alignItems(HorizontalAlign.Center)
  }
}

七、踩坑记录与最佳实践

踩坑1:ForEach的keyGenerator缺失导致列表渲染异常

问题描述:使用 ForEach 渲染列表时,如果不提供第三个参数(keyGenerator),当数据发生变化时可能导致列表项复用错误。

错误写法

ForEach(this.list, (item: string) => {
  ListItem() {
    Text(item)
  }
  // 缺少 keyGenerator
})

正确写法

ForEach(this.list, (item: string, index: number) => {
  ListItem() {
    Text(item)
  }
}, (item: string, index: number) => `${item}_${index}`)  // 提供唯一key

踩坑2:animation属性位置影响动画效果

问题描述animation 属性必须放在要动画的属性之后,否则动画不会生效。

错误写法

Text('Hello')
  .animation({ duration: 300 })  // 放在前面,不生效
  .fontSize(this.isLarge ? 24 : 16)

正确写法

Text('Hello')
  .fontSize(this.isLarge ? 24 : 16)  // 要动画的属性在前
  .animation({ duration: 300 })      // animation在后

踩坑3:组件id在RelativeContainer中必须唯一

问题描述:在 RelativeContainer 中,子组件的 id 必须唯一,否则布局会混乱。

解决方案:使用常量定义id,避免字符串重复。

// 定义id常量
private static readonly ID_HEADER = 'header'
private static readonly ID_CONTENT = 'content'
private static readonly ID_FOOTER = 'footer'

RelativeContainer() {
  Text('Header')
    .id(RelativeContainerExample.ID_HEADER)
    .alignRules({ top: { anchor: '__container__' } })

  Text('Content')
    .id(RelativeContainerExample.ID_CONTENT)
    .alignRules({ top: { anchor: RelativeContainerExample.ID_HEADER } })
}

踩坑4:网络图片需要配置网络权限

问题描述:直接加载网络图片不显示,没有报错。

解决方案:在 module.json5 中配置网络权限。

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ]
  }
}

踩坑5:状态变量初始化时机

问题描述:在组件外部定义的变量无法触发UI刷新。

错误写法

let count = 0  // 普通变量,不触发UI刷新

Button('点击')
  .onClick(() => {
    count++  // UI不会更新
  })

正确写法

@State count: number = 0  // 状态变量,触发UI刷新

Button('点击')
  .onClick(() => {
    this.count++  // UI会自动更新
  })

踩坑6:Row/Column的layoutWeight使用注意事项

问题描述layoutWeight 会让组件占据剩余空间,但如果父容器没有固定尺寸,可能不生效。

解决方案:确保父容器有明确的尺寸约束。

// 确保Row有宽度约束
Row() {
  Text('左侧')
    .width(80)
  Text('右侧占满')
    .layoutWeight(1)  // 占据剩余空间
}
.width('100%')  // 必须设置宽度

最佳实践总结

实践

说明

组件粒度适中

单个组件不超过200行,过长则拆分

状态最小化

只用必要的 @State,避免过度状态化

样式复用

使用 @Styles@Extend 提取公共样式

列表优化

LazyForEach 替代 ForEach 处理长列表

图片优化

使用合适的 objectFit,配置占位图

动画流畅

动画时长控制在300ms以内,使用合适的曲线


八、总结与下期预告

本文总结

本文系统讲解了鸿蒙NEXT ArkUI组件库的核心知识:

  1. 基础组件:掌握了 TextImageButtonTextInputToggleProgressList 等常用组件的核心属性和使用方式

  2. 布局系统:理解了 ColumnRowStackFlexGridRelativeContainer 六大布局容器的特性和适用场景

  3. 自定义组件:学会了 @Component@Builder@Extend@Styles 的用法,能够开发可复用的业务组件

  4. 样式主题:掌握了内联样式、全局样式复用和深色模式适配方案

  5. 动画效果:实现了属性动画、显式动画、转场动画和路径动画

  6. 踩坑经验:总结了6个常见坑点和最佳实践

关键知识点速查表

知识点

核心要点

Text

fontSizefontWeightfontColormaxLinestextOverflow

Image

objectFit(Cover/Contain/Fill)、borderRadiusshadow

Button

ButtonType(Capsule/Circle/Normal)、stateEffect

Column/Row

spacejustifyContentalignItems

Flex

wrapdirectionjustifyContent

@Builder

可复用的UI片段,避免代码重复

@Extend

扩展原生组件样式

@Styles

通用样式属性提取

animation

放在目标属性之后,配置 durationcurve

下期预告

第3篇:状态管理一文通:@State、@Prop、@Link、@Provide/Consume全解析

状态管理是鸿蒙应用开发的核心难点。下期我们将深入讲解:

  • @State:组件内状态管理的最佳实践

  • @Prop:父子组件单向数据传递的正确姿势

  • @Link:父子组件双向数据绑定的实现原理

  • @Provide/@Consume:跨组件层级数据共享的高级用法

  • 状态管理常见陷阱与性能优化

关注我,第一时间获取更新!


鸿蒙NEXT开发实战系列 -- 全部文章

  1. 鸿蒙NEXT开发从零到一

  2. ArkUI组件库完全指南(本文)

  3. 状态管理一文通

  4. 数据持久化与网络请求全攻略

  5. 性能优化实战指南


本文基于 HarmonyOS NEXT (API 14) 编写,代码示例均已在 DevEco Studio 5.0.5+ 环境下验证通过。如有问题欢迎评论区交流。


标签:鸿蒙NEXT | HarmonyOS NEXT | ArkUI | ArkTS | DevEco Studio | 组件库 | 布局系统 | @Builder | @Extend | @Styles | 自定义组件 | 声明式UI

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐