在这里插入图片描述

在这里插入图片描述

1. 应用概述

购物清单(ShoppingList)是一款基于HarmonyOS ArkTS框架开发的日常生活工具,其核心功能是帮助用户记录购物清单,支持添加物品、标记完成、删除物品等操作。这款应用是五个应用中最复杂的一个,引入了List列表组件、ForEach循环渲染、Checkbox复选框、数组状态管理以及删除操作等进阶特性。

从技术实现角度来看,购物清单应用充分利用了ArkUI框架的列表渲染能力。通过@State装饰器管理购物物品数组,通过ForEach组件实现列表渲染,通过Checkbox组件实现完成状态切换,通过splice方法实现删除操作。应用的代码结构清晰地展示了列表类应用的核心开发模式。

本技术博客将从应用架构设计、核心代码实现、列表渲染机制、状态管理设计、数组操作方法、UI交互设计等多个维度,对这款购物清单应用进行全面的技术剖析。通过本文的深入讲解,读者能够掌握HarmonyOS ArkTS开发中的核心知识点,特别是List列表组件的使用、ForEach循环渲染、数组状态管理、Checkbox组件交互以及待办事项类应用的设计要点。

2. 技术架构分析

2.1 整体架构设计

购物清单应用采用了标准的单页面架构,整个应用仅包含一个主页面,通过垂直布局的Column容器组织各个功能区块。从代码组织角度来看,应用主要分为以下几个核心部分:页面入口组件(ShoppingList)、通用标题栏组件(CommonTitleBar)、输入区域、统计区域、购物清单列表区域。

┌─────────────────────────────────────────────────────────┐
│                   ShoppingList页面                            │
│  ┌─────────────────────────────────────────────────┐    │
│  │              CommonTitleBar组件                  │    │
│  │              (导航栏+标题显示)                    │    │
│  └─────────────────────────────────────────────────┘    │
│  ┌─────────────────────────────────────────────────┐    │
│  │              输入区域                            │    │
│  │              (TextInput + Button)               │    │
│  └─────────────────────────────────────────────────┘    │
│  ┌─────────────────────────────────────────────────┐    │
│  │              统计区域                            │    │
│  │              (待购买/已完成统计)                 │    │
│  └─────────────────────────────────────────────────┘    │
│  ┌─────────────────────────────────────────────────┐    │
│  │              购物清单列表区域                    │    │
│  │              (List + ForEach + Checkbox)        │    │
│  └─────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────┘

购物清单应用是五个应用中UI结构最复杂的一个,包含了输入控件、统计显示和动态列表三个功能区块。

2.2 模块依赖关系

从依赖关系的角度分析,购物清单应用主要依赖以下几个核心模块:

首先是CommonTitleBar组件,该组件提供了统一的页面标题栏和返回按钮功能。

其次是ArkUI的基础组件:

  • TextInput:文本输入组件,用于输入新物品名称
  • Button:按钮组件,用于触发添加操作
  • List:列表容器组件,用于展示物品列表
  • ListItem:列表项组件,对应单个物品
  • ForEach:循环渲染组件,用于渲染物品列表
  • Checkbox:复选框组件,用于标记物品完成状态
  • Row/Column:布局容器组件

2.3 数据模型设计

购物清单应用的数据模型设计了一个物品数组:

@State app_items: Record<string, boolean>[] = [];
@State app_newItem: string = '';
状态变量 类型 初始值 说明
app_items Record<string, boolean>[] [] 购物物品数组
app_newItem string ‘’ 新物品输入框的值

物品的数据结构采用Record类型:

Record<string, boolean> = {
  'name': '物品名称',    // string
  'checked': false       // boolean
}

使用Record类型而非自定义接口是ArkTS中的常见做法。Record<string, boolean>表示键为string类型、值为boolean类型的对象。

3. 核心代码详解

3.1 状态管理机制

购物清单应用的状态管理使用数组和字符串两个@State装饰器:

@State app_items: Record<string, boolean>[] = [];
@State app_newItem: string = '';

两个状态变量的职责划分:

app_items:存储购物清单中的所有物品,每个物品包含名称和完成状态

app_newItem:存储用户当前在输入框中输入的文本

数组状态的特点:

  • 数组是引用类型,修改数组元素需要触发状态更新
  • 直接修改数组元素(如this.app_items[0].checked = true)不会自动触发UI更新
  • 需要使用数组方法(push、splice)来修改数组

3.2 添加物品功能

添加物品功能展示了如何向数组添加元素:

Button('添加')
  .width('30%')
  .onClick(() => {
    if (this.app_newItem !== '') {
      let app_item: Record<string, boolean> = {
        'name': this.app_newItem,
        'checked': false
      };
      this.app_items.push(app_item);
      this.app_newItem = '';
    }
  })

添加物品的流程:

  1. 检查输入是否为空
  2. 创建新的物品对象(名称=输入值,完成状态=false)
  3. 使用push方法将物品添加到数组
  4. 清空输入框

push方法的特点:

  • 直接修改原数组
  • 自动触发状态更新
  • UI自动重新渲染

3.3 列表渲染机制

ForEach组件是ArkUI中渲染列表的核心组件:

List() {
  ForEach(this.app_items, (app_item: Record<string, boolean>, app_index: number) => {
    ListItem() {
      Row() {
        Checkbox()
          .select(app_item['checked'])
          .onChange((app_value: boolean) => {
            this.app_items[app_index]['checked'] = app_value;
          })

        Text(app_item['name'])
          .fontSize(16)
          .fontColor($r('app.color.app_color_text_primary'))
          .opacity(app_item['checked'] ? 0.5 : 1.0)
          .decoration(app_item['checked'] ? TextDecorationType.LineThrough : TextDecorationType.None)

        Blank()

        Button('删除')
          .fontSize(12)
          .type(ButtonType.Normal)
          .backgroundColor($r('app.color.app_color_danger'))
          .onClick(() => {
            this.app_items.splice(app_index, 1);
          })
      }
      .width('100%')
      .padding(12)
      .backgroundColor($r('app.color.app_color_white'))
      .borderRadius(8)
    }
  })
}
.width('100%')
.layoutWeight(1)

ForEach的关键参数:

参数 说明
this.app_items 要遍历的数组
app_item 当前遍历到的元素
app_index 当前元素的索引(可选)

ForEach的渲染机制:

  • 数组中每个元素渲染一个ListItem
  • 使用索引追踪元素,便于删除操作
  • 数组变化时自动重新渲染列表

3.4 切换完成状态

Checkbox组件用于切换物品的完成状态:

Checkbox()
  .select(app_item['checked'])
  .onChange((app_value: boolean) => {
    this.app_items[app_index]['checked'] = app_value;
  })

Checkbox的关键属性和方法:

属性/方法 说明
select 设置复选框的选中状态
onChange 选中状态变化时的回调

完成状态变化的视觉效果:

Text(app_item['name'])
  .opacity(app_item['checked'] ? 0.5 : 1.0)           // 半透明效果
  .decoration(app_item['checked'] ? TextDecorationType.LineThrough : TextDecorationType.None)  // 删除线效果

当物品被标记为完成时:

  • 文字变为半透明(opacity: 0.5)
  • 文字添加删除线(decoration: LineThrough)

3.5 删除物品功能

splice方法用于从数组中删除元素:

Button('删除')
  .onClick(() => {
    this.app_items.splice(app_index, 1);
  })

splice方法的参数:

参数 说明
app_index 0 要删除元素的起始索引
1 1 要删除的元素数量

splice方法的特点:

  • 直接修改原数组
  • 自动触发状态更新
  • 删除后后续元素自动重新索引

3.6 统计数据计算

统计区域显示待购买和已完成的数量:

Row({ space: 16 }) {
  Text(`待购买: ${this.app_getUncheckedCount()}`)
    .fontSize(14)
    .fontColor($r('app.color.app_color_text_secondary'))

  Text(`已完成: ${this.app_getCheckedCount()}`)
    .fontSize(14)
    .fontColor($r('app.color.app_color_success'))
}

统计方法的实现:

private app_getUncheckedCount(): number {
  let app_count: number = 0;
  for (let app_i: number = 0; app_i < this.app_items.length; app_i++) {
    if (!this.app_items[app_i]['checked']) {
      app_count++;
    }
  }
  return app_count;
}

private app_getCheckedCount(): number {
  let app_count: number = 0;
  for (let app_i: number = 0; app_i < this.app_items.length; app_i++) {
    if (this.app_items[app_i]['checked']) {
      app_count++;
    }
  }
  return app_count;
}

注意:由于ArkTS不支持解构赋值和部分数组方法,统计使用了传统的for循环实现。

4. UI布局设计

4.1 整体布局结构

购物清单应用的UI布局采用垂直堆叠的Column容器:

build() {
  Column() {
    CommonTitleBar({
      app_title: '购物清单',
      app_showBack: true
    })

    Column({ space: 16 }) {
      // 输入区域
      // 统计区域
      // 列表区域
    }
    .width('100%')
    .padding(16)
  }
  .width('100%')
  .height('100%')
  .backgroundColor($r('app.color.app_color_background'))
}

4.2 输入区域设计

输入区域包含文本输入框和添加按钮:

Row({ space: 12 }) {
  TextInput({ placeholder: '添加新物品' })
    .width('70%')
    .onChange((app_value: string) => {
      this.app_newItem = app_value;
    })

  Button('添加')
    .width('30%')
    .onClick(() => {
      // 添加逻辑
    })
}

设计要点解析:

输入框:占70%宽度,留出空间给按钮

按钮:占30%宽度,提供足够的点击区域

比例分配:70:30的比例在移动端设计中较为常见

4.3 统计区域设计

统计区域显示待购买和已完成物品的数量:

Row({ space: 16 }) {
  Text(`待购买: ${this.app_getUncheckedCount()}`)
    .fontSize(14)
    .fontColor($r('app.color.app_color_text_secondary'))

  Text(`已完成: ${this.app_getCheckedCount()}`)
    .fontSize(14)
    .fontColor($r('app.color.app_color_success'))
}

设计要点解析:

左右分布:两个统计项水平排列

颜色区分:待购买用普通灰色,已完成用成功绿色

动态数值:数值由方法计算得出,实时更新

4.4 列表区域设计

列表区域是购物清单应用的核心界面:

List() {
  ForEach(this.app_items, (app_item: Record<string, boolean>, app_index: number) => {
    ListItem() {
      Row() {
        Checkbox()
          .select(app_item['checked'])
          .onChange((app_value: boolean) => {
            this.app_items[app_index]['checked'] = app_value;
          })

        Text(app_item['name'])
          .fontSize(16)
          .fontColor($r('app.color.app_color_text_primary'))
          .opacity(app_item['checked'] ? 0.5 : 1.0)
          .decoration(app_item['checked'] ? TextDecorationType.LineThrough : TextDecorationType.None)

        Blank()

        Button('删除')
          .fontSize(12)
          .type(ButtonType.Normal)
          .backgroundColor($r('app.color.app_color_danger'))
          .onClick(() => {
            this.app_items.splice(app_index, 1);
          })
      }
      .width('100%')
      .padding(12)
      .backgroundColor($r('app.color.app_color_white'))
      .borderRadius(8)
    }
  })
}
.width('100%')
.layoutWeight(1)

设计要点解析:

List容器:提供可滚动列表的基础设施

layoutWeight(1):占据剩余的全部空间,实现自适应高度

Row布局:水平排列复选框、物品名称和删除按钮

Blank():自动填充中间空间,将删除按钮推到右侧

圆角卡片:每个列表项都是带圆角的白色卡片

5. 列表类应用开发要点

5.1 List组件基础

List是ArkUI中用于展示长列表的容器组件:

List() {
  // ListItem子组件
}
.width('100%')
.height('100%')

List组件的特点:

  • 支持大量数据的高效渲染
  • 自动处理滚动和复用
  • 与ForEach配合实现动态列表

5.2 ForEach循环渲染

ForEach是ArkUI中实现循环渲染的组件:

ForEach(arr, (item, index) => {
  // 渲染逻辑
})

ForEach的渲染流程:

  1. 接收数组作为数据源
  2. 遍历数组每个元素
  3. 为每个元素执行渲染逻辑
  4. 返回对应的UI组件

5.3 数组状态管理

ArkTS中数组状态管理的注意事项:

正确做法

// 使用push添加元素
this.app_items.push(newItem);

// 使用splice删除元素
this.app_items.splice(index, 1);

// 直接修改元素属性(需要确保触发更新)
this.app_items[index]['checked'] = true;

ArkTS限制

  • 不支持解构赋值
  • 不支持filter、map等高阶数组方法
  • 不支持通过索引直接赋值新对象

5.4 列表性能优化

列表性能优化的常用方法:

保持Item简单:避免在Item中执行复杂计算

使用layoutWeight:正确设置列表项的权重

懒加载:对于超长列表,考虑使用懒加载

List() {
  ForEach(this.app_items, (app_item, index) => {
    ListItem() {
      // 简单的Item内容
    }
  })
}

6. ArkTS核心特性应用

6.1 Record类型使用

购物清单应用使用Record类型定义物品结构:

@State app_items: Record<string, boolean>[] = [];

Record类型的特点:

  • 键的类型固定为string
  • 值的类型固定为boolean
  • 适合简单的键值对数据结构
  • 不需要预先定义接口

6.2 ForEach组件详解

ForEach是ArkUI中实现列表渲染的核心组件:

ForEach(
  this.app_items,                          // 数据源
  (app_item: Record<string, boolean>, app_index: number) => {  // 回调
    ListItem() { ... }                      // 渲染内容
  }
)

ForEach的关键点:

  • 第一个参数是数组
  • 第二个参数是生成器函数
  • 生成器返回要渲染的UI组件
  • 支持可选的索引参数

6.3 Checkbox组件使用

Checkbox是ArkUI中的复选框组件:

Checkbox()
  .select(app_item['checked'])
  .onChange((app_value: boolean) => {
    // 处理选中状态变化
  })

Checkbox的属性:

属性 类型 说明
select boolean 当前选中状态

Checkbox的事件:

事件 回调参数 说明
onChange boolean 选中状态变化时触发

6.4 TextInput组件使用

TextInput是ArkUI中的文本输入组件:

TextInput({ placeholder: '添加新物品' })
  .width('70%')
  .onChange((app_value: string) => {
    this.app_newItem = app_value;
  })

TextInput的关键属性:

属性 说明
placeholder 占位提示文字
text 输入框的文本值

TextInput的事件:

事件 回调参数 说明
onChange string 输入内容变化时触发

7. 扩展与展望

7.1 当前功能总结

购物清单应用实现了以下核心功能:

功能模块 实现描述
添加物品 通过输入框和按钮添加新物品
列表展示 使用List和ForEach渲染物品列表
切换完成 使用Checkbox切换物品完成状态
删除物品 使用splice方法删除物品
统计显示 实时计算待购买/已完成数量
视觉效果 完成项半透明+删除线效果

7.2 功能扩展方向

基于当前的购物清单应用架构,可以进行以下功能扩展:

数据持久化:使用AppStorage保存购物清单:

aboutToDisappear(): void {
  app_setString('shopping_items', JSON.stringify(this.app_items));
}

aboutToAppear(): void {
  const app_stored: string = app_getString('shopping_items', '[]');
  try {
    this.app_items = JSON.parse(app_stored);
  } catch (error) {
    console.error('加载购物清单失败');
  }
}

编辑物品:支持修改物品名称:

Button('编辑')
  .onClick(() => {
    // 显示编辑对话框
    // 更新物品名称
  })

分类管理:支持物品分类:

@State app_category: string = '全部';

interface ShoppingItem {
  name: string;
  checked: boolean;
  category: string;
}

批量操作:支持全选、批量删除:

Button('全选')
  .onClick(() => {
    for (let app_i: number = 0; app_i < this.app_items.length; app_i++) {
      this.app_items[app_i]['checked'] = true;
    }
  })

Button('清空已完成')
  .onClick(() => {
    // 过滤掉已完成的项
    let app_unchecked: Record<string, boolean>[] = [];
    for (let app_i: number = 0; app_i < this.app_items.length; app_i++) {
      if (!this.app_items[app_i]['checked']) {
        app_unchecked.push(this.app_items[app_i]);
      }
    }
    this.app_items = app_unchecked;
  })

拖拽排序:支持调整物品顺序:

List() {
  ForEach(this.app_items, (app_item, index) => {
    ListItem() {
      Row() {
        // 物品内容
      }
      .draggable(true)  // 启用拖拽
    }
  })
}

搜索功能:支持搜索和过滤:

@State app_searchText: string = '';

TextInput({ placeholder: '搜索物品' })
  .onChange((app_value: string) => {
    this.app_searchText = app_value;
  })

// 在ForEach中过滤
ForEach(this.app_items.filter(item => item['name'].includes(this.app_searchText)), ...)

8. 技术要点总结

8.1 核心代码模块

购物清单应用的代码结构:

@Entry
@Component
struct ShoppingList {
  @State app_items: Record<string, boolean>[] = [];
  @State app_newItem: string = '';

  build() {
    Column() {
      CommonTitleBar({...})
      Column({ space: 16 }) {
        // 输入区域
        // 统计区域
        // 列表区域(List + ForEach)
      }
    }
  }

  private app_getUncheckedCount(): number {...}
  private app_getCheckedCount(): number {...}
}

8.2 关键技术点

技术点 在本应用中的体现
List组件 购物清单的容器
ForEach组件 遍历渲染物品列表
Checkbox组件 切换物品完成状态
TextInput组件 输入新物品名称
Record类型 物品数据结构
splice方法 删除数组元素
push方法 添加数组元素
layoutWeight 自适应列表高度

8.3 数组操作方法

方法 用途 示例
push 添加元素到末尾 this.app_items.push(item)
splice 删除指定位置元素 this.app_items.splice(index, 1)

9. 完整代码结构

import { CommonTitleBar } from '../../components/CommonTitleBar';

@Entry
@Component
struct ShoppingList {
  @State app_items: Record<string, boolean>[] = [];
  @State app_newItem: string = '';

  build() {
    Column() {
      CommonTitleBar({
        app_title: '购物清单',
        app_showBack: true
      })

      Column({ space: 16 }) {
        Row({ space: 12 }) {
          TextInput({ placeholder: '添加新物品' })
            .width('70%')
            .onChange((app_value: string) => {
              this.app_newItem = app_value;
            })

          Button('添加')
            .width('30%')
            .onClick(() => {
              if (this.app_newItem !== '') {
                let app_item: Record<string, boolean> = {
                  'name': this.app_newItem,
                  'checked': false
                };
                this.app_items.push(app_item);
                this.app_newItem = '';
              }
            })
        }

        Row({ space: 16 }) {
          Text(`待购买: ${this.app_getUncheckedCount()}`)
            .fontSize(14)
            .fontColor($r('app.color.app_color_text_secondary'))

          Text(`已完成: ${this.app_getCheckedCount()}`)
            .fontSize(14)
            .fontColor($r('app.color.app_color_success'))
        }

        List() {
          ForEach(this.app_items, (app_item: Record<string, boolean>, app_index: number) => {
            ListItem() {
              Row() {
                Checkbox()
                  .select(app_item['checked'])
                  .onChange((app_value: boolean) => {
                    this.app_items[app_index]['checked'] = app_value;
                  })

                Text(app_item['name'])
                  .fontSize(16)
                  .fontColor($r('app.color.app_color_text_primary'))
                  .opacity(app_item['checked'] ? 0.5 : 1.0)
                  .decoration(app_item['checked'] ? TextDecorationType.LineThrough : TextDecorationType.None)

                Blank()

                Button('删除')
                  .fontSize(12)
                  .type(ButtonType.Normal)
                  .backgroundColor($r('app.color.app_color_danger'))
                  .onClick(() => {
                    this.app_items.splice(app_index, 1);
                  })
              }
              .width('100%')
              .padding(12)
              .backgroundColor($r('app.color.app_color_white'))
              .borderRadius(8)
            }
          })
        }
        .width('100%')
        .layoutWeight(1)
      }
      .width('100%')
      .padding(16)
    }
    .width('100%')
    .height('100%')
    .backgroundColor($r('app.color.app_color_background'))
  }

  private app_getUncheckedCount(): number {
    let app_count: number = 0;
    for (let app_i: number = 0; app_i < this.app_items.length; app_i++) {
      if (!this.app_items[app_i]['checked']) {
        app_count++;
      }
    }
    return app_count;
  }

  private app_getCheckedCount(): number {
    let app_count: number = 0;
    for (let app_i: number = 0; app_i < this.app_items.length; app_i++) {
      if (this.app_items[app_i]['checked']) {
        app_count++;
      }
    }
    return app_count;
  }
}

10. 结语

购物清单应用作为一款待办事项类应用,展示了HarmonyOS ArkUI框架在列表类应用开发方面的核心能力。通过List、ForEach、Checkbox等组件的组合使用,实现了添加、完成标记、删除等完整的功能流程。

从技术角度来看,应用的核心亮点在于列表渲染和数组状态管理。ForEach组件配合List组件实现了高效列表渲染,push和splice方法实现了数组的增删操作,Checkbox组件实现了交互功能。

从应用设计的角度来看,购物清单应用遵循了"简洁直观、操作便捷"的设计原则。每个列表项都清晰地展示了物品名称、完成状态和删除按钮,用户可以快速完成各种操作。半透明和删除线效果让已完成项目一目了然。

Logo

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

更多推荐