在这里插入图片描述


项目概述

传统移动端常依赖手动刷新与繁琐的状态管理来驱动 UI 更新;而在 HarmonyOS 的 ArkTS 框架中,ArkUI 以“声明式 UI + 响应式数据绑定”重塑开发范式,显著简化界面构建与状态同步。本文以「任务管理」为例,完整实现增删改查、状态切换与筛选过滤等能力,展示从页面到逻辑的端到端实践。

特性 传统开发 ArkTS(ArkUI)
UI 构建 XML/HTML 模板 声明式 UI(直接描述界面结构)
状态管理 手动刷新视图 @State 自动刷新 UI
列表渲染 RecyclerView/ListView + Adapter List + ForEach 声明式渲染
数据更新 notifyDataSetChanged() 手动通知 数组操作自动触发 UI 更新
布局方式 多层嵌套布局 Column/Row/List 容器简洁组织
事件处理 回调监听器 直接绑定函数,链式调用
性能优化 手动 ViewHolder 复用 自动虚拟化与回收机制
代码结构 多文件耦合 单 ETS 文件即可实现复杂功能

实现效果

架构设计与实现过程

数据模型定义

通过 interface TaskItem 定义任务数据结构:

interface TaskItem {
  id: number
  title: string
  completed: boolean
  createTime: string
}
  • id:唯一标识任务
  • title:任务标题
  • completed:任务完成状态
  • createTime:任务创建时间

通过 @State 响应式管理核心状态:

  • tasks:任务数组
  • newTaskTitle:新任务输入内容
  • filterType:筛选类型(全部/待办/已完成)

UI 构建逻辑

采用声明式 UI,通过 Column 组件组织布局:

Column({ space: 20 }) {
  this.buildHeader()
  this.buildAddTaskSection()
  this.buildFilterTabs()
  this.buildTaskList()
}

各区域模块化构建:

  • buildHeader():标题与统计信息
  • buildAddTaskSection():输入与添加任务
  • buildFilterTabs():任务筛选标签栏
  • buildTaskList():任务列表展示区

任务操作逻辑

添加任务:通过时间戳生成唯一 ID,将新任务添加至 tasks 数组。

addTask() {
  const newTask: TaskItem = {
    id: Date.now(),
    title: this.newTaskTitle.trim(),
    completed: false,
    createTime: new Date().toISOString().split('T')[0]
  }
  this.tasks.push(newTask)
  this.newTaskTitle = ''
}

状态切换:点击任务后切换 completed 状态,实现待办/已完成切换。

toggleTaskStatus(taskId: number) {
  const taskIndex = this.tasks.findIndex(task => task.id === taskId)
  if (taskIndex !== -1) {
    this.tasks[taskIndex].completed = !this.tasks[taskIndex].completed
  }
}

删除任务:通过 filter 操作删除指定任务,数据更新后 UI 自动同步。

deleteTask(taskId: number) {
  this.tasks = this.tasks.filter(task => task.id !== taskId)
}

筛选与过滤逻辑

通过 getFilteredTasks() 方法按 filterType 返回不同任务列表,结合条件渲染动态展示对应任务集合。
● all:返回所有任务
● pending:仅返回未完成任务
● completed:仅返回已完成任务

getFilteredTasks(): TaskItem[] {
  switch (this.filterType) {
    case 'pending':
      return this.tasks.filter(task => !task.completed)
    case 'completed':
      return this.tasks.filter(task => task.completed)
    default:
      return this.tasks
  }
}

动态样式与交互反馈

样式切换

高亮当前筛选状态,区分已完成与未完成任务。

.fontColor(this.filterType === type ? '#FFFFFF' : '#666666')
.backgroundColor(task.completed ? '#4CAF50' : '#FFFFFF')

删除线效果

.decoration({
  type: task.completed ? TextDecorationType.LineThrough : TextDecorationType.None
})

交互逻辑

  • onClick:绑定按钮点击事件
  • onChange:监听输入框变化
  • onSubmit:键盘回车提交任务
  • .enabled():控制按钮可用性,防止空提交

ArkUI List 核心技术解析

声明式渲染机制

ArkUI 的 List 组件采用声明式编程模型。开发者只需描述“UI 应该是什么样子”,无需手动操作 DOM。

  • 数据驱动渲染@State 数据更新 → ForEach 自动刷新 UI
  • 虚拟化性能优化:只渲染可视区域内列表项,滚动流畅
List({ space: 8 }) {
  ForEach(this.tasks, (task: TaskItem) => {
    ListItem() {
      // 列表项内容
    }
  }, (task: TaskItem) => task.id.toString())
}

响应式状态管理

@State 自动监听数组和对象的变化,无需手动刷新:

  1. 用户点击“添加”
  2. addTask()this.tasks.push(newTask)
  3. ArkUI 检测到变化
  4. 自动刷新 ForEach 区域
  5. UI 实时更新

组件化与复用

通过 @Builder 封装复杂 UI:

@Builder
buildTaskItem(task: TaskItem, index: number) {
  // 列表项布局逻辑
}
  • 提升代码可读性
  • 降低耦合,便于维护
  • 有利于性能优化

完整实现代码

interface TaskItem {
  id: number
  title: string
  completed: boolean
  createTime: string
}

@Entry
@Component
struct Index {
  @State tasks: TaskItem[] = [
    { id: 1, title: '学习 ArkUI List 组件', completed: false, createTime: '2024-10-15' },
    { id: 2, title: '编写技术博客', completed: true, createTime: '2024-10-14' },
    { id: 3, title: '优化应用性能', completed: false, createTime: '2024-10-13' }
  ]
  @State newTaskTitle: string = ''
  @State filterType: string = 'all'

  build() {
    Column({ space: 20 }) {
      // 头部区域
      this.buildHeader()

      // 添加任务区域
      this.buildAddTaskSection()

      // 筛选标签
      this.buildFilterTabs()

      // 任务列表
      this.buildTaskList()
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F8F9FA')
    .padding({ top: 50 })
  }

  @Builder
  buildHeader() {
    Row() {
      Text('我的任务清单')
        .fontSize(28)
        .fontWeight(FontWeight.Bold)
        .fontColor('#1976D2')

      Blank()

      Text(`${this.getCompletedCount()}/${this.tasks.length}`)
        .fontSize(16)
        .fontColor('#666666')
        .backgroundColor('#E8F5E8')
        .padding({ left: 12, right: 12, top: 6, bottom: 6 })
        .borderRadius(12)
    }
    .width('90%')
    .margin({ bottom: 10 })
  }

  @Builder
  buildAddTaskSection() {
    Row({ space: 10 }) {
      TextInput({ placeholder: '输入新任务', text: this.newTaskTitle })
        .flexGrow(1)
        .onChange((value) => {
          this.newTaskTitle = value
        })
        .onSubmit(() => this.addTask())

      Button('添加')
        .backgroundColor('#4CAF50')
        .onClick(() => this.addTask())
        .enabled(this.newTaskTitle.trim().length > 0)
    }
    .width('90%')
    .margin({ bottom: 10 })
  }

  @Builder
  buildFilterTabs() {
    Row({ space: 12 }) {
      this.buildFilterTab('all', '全部')
      this.buildFilterTab('pending', '待完成')
      this.buildFilterTab('completed', '已完成')
    }
    .width('90%')
    .justifyContent(FlexAlign.Center)
    .margin({ bottom: 10 })
  }

  @Builder
  buildFilterTab(type: string, label: string) {
    Text(label)
      .fontSize(14)
      .fontColor(this.filterType === type ? '#FFFFFF' : '#666666')
      .backgroundColor(this.filterType === type ? '#007AFF' : '#F0F0F0')
      .padding({ left: 16, right: 16, top: 8, bottom: 8 })
      .borderRadius(16)
      .onClick(() => this.filterType = type)
  }

  @Builder
  buildTaskList() {
    List({ space: 8 }) {
      ForEach(this.getFilteredTasks(), (task: TaskItem, index: number) => {
        ListItem() {
          this.buildTaskItem(task, index)
        }
        .backgroundColor('#FFFFFF')
        .borderRadius(12)
        .padding({ left: 16, right: 16, top: 12, bottom: 12 })
        .shadow({ radius: 2, color: '#E0E0E0' })
      }, (task: TaskItem) => task.id.toString())
    }
    .width('90%')
    .layoutWeight(1)
    .scrollBar(BarState.Auto)
    .divider({
      strokeWidth: 1,
      color: '#F0F0F0',
      startMargin: 16,
      endMargin: 16
    })
  }

  @Builder
  buildTaskItem(task: TaskItem, index: number) {
    Row({ space: 12 }) {
      // 完成状态指示器(用容器画一个圆点,避免 Circle.fill/stroke 签名不匹配)
      // 已完成:实心绿色圆;未完成:白底灰边圆
      Stack() {
        // 空内容,只用外观
      }
      .width(20)
      .height(20)
      .borderRadius(10)
      .backgroundColor(task.completed ? '#4CAF50' : '#FFFFFF')
      .border({ width: 2, color: task.completed ? '#4CAF50' : '#CCCCCC' })
      .onClick(() => this.toggleTaskStatus(task.id))

      // 任务内容
      Column({ space: 4 }) {
        Text(task.title)
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
          .fontColor(task.completed ? '#999999' : '#1A1A1A')
          .decoration({
            type: task.completed ? TextDecorationType.LineThrough : TextDecorationType.None
          })
          .maxLines(2)
          //新签名:传对象
          .textOverflow({ overflow: TextOverflow.Ellipsis })

        Text(task.createTime)
          .fontSize(12)
          .fontColor('#999999')
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)

      // 删除按钮
      Button('删除')
        .fontSize(12)
        .fontColor('#FFFFFF')
        .backgroundColor('#FF3B30')
        .borderRadius(6)
        .padding({ left: 12, right: 12, top: 6, bottom: 6 })
        .onClick(() => this.deleteTask(task.id))
    }
    .width('100%')
    .alignItems(VerticalAlign.Center)
  }

  // 数据操作方法
  addTask() {
    if (this.newTaskTitle.trim().length > 0) {
      const newTask: TaskItem = {
        id: Date.now(),
        title: this.newTaskTitle.trim(),
        completed: false,
        createTime: new Date().toISOString().split('T')[0]
      }
      this.tasks.push(newTask)
      this.newTaskTitle = ''
    }
  }

  toggleTaskStatus(taskId: number) {
    const taskIndex = this.tasks.findIndex(task => task.id === taskId)
    if (taskIndex !== -1) {
      this.tasks[taskIndex].completed = !this.tasks[taskIndex].completed
    }
  }

  deleteTask(taskId: number) {
    this.tasks = this.tasks.filter(task => task.id !== taskId)
  }

  // 辅助方法
  getFilteredTasks(): TaskItem[] {
    switch (this.filterType) {
      case 'pending':
      case 'pending':
        return this.tasks.filter(task => !task.completed)
      case 'completed':
        return this.tasks.filter(task => task.completed)
      default:
        return this.tasks
    }
  }

  getCompletedCount(): number {
    return this.tasks.filter(task => task.completed).length
  }
}

总结

本文以一个任务管理小应用为例,展示了 HarmonyOS 上 ArkTS 与 ArkUI 的实战效果。声明式 UI 搭配响应式状态,让界面更新更省心;@State 自动触发渲染,配合列表虚拟化保证大数据量下的流畅;用 @Builder 做模块化,代码更清爽、易复用。 做小项目能快起来,做大项目也能稳住——在 HarmonyOS 上,“声明式 + 响应式”会是更省心的路子。

在这里插入图片描述

Logo

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

更多推荐