ArkUI声明式开发范式:构建响应式用户界面

ArkUI是HarmonyOS的现代化UI开发框架,采用声明式编程范式。本文将深入讲解ArkUI的核心概念、语法特性和最佳实践,帮助你构建高性能的响应式用户界面。

一、声明式UI与命令式UI对比

1.1 传统命令式UI开发

// 命令式编程示例 - 需要手动操作DOM
class CommandiveExample {
  private count: number = 0
  private countElement: HTMLElement
  
  constructor() {
    this.countElement = document.getElementById('count')
    document.getElementById('increment').addEventListener('click', () => {
      this.increment()
    })
  }
  
  increment() {
    this.count++
    this.countElement.textContent = this.count.toString()
    // 需要手动更新UI状态
    if (this.count > 5) {
      this.countElement.style.color = 'red'
    }
  }
}

1.2 现代声明式UI开发

// 声明式编程示例 - 数据驱动UI
@Component
struct DeclarativeExample {
  @State count: number = 0

  build() {
    Column() {
      // UI自动响应数据变化
      Text(this.count.toString())
        .fontColor(this.count > 5 ? Color.Red : Color.Black)
      
      Button('增加')
        .onClick(() => {
          this.count++ // 只需要更新数据,UI自动更新
        })
    }
  }
}

二、ArkUI核心装饰器详解

2.1 状态管理装饰器

@Component
struct StateManagementExample {
  // @State - 组件内部状态
  @State message: string = 'Hello ArkUI'
  @State count: number = 0
  @State isActive: boolean = true
  @State userList: string[] = ['用户1', '用户2']
  @State userInfo: object = { name: '张三', age: 25 }

  // @Prop - 从父组件单向同步
  @Prop propData: string = ''

  // @Link - 与父组件双向同步
  @Link @Watch('onLinkChange') linkData: number
  
  // @Provide/@Consume - 跨组件层级状态共享
  @Provide themeColor: string = '#007DFF'
  
  // 状态变化监听
  onLinkChange(): void {
    console.log('Link数据发生变化:', this.linkData)
  }

  build() {
    Column({ space: 20 }) {
      Text('状态管理示例')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      
      // 状态使用示例
      Text(`消息: ${this.message}`)
      Text(`计数: ${this.count}`)
      Text(`活跃状态: ${this.isActive}`)
      Text(`Prop数据: ${this.propData}`)
      Text(`Link数据: ${this.linkData}`)
      
      Button('更新状态')
        .onClick(() => {
          this.updateAllStates()
        })
    }
    .width('100%')
    .padding(20)
  }

  updateAllStates() {
    // 更新各种状态
    this.message = '状态已更新'
    this.count += 1
    this.isActive = !this.isActive
    this.userList.push(`用户${this.count}`)
    this.userInfo = { ...this.userInfo, age: this.userInfo.age + 1 }
  }
}

2.2 生命周期装饰器

@Component
struct LifecycleExample {
  @State timer: number = 0
  
  // 组件即将创建时调用
  aboutToAppear() {
    console.log('组件即将出现在视图中')
    this.startTimer()
  }
  
  // 组件即将销毁时调用
  aboutToDisappear() {
    console.log('组件即将从视图中消失')
    this.stopTimer()
  }
  
  // 组件更新前调用
  aboutToUpdate() {
    console.log('组件状态即将更新')
  }
  
  // 组件更新后调用
  onPageShow() {
    console.log('页面显示')
  }
  
  // 页面隐藏时调用
  onPageHide() {
    console.log('页面隐藏')
  }

  private timerId: number = 0
  
  startTimer() {
    this.timerId = setInterval(() => {
      this.timer++
    }, 1000)
  }
  
  stopTimer() {
    if (this.timerId) {
      clearInterval(this.timerId)
      this.timerId = 0
    }
  }

  build() {
    Column() {
      Text(`计时器: ${this.timer}秒`)
        .fontSize(24)
      
      Button('重置计时器')
        .onClick(() => {
          this.timer = 0
        })
    }
  }
}

三、布局系统与容器组件

3.1 常用布局容器

@Component
struct LayoutExample {
  @State currentLayout: string = 'column'

  build() {
    Column() {
      // 布局选择器
      Row({ space: 10 }) {
        Button('Column布局').onClick(() => { this.currentLayout = 'column' })
        Button('Row布局').onClick(() => { this.currentLayout = 'row' })
        Button('Stack布局').onClick(() => { this.currentLayout = 'stack' })
        Button('Flex布局').onClick(() => { this.currentLayout = 'flex' })
      }

      // 动态布局显示
      if (this.currentLayout === 'column') {
        this.buildColumnLayout()
      } else if (this.currentLayout === 'row') {
        this.buildRowLayout()
      } else if (this.currentLayout === 'stack') {
        this.buildStackLayout()
      } else {
        this.buildFlexLayout()
      }
    }
  }

  // Column布局 - 垂直排列
  @Builder
  buildColumnLayout() {
    Column({ space: 10 }) {
      Text('Column布局 - 垂直排列')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)

      ForEach([1, 2, 3, 4], (item: number) => {
        Text(`项目 ${item}`)
          .width('90%')
          .height(60)
          .backgroundColor('#E0F7FA')
          .textAlign(TextAlign.Center)
          .borderRadius(8)
      })
    }
    .width('100%')
    .padding(10)
    .backgroundColor('#F5F5F5')
  }

  // Row布局 - 水平排列
  @Builder
  buildRowLayout() {
    Column() {
      Text('Row布局 - 水平排列')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 10 })

      Row({ space: 10 }) {
        ForEach([1, 2, 3, 4], (item: number) => {
          Text(`项目 ${item}`)
            .height(60)
            .layoutWeight(1) // 等分宽度
            .backgroundColor('#E8F5E8')
            .textAlign(TextAlign.Center)
            .borderRadius(8)
        })
      }
      .width('100%')
      .height(80)
    }
    .width('100%')
    .padding(10)
    .backgroundColor('#F5F5F5')
  }

  // Stack布局 - 层叠排列
  @Builder
  buildStackLayout() {
    Column() {
      Text('Stack布局 - 层叠排列')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 10 })

      Stack({ alignContent: Alignment.BottomEnd }) {
        // 底层
        Text('底层内容')
          .width(200)
          .height(150)
          .backgroundColor('#FFEBEE')
          .textAlign(TextAlign.Center)

        // 中层
        Text('中层内容')
          .width(150)
          .height(100)
          .backgroundColor('#E3F2FD')
          .textAlign(TextAlign.Center)

        // 顶层
        Text('顶层内容')
          .width(100)
          .height(50)
          .backgroundColor('#E8F5E8')
          .textAlign(TextAlign.Center)
      }
      .width('100%')
      .height(200)
    }
    .width('100%')
    .padding(10)
    .backgroundColor('#F5F5F5')
  }

  // Flex布局 - 弹性布局
  @Builder
  buildFlexLayout() {
    Column() {
      Text('Flex布局 - 弹性布局')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 10 })

      Flex({ direction: FlexDirection.Row, wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceAround }) {
        ForEach([1, 2, 3, 4, 5, 6], (item: number) => {
          Text(`Flex ${item}`)
            .width(80)
            .height(80)
            .backgroundColor(item % 2 === 0 ? '#FFECB3' : '#D1C4E9')
            .textAlign(TextAlign.Center)
            .margin(5)
        })
      }
      .width('100%')
    }
    .width('100%')
    .padding(10)
    .backgroundColor('#F5F5F5')
  }
}

3.2 响应式布局技巧

@Component
struct ResponsiveLayout {
  @State screenWidth: number = 360
  @State isLandscape: boolean = false

  // 根据屏幕尺寸计算布局参数
  get isSmallScreen(): boolean {
    return this.screenWidth < 400
  }

  get columnCount(): number {
    return this.isSmallScreen ? 2 : 4
  }

  get itemSize(): number {
    return this.isSmallScreen ? 80 : 100
  }

  build() {
    Column() {
      // 布局信息显示
      Text(`屏幕宽度: ${this.screenWidth}`)
      Text(`横屏模式: ${this.isLandscape}`)
      Text(`列数: ${this.columnCount}`)

      // 响应式网格布局
      Grid() {
        ForEach([1, 2, 3, 4, 5, 6, 7, 8], (item: number) => {
          GridItem() {
            Text(`项目 ${item}`)
              .width(this.itemSize)
              .height(this.itemSize)
              .backgroundColor(this.getItemColor(item))
              .textAlign(TextAlign.Center)
              .fontColor(Color.White)
              .fontSize(this.isSmallScreen ? 14 : 16)
          }
        })
      }
      .columnsTemplate('1fr '.repeat(this.columnCount))
      .rowsTemplate('1fr 1fr')
      .columnsGap(10)
      .rowsGap(10)
      .width('100%')
      .height(300)
      .margin({ top: 20 })
    }
    .width('100%')
    .padding(20)
    .onAreaChange((oldValue, newValue) => {
      // 监听区域变化,实现响应式
      this.screenWidth = newValue.width as number
      this.isLandscape = newValue.width as number > newValue.height as number
    })
  }

  getItemColor(index: number): string {
    const colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FECA57', '#FF9FF3', '#54A0FF', '#5F27CD']
    return colors[index % colors.length]
  }
}

四、组件化开发与@Builder

4.1 @Builder基础用法

@Component
struct BuilderExample {
  @State userData: object = {
    name: '张三',
    age: 28,
    avatar: 'user_avatar.png',
    level: 'VIP'
  }

  build() {
    Column({ space: 15 }) {
      // 使用@Builder构建可复用组件
      this.UserCard(this.userData)
      
      this.StatisticsCard({
        title: '学习进度',
        value: '75%',
        icon: 'progress_icon.png'
      })
      
      this.ActionButtons()
    }
    .width('100%')
    .padding(20)
  }

  // @Builder函数 - 用户卡片
  @Builder
  UserCard(userInfo: object) {
    Row({ space: 15 }) {
      // 头像
      Image(userInfo.avatar)
        .width(60)
        .height(60)
        .borderRadius(30)
        .backgroundColor('#E0E0E0')
      
      // 用户信息
      Column({ space: 5 }) {
        Text(userInfo.name)
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
        
        Text(`年龄: ${userInfo.age} | 等级: ${userInfo.level}`)
          .fontSize(14)
          .fontColor('#666')
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)
    }
    .width('100%')
    .padding(15)
    .backgroundColor(Color.White)
    .borderRadius(12)
    .shadow({ radius: 8, color: '#1A000000', offsetX: 2, offsetY: 2 })
  }

  // @Builder函数 - 统计卡片
  @Builder
  StatisticsCard(config: object) {
    Column({ space: 10 }) {
      Row() {
        Text(config.title)
          .fontSize(16)
          .fontColor('#666')
          .layoutWeight(1)
        
        Image(config.icon)
          .width(20)
          .height(20)
      }
      
      Text(config.value)
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .fontColor('#007DFF')
    }
    .width('100%')
    .padding(15)
    .backgroundColor(Color.White)
    .borderRadius(12)
    .shadow({ radius: 4, color: '#1A000000', offsetX: 1, offsetY: 1 })
  }

  // @Builder函数 - 操作按钮组
  @Builder
  ActionButtons() {
    Row({ space: 10 }) {
      Button('编辑资料')
        .layoutWeight(1)
        .backgroundColor('#007DFF')
        .fontColor(Color.White)
      
      Button('消息')
        .layoutWeight(1)
        .backgroundColor('#34C759')
        .fontColor(Color.White)
      
      Button('设置')
        .layoutWeight(1)
        .backgroundColor('#8E8E93')
        .fontColor(Color.White)
    }
    .width('100%')
    .height(50)
  }
}

4.2 高级@Builder用法

@Component
struct AdvancedBuilderExample {
  @State itemList: any[] = [
    { id: 1, name: '项目A', type: 'important', progress: 80 },
    { id: 2, name: '项目B', type: 'normal', progress: 45 },
    { id: 3, name: '项目C', type: 'urgent', progress: 95 }
  ]

  build() {
    List({ space: 10 }) {
      ForEach(this.itemList, (item: any) => {
        ListItem() {
          this.ListItemBuilder(item)
        }
      }, (item: any) => item.id.toString())
    }
    .width('100%')
    .height('100%')
  }

  // 带参数的条件构建器
  @Builder
  ListItemBuilder(item: any) {
    Row() {
      // 类型图标
      this.TypeIcon(item.type)
      
      // 内容区域
      Column({ space: 5 }) {
        Text(item.name)
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
        
        this.ProgressBar(item.progress)
      }
      .layoutWeight(1)
      .alignItems(HorizontalAlign.Start)
      
      // 操作按钮
      this.ActionButtons(item)
    }
    .width('100%')
    .padding(15)
    .backgroundColor(this.getItemBackground(item.type))
    .borderRadius(8)
  }

  // 类型图标构建器
  @Builder
  TypeIcon(type: string) {
    let iconColor: string = '#666'
    let iconName: string = 'normal_icon'
    
    switch (type) {
      case 'important':
        iconColor = '#FF9500'
        iconName = 'important_icon'
        break
      case 'urgent':
        iconColor = '#FF3B30'
        iconName = 'urgent_icon'
        break
    }
    
    Image(iconName)
      .width(24)
      .height(24)
      .margin({ right: 10 })
      .tintColor(iconColor)
  }

  // 进度条构建器
  @Builder
  ProgressBar(progress: number) {
    Column() {
      // 进度文本
      Row() {
        Text('进度')
          .fontSize(12)
          .fontColor('#666')
        
        Text(`${progress}%`)
          .fontSize(12)
          .fontColor('#007DFF')
          .margin({ left: 5 })
      }
      
      // 进度条
      Stack() {
        // 背景条
        Row()
          .width('100%')
          .height(4)
          .backgroundColor('#E5E5EA')
          .borderRadius(2)
        
        // 进度条
        Row()
          .width(`${progress}%`)
          .height(4)
          .backgroundColor(this.getProgressColor(progress))
          .borderRadius(2)
      }
      .width('100%')
      .height(4)
      .margin({ top: 5 })
    }
    .width('100%')
  }

  // 操作按钮构建器
  @Builder
  ActionButtons(item: any) {
    Row({ space: 5 }) {
      Button('编辑')
        .width(60)
        .height(30)
        .fontSize(12)
        .backgroundColor('#007DFF')
        .fontColor(Color.White)
      
      Button('删除')
        .width(60)
        .height(30)
        .fontSize(12)
        .backgroundColor('#FF3B30')
        .fontColor(Color.White)
    }
  }

  getItemBackground(type: string): string {
    switch (type) {
      case 'important': return '#FFF4E6'
      case 'urgent': return '#FFE6E6'
      default: return '#F2F2F7'
    }
  }

  getProgressColor(progress: number): string {
    if (progress >= 80) return '#34C759'
    if (progress >= 50) return '#FF9500'
    return '#FF3B30'
  }
}

五、动画与交互效果

5.1 属性动画

@Component
struct AnimationExample {
  @State scale: number = 1.0
  @State opacity: number = 1.0
  @State rotation: number = 0
  @State translateX: number = 0
  @State isAnimating: boolean = false

  build() {
    Column({ space: 20 }) {
      // 动画展示区域
      Stack() {
        // 动画目标元素
        Row() {
          Text('动画示例')
            .fontSize(20)
            .fontColor(Color.White)
        }
        .width(150)
        .height(150)
        .backgroundColor('#007DFF')
        .scale({ x: this.scale, y: this.scale })
        .opacity(this.opacity)
        .rotate({ angle: this.rotation })
        .translate({ x: this.translateX, y: 0 })
        .borderRadius(12)
      }
      .width('100%')
      .height(200)
      .justifyContent(FlexAlign.Center)

      // 动画控制按钮
      Button(this.isAnimating ? '停止动画' : '开始动画')
        .onClick(() => {
          this.toggleAnimation()
        })
        .width(200)

      // 单独动画控制
      Row({ space: 10 }) {
        Button('缩放')
          .onClick(() => { this.animateScale() })
          .layoutWeight(1)
        
        Button('透明度')
          .onClick(() => { this.animateOpacity() })
          .layoutWeight(1)
        
        Button('旋转')
          .onClick(() => { this.animateRotation() })
          .layoutWeight(1)
      }
      .width('100%')
    }
    .width('100%')
    .padding(20)
  }

  toggleAnimation() {
    if (this.isAnimating) {
      this.stopAnimation()
    } else {
      this.startComplexAnimation()
    }
    this.isAnimating = !this.isAnimating
  }

  startComplexAnimation() {
    // 复杂组合动画
    animateTo({
      duration: 2000,
      tempo: 1.0,
      curve: Curve.EaseInOut,
      iterations: -1, // 无限循环
      playMode: PlayMode.Alternate // 往返播放
    }, () => {
      this.scale = 1.5
      this.opacity = 0.5
      this.rotation = 180
      this.translateX = 100
    })
  }

  stopAnimation() {
    // 重置动画参数
    animateTo({
      duration: 500
    }, () => {
      this.scale = 1.0
      this.opacity = 1.0
      this.rotation = 0
      this.translateX = 0
    })
  }

  animateScale() {
    animateTo({
      duration: 1000,
      curve: Curve.Spring
    }, () => {
      this.scale = this.scale === 1.0 ? 1.5 : 1.0
    })
  }

  animateOpacity() {
    animateTo({
      duration: 800,
      curve: Curve.Ease
    }, () => {
      this.opacity = this.opacity === 1.0 ? 0.3 : 1.0
    })
  }

  animateRotation() {
    animateTo({
      duration: 1200,
      curve: Curve.Linear
    }, () => {
      this.rotation += 180
    })
  }
}

六、最佳实践与性能优化

6.1 性能优化技巧

@Component
struct PerformanceOptimizedExample {
  @State dataList: any[] = this.generateLargeData()
  @State searchText: string = ''
  @State filterType: string = 'all'

  // 计算属性 - 避免不必要的重新计算
  get filteredData(): any[] {
    console.log('过滤数据...')
    return this.dataList.filter(item => {
      const matchesSearch = item.name.toLowerCase().includes(this.searchText.toLowerCase())
      const matchesFilter = this.filterType === 'all' || item.type === this.filterType
      return matchesSearch && matchesFilter
    })
  }

  build() {
    Column() {
      // 搜索和过滤控件
      this.buildControls()
      
      // 优化列表渲染
      LazyForEach(new DataSource(this.filteredData), (item: any) => {
        ListItem() {
          this.OptimizedListItem(item)
        }
      }, (item: any) => item.id.toString())
    }
  }

  @Builder
  buildControls() {
    Column({ space: 10 }) {
      // 搜索框
      TextInput({ placeholder: '搜索...' })
        .onChange((value: string) => {
          this.searchText = value
        })
        .width('100%')
      
      // 过滤按钮
      Row({ space: 5 }) {
        ForEach(['all', 'type1', 'type2', 'type3'], (type: string) => {
          Button(type === 'all' ? '全部' : type)
            .stateEffect(this.filterType === type)
            .onClick(() => { this.filterType = type })
            .layoutWeight(1)
        })
      }
    }
    .padding(10)
    .backgroundColor(Color.White)
  }

  // 使用@Builder优化列表项渲染
  @Builder
  OptimizedListItem(item: any) {
    Row() {
      // 使用条件渲染避免不必要的节点
      if (item.avatar) {
        Image(item.avatar)
          .width(40)
          .height(40)
          .margin({ right: 10 })
      }
      
      Column({ space: 5 }) {
        Text(item.name)
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
        
        Text(item.description)
          .fontSize(12)
          .fontColor('#666')
          .maxLines(1)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
      }
      .layoutWeight(1)
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .padding(10)
  }

  // 生成测试数据
  generateLargeData(): any[] {
    const data = []
    for (let i = 0; i < 1000; i++) {
      data.push({
        id: i,
        name: `项目 ${i}`,
        type: `type${i % 3 + 1}`,
        description: `这是第 ${i} 个项目的描述信息`,
        avatar: i % 5 === 0 ? 'avatar.png' : undefined
      })
    }
    return data
  }
}

// 数据源类
class DataSource implements IDataSource {
  private data: any[]
  
  constructor(data: any[]) {
    this.data = data
  }
  
  totalCount(): number {
    return this.data.length
  }
  
  getData(index: number): any {
    return this.data[index]
  }
  
  registerDataChangeListener(listener: DataChangeListener): void {
    // 注册数据变化监听
  }
  
  unregisterDataChangeListener(listener: DataChangeListener): void {
    // 取消注册
  }
}

总结

通过本文的学习,你应该已经掌握了ArkUI声明式开发范式的核心概念:

  1. 声明式语法:数据驱动UI,简化开发流程
  2. 状态管理:多种装饰器满足不同场景的状态管理需求
  3. 布局系统:灵活的容器组件和响应式布局技巧
  4. 组件化开发:使用@Builder构建可复用组件
  5. 动画交互:丰富的动画效果和流畅的用户体验
  6. 性能优化:最佳实践确保应用高性能运行

声明式开发范式是现代化UI开发的趋势,掌握ArkUI将帮助你构建更加优雅、高效的HarmonyOS应用。

需要参加鸿蒙认证的请点击 鸿蒙认证链接

posted @ 2025-10-30 10:54  ifeng918  阅读(11)  评论(0)    收藏  举报