在这里插入图片描述

一、概述

备忘录应用是一款基于HarmonyOS ArkTS框架开发的待办事项管理工具,提供了完整的待办事项管理功能,包括添加、标记完成、删除、筛选等核心操作。该应用展示了HarmonyOS声明式UI开发的核心模式,是学习ArkTS开发的经典案例。

1.1 应用功能清单

功能模块 描述
添加待办 通过输入框添加新的待办事项
完成标记 通过复选框切换待办事项的完成状态
删除待办 单条删除指定的待办事项
筛选查看 支持全部/待完成/已完成三种筛选模式
批量清理 一键清除所有已完成的待办事项
数据统计 显示总数、待完成数、已完成数
数据持久化 自动保存到本地存储,重启后数据不丢失

1.2 技术栈概览

本应用的技术架构体现了HarmonyOS应用开发的核心特性:

  • UI框架:ArkUI声明式开发范式
  • 状态管理:@State装饰器实现响应式数据绑定
  • 列表渲染:ForEach循环渲染动态列表
  • 数据持久化:AppStorage + dataPreferences实现本地存储
  • 组件复用:CommonTitleBar通用标题栏组件
  • 路由管理:AppRouter页面导航管理

二、架构分析

2.1 整体架构

备忘录应用采用标准的ArkUI单页面架构,所有业务逻辑和UI构建都在一个@Component struct中完成。应用结构清晰,分为以下几个层次:

┌─────────────────────────────────────┐
│           Memo页面                  │
│  ┌─────────────────────────────┐   │
│  │      CommonTitleBar         │   │
│  │        (标题栏)              │   │
│  └─────────────────────────────┘   │
│  ┌─────────────────────────────┐   │
│  │      输入区域                │   │
│  │  TextField + Button         │   │
│  └─────────────────────────────┘   │
│  ┌─────────────────────────────┐   │
│  │      筛选按钮组              │   │
│  │   全部 | 待完成 | 已完成      │   │
│  └─────────────────────────────┘   │
│  ┌─────────────────────────────┐   │
│  │      待办列表(Scroll)        │   │
│  │   ForEach渲染待办项          │   │
│  └─────────────────────────────┘   │
│  ┌─────────────────────────────┐   │
│  │      统计与操作栏            │   │
│  │   统计数据 + 清除已完成按钮   │   │
│  └─────────────────────────────┘   │
└─────────────────────────────────────┘

2.2 数据流分析

应用采用单向数据流模式,数据变化驱动UI自动更新:

  1. 初始化阶段:aboutToAppear回调触发app_loadMemos,从AppStorage读取已保存的待办数据
  2. 用户交互阶段:用户操作触发状态变更(@State变量修改)
  3. 数据持久化阶段:每次状态变更后自动调用app_saveMemos保存到本地
  4. UI更新阶段:ArkUI框架自动检测@State变量变化,触发相关UI组件重新渲染
用户操作 → @State变更 → 自动保存 → UI自动更新
    ↑                                      │
    └────────── AppStorage ────────────────┘

2.3 模块依赖关系

pages/life/Memo.ets
├── components/CommonTitleBar.ets
│   └── utils/router.ets
└── utils/storage.ets
    └── @ohos.data.preferences

三、核心代码详解

3.1 数据结构定义

备忘录应用使用TypeScript的interface来定义待办事项的数据结构,这种方式在ArkTS中是标准的对象类型定义方式。

interface App_MemoItem {
  app_id: string;
  app_content: string;
  app_completed: boolean;
  app_createdAt: string;
}

字段说明

字段名 类型 描述
app_id string 待办事项的唯一标识符,使用时间戳生成
app_content string 待办事项的文本内容
app_completed boolean 完成状态,true表示已完成
app_createdAt string 创建时间,格式为本地化日期时间字符串

代码解读

  • app_id字段使用Date.now().toString()生成,确保每个待办事项都有全局唯一的标识
  • app_completed是布尔类型,用于标记完成状态,这在待办类应用中是最核心的状态字段
  • app_createdAt记录创建时间,方便用户了解待办的添加时间
// 创建新待办时的数据结构
const app_newItem: App_MemoItem = {
  app_id: Date.now().toString(),
  app_content: this.app_newMemo.trim(),
  app_completed: false,
  app_createdAt: new Date().toLocaleString('zh-CN')
};

命名规范说明

应用所有变量和函数都以app_前缀开头,这是该项目的命名约定,用于:

  • 明确标识这是应用层代码
  • 避免与系统API命名冲突
  • 提高代码可读性和可维护性

3.2 状态管理

备忘录应用的状态管理采用ArkTS提供的@State装饰器,这是声明式UI框架中最基本的状态管理机制。

@State app_memos: App_MemoItem[] = [];
@State app_newMemo: string = '';
@State app_filter: 'all' | 'active' | 'completed' = 'all';

三个核心状态变量

状态变量 类型 默认值 用途
app_memos App_MemoItem[] [] 存储所有待办事项的数组
app_newMemo string ‘’ 输入框的实时绑定值
app_filter ‘all’ | ‘active’ | ‘completed’ ‘all’ 当前筛选模式

@State装饰器的作用机制

@State是ArkTS中的响应式状态管理装饰器,它的核心机制如下:

  1. 私有性:@State修饰的变量是组件私有的,只能在当前组件内访问和修改
  2. 响应式:当@State变量值发生变化时,ArkUI框架会自动触发UI重新渲染
  3. 追踪性:框架会追踪@State变量的所有引用,在变量变化时精准更新相关UI组件
// 状态变化的典型场景:添加新待办
app_addMemo(): void {
  if (this.app_newMemo.trim() === '') {
    return;
  }
  const app_newItem: App_MemoItem = {
    app_id: Date.now().toString(),
    app_content: this.app_newMemo.trim(),
    app_completed: false,
    app_createdAt: new Date().toLocaleString('zh-CN')
  };
  this.app_memos.unshift(app_newItem);  // 修改app_memos数组
  this.app_newMemo = '';                 // 清空输入框
  this.app_saveMemos();                  // 持久化保存
}

app_memos数组发生变化时:

  • ForEach会自动检测到数组变化
  • 列表会重新渲染,显示新增的待办项
  • 统计栏的数字也会自动更新

筛选状态的应用

// 筛选状态影响按钮样式
Button('全部')
  .backgroundColor(this.app_filter === 'all' ? $r('app.color.app_color_primary') : $r('app.color.app_color_white'))
  .fontColor(this.app_filter === 'all' ? $r('app.color.app_color_white') : $r('app.color.app_color_text_primary'))
  .onClick(() => this.app_filter = 'all')

Button('待完成')
  .backgroundColor(this.app_filter === 'active' ? $r('app.color.app_color_primary') : $r('app.color.app_color_white'))
  .onClick(() => this.app_filter = 'active')

Button('已完成')
  .backgroundColor(this.app_filter === 'completed' ? $r('app.color.app_color_primary') : $r('app.color.app_color_white'))
  .onClick(() => this.app_filter = 'completed')

筛选按钮通过条件表达式动态改变背景色和字体颜色,当前选中的筛选模式会显示为主色调(蓝色),未选中的显示为白色。


3.3 数据持久化

数据持久化是备忘录应用的核心功能之一,确保用户数据在应用关闭后不会丢失。应用使用AppStorage工具类配合HarmonyOS的dataPreferences API实现本地存储。

AppStorage工具类实现

import dataPreferences from '@ohos.data.preferences';
import type common from '@ohos.app.ability.common';

let app_pref_instance: dataPreferences.Preferences | null = null;

export class AppStorage {
  static async app_init(app_context: common.UIAbilityContext): Promise<void> {
    try {
      app_pref_instance = await dataPreferences.getPreferences(app_context, 'app_storage');
    } catch (error) {
      console.error(`存储初始化失败: ${error}`);
    }
  }

  static async app_setString(app_key: string, app_value: string): Promise<void> {
    if (app_pref_instance !== null) {
      try {
        await app_pref_instance.putSync(app_key, app_value);
        await app_pref_instance.flushSync();
      } catch (error) {
        console.error(`存储设置失败: ${error}`);
      }
    }
  }

  static app_getString(app_key: string, app_defaultValue: string): string {
    if (app_pref_instance !== null) {
      try {
        return app_pref_instance.getSync(app_key, app_defaultValue) as string;
      } catch (error) {
        console.error(`存储获取失败: ${error}`);
        return app_defaultValue;
      }
    }
    return app_defaultValue;
  }
  // ... 其他方法类似
}

AppStorage核心方法说明

方法名 参数 返回值 功能
app_init UIAbilityContext Promise 初始化Preferences实例
app_setString key, value Promise 异步存储字符串
app_getString key, defaultValue string 同步读取字符串
app_setNumber key, value Promise 异步存储数字
app_getNumber key, defaultValue number 同步读取数字
app_setBoolean key, value Promise 异步存储布尔值
app_getBoolean key, defaultValue boolean 同步读取布尔值
app_delete key Promise 删除指定key
app_clear - Promise 清空所有数据

Memo页面中的存储调用

app_loadMemos(): void {
  const app_stored: string = app_getString('memo_items', '[]');
  try {
    this.app_memos = JSON.parse(app_stored);
  } catch (error) {
    this.app_memos = [];
  }
}

app_saveMemos(): void {
  app_setString('memo_items', JSON.stringify(this.app_memos));
}

存储原理剖析

  1. 数据序列化:待办数组通过JSON.stringify转换为JSON字符串
  2. 键值对存储:使用’memo_items’作为存储键名
  3. 数据读取:启动时通过JSON.parse将JSON字符串还原为数组对象
  4. 容错处理:读取失败时使用空数组作为默认值,确保应用不会崩溃
// 存储的数据格式示例
'memo_items' -> '[{"app_id":"1718600000000","app_content":"买牛奶","app_completed":false,"app_createdAt":"2024/6/17 10:00:00"},...]'

同步与异步的合理使用

  • app_getString使用同步读取,因为启动时需要立即获取数据进行渲染
  • app_setString使用异步存储,配合flushSync确保数据写入磁盘
  • 这种同步读取、异步写入的模式是移动应用开发的常见优化策略

3.4 列表渲染

列表渲染是备忘录应用的核心UI功能,使用ForEach循环实现动态列表的渲染。

ForEach语法结构

ForEach(this.app_getFilteredMemos(), (app_item: App_MemoItem) => {
  // 列表项内容
})

ForEach接收两个参数:

  1. arr: 要遍历的数组,这里调用app_getFilteredMemos()获取筛选后的数据
  2. itemGenerator: 遍历处理函数,接收当前项作为参数

列表项结构

Row({ space: 12 }) {
  Checkbox()
    .select(app_item.app_completed)
    .selectedColor($r('app.color.app_color_success'))
    .onChange(() => this.app_toggleMemo(app_item.app_id))

  Text(app_item.app_content)
    .width('flexGrow')
    .fontSize(16)
    .fontColor(app_item.app_completed ? $r('app.color.app_color_text_tertiary') : $r('app.color.app_color_text_primary'))
    .decoration({ type: app_item.app_completed ? TextDecorationType.LineThrough : TextDecorationType.None })

  Text(app_item.app_createdAt)
    .fontSize(12)
    .fontColor($r('app.color.app_color_text_tertiary'))

  Button() {
    Text('删除')
      .fontSize(12)
  }
  .width(50)
  .height(32)
  .backgroundColor($r('app.color.app_color_danger'))
  .fontColor($r('app.color.app_color_white'))
  .borderRadius(6)
  .onClick(() => this.app_deleteMemo(app_item.app_id))
}
.width('100%')
.height(56)
.backgroundColor($r('app.color.app_color_white'))
.borderRadius(8)
.padding({ left: 16, right: 16 })
.alignItems(VerticalAlign.Center)

列表项组件说明

组件 作用 关键属性
Checkbox 完成状态切换 select绑定完成状态,onChange触发切换
Text(内容) 显示待办文字 flexGrow占满剩余空间,已完成时显示删除线
Text(时间) 显示创建时间 字体较小,颜色为灰色
Button 删除操作 红色背景,调用app_deleteMemo

已完成状态的视觉表现

.fontColor(app_item.app_completed ? $r('app.color.app_color_text_tertiary') : $r('app.color.app_color_text_primary'))
.decoration({ type: app_item.app_completed ? TextDecorationType.LineThrough : TextDecorationType.None })

app_completed为true时:

  • 文字颜色变为灰色(app_color_text_tertiary)
  • 文字添加删除线装饰(LineThrough)
  • 视觉上明确区分已完成和未完成的待办

空状态处理

if (this.app_getFilteredMemos().length === 0) {
  Column({ space: 8 }) {
    Text('暂无待办事项')
      .fontSize(16)
      .fontColor($r('app.color.app_color_text_tertiary'))
    Text('点击上方添加新的待办')
      .fontSize(14)
      .fontColor($r('app.color.app_color_text_secondary'))
  }
  .width('100%')
  .padding({ top: 60, bottom: 60 })
  .alignItems(HorizontalAlign.Center)
}

空状态提示使用条件渲染(if指令),当筛选后的列表为空时显示友好的提示文案,引导用户添加新的待办事项。


3.5 筛选功能实现

筛选功能是备忘录应用的重要交互特性,支持全部、待完成、已完成三种筛选模式。

筛选状态的类型定义

@State app_filter: 'all' | 'active' | 'completed' = 'all';

使用联合类型定义筛选模式,三种模式含义如下:

模式 含义
全部 ‘all’ 显示所有待办事项
待完成 ‘active’ 仅显示未完成的待办事项
已完成 ‘completed’ 仅显示已完成的待办事项

筛选逻辑实现

app_getFilteredMemos(): App_MemoItem[] {
  switch (this.app_filter) {
    case 'active':
      return this.app_memos.filter((app_item: App_MemoItem) => !app_item.app_completed);
    case 'completed':
      return this.app_memos.filter((app_item: App_MemoItem) => app_item.app_completed);
    default:
      return this.app_memos;
  }
}

筛选函数使用switch语句匹配当前筛选模式:

  • 'active':返回app_completed为false的项
  • 'completed':返回app_completed为true的项
  • 'all':直接返回完整数组

筛选按钮组实现

Row({ space: 8 }) {
  Button('全部')
    .width('33%')
    .height(40)
    .fontSize(14)
    .fontWeight(FontWeight.Medium)
    .backgroundColor(this.app_filter === 'all' ? $r('app.color.app_color_primary') : $r('app.color.app_color_white'))
    .fontColor(this.app_filter === 'all' ? $r('app.color.app_color_white') : $r('app.color.app_color_text_primary'))
    .borderRadius(8)
    .onClick(() => this.app_filter = 'all')

  Button('待完成')
    .width('33%')
    .height(40)
    .fontSize(14)
    .fontWeight(FontWeight.Medium)
    .backgroundColor(this.app_filter === 'active' ? $r('app.color.app_color_primary') : $r('app.color.app_color_white'))
    .fontColor(this.app_filter === 'active' ? $r('app.color.app_color_white') : $r('app.color.app_color_text_primary'))
    .borderRadius(8)
    .onClick(() => this.app_filter = 'active')

  Button('已完成')
    .width('33%')
    .height(40)
    .fontSize(14)
    .fontWeight(FontWeight.Medium)
    .backgroundColor(this.app_filter === 'completed' ? $r('app.color.app_color_primary') : $r('app.color.app_color_white'))
    .fontColor(this.app_filter === 'completed' ? $r('app.color.app_color_white') : $r('app.color.app_color_text_primary'))
    .borderRadius(8)
    .onClick(() => this.app_filter = 'completed')
}
.width('100%')
.padding({ left: 16, right: 16 })

三个按钮采用均分宽度布局(各占33%),按钮之间间距为8。选中状态通过动态绑定背景色和字体颜色来实现,提供了直观的视觉反馈。


3.6 待办操作方法

备忘录应用提供了完整的待办操作方法,包括添加、完成切换、删除和批量清除。

添加待办

app_addMemo(): void {
  if (this.app_newMemo.trim() === '') {
    return;
  }
  const app_newItem: App_MemoItem = {
    app_id: Date.now().toString(),
    app_content: this.app_newMemo.trim(),
    app_completed: false,
    app_createdAt: new Date().toLocaleString('zh-CN')
  };
  this.app_memos.unshift(app_newItem);
  this.app_newMemo = '';
  this.app_saveMemos();
}

添加逻辑说明:

  1. 首先校验输入内容,空白内容直接返回不处理
  2. 使用trim()去除首尾空格,避免无效空白待办
  3. 使用unshift()将新待办添加到数组头部,新待办显示在列表最上方
  4. 生成唯一ID使用时间戳Date.now().toString()
  5. 创建完成后清空输入框
  6. 自动触发保存持久化

完成状态切换

app_toggleMemo(app_id: string): void {
  const app_index: number = this.app_memos.findIndex((app_item: App_MemoItem) => app_item.app_id === app_id);
  if (app_index !== -1) {
    this.app_memos[app_index].app_completed = !this.app_memos[app_index].app_completed;
    this.app_saveMemos();
  }
}

切换逻辑说明:

  1. 根据ID查找待办在数组中的索引位置
  2. 使用findIndex遍历数组匹配app_id
  3. 找到后取反app_completed布尔值
  4. 触发保存操作

删除待办

app_deleteMemo(app_id: string): void {
  this.app_memos = this.app_memos.filter((app_item: App_MemoItem) => app_item.app_id !== app_id);
  this.app_saveMemos();
}

删除逻辑说明:

  1. 使用filter方法创建新数组,排除要删除的项
  2. 通过ID匹配确定要排除的项
  3. 重新赋值app_memos触发响应式更新
  4. 触发保存操作

批量清除已完成

app_clearCompleted(): void {
  this.app_memos = this.app_memos.filter((app_item: App_MemoItem) => !app_item.app_completed);
  this.app_saveMemos();
}

批量清除逻辑说明:

  1. 使用filter方法保留app_completed为false的项
  2. 即过滤掉所有已完成的待办事项
  3. 触发保存操作

3.7 统计功能实现

统计功能为用户提供直观的数据概览,包括总数、待完成数、已完成数。

统计方法

app_getStats(): { app_total: number; app_active: number; app_completed: number } {
  const app_total: number = this.app_memos.length;
  const app_completed: number = this.app_memos.filter((app_item: App_MemoItem) => app_item.app_completed).length;
  return {
    app_total,
    app_active: app_total - app_completed,
    app_completed
  };
}

返回值类型说明:

  • app_total: 总待办数量
  • app_active: 待完成数量 = 总数 - 已完成数
  • app_completed: 已完成数量

统计栏UI

if (this.app_memos.length > 0) {
  Row({ space: 16 }) {
    Text(`总数: ${this.app_getStats().app_total} | 待完成: ${this.app_getStats().app_active} | 已完成: ${this.app_getStats().app_completed}`)
      .fontSize(14)
      .fontColor($r('app.color.app_color_text_secondary'))

    if (this.app_getStats().app_completed > 0) {
      Button('清除已完成')
        .width(120)
        .height(36)
        .fontSize(14)
        .fontWeight(FontWeight.Medium)
        .backgroundColor($r('app.color.app_color_warning'))
        .fontColor($r('app.color.app_color_white'))
        .borderRadius(6)
        .onClick(() => this.app_clearCompleted())
    }
  }
  .width('100%')
  .padding({ left: 16, right: 16, bottom: 16 })
  .justifyContent(FlexAlign.SpaceBetween)
}

统计栏UI特点:

  1. 使用条件渲染确保没有待办时不显示统计栏
  2. 统计信息使用管道符分隔显示
  3. "清除已完成"按钮仅在有已完成的待办时显示
  4. 按钮使用警告色(橙色)提示用户这是危险操作

3.8 生命周期与初始化

备忘录应用使用aboutToAppear生命周期回调进行数据初始化,这是ArkTS组件开发的标准模式。

aboutToAppear回调

aboutToAppear(): void {
  this.app_loadMemos();
}

生命周期说明:

  • aboutToAppear在组件即将显示时调用
  • 只会被调用一次,适合执行初始化操作
  • 确保UI渲染前数据已经加载完成

四、组件结构解析

4.1 整体布局架构

备忘录应用的UI采用垂直布局(Column),整体结构如下:

build() {
  Column() {
    // 1. 标题栏
    CommonTitleBar({
      app_title: '备忘录',
      app_showBack: true
    })

    // 2. 内容区域(Column + space: 16)
    Column({ space: 16 }) {
      // 输入区域
      // 筛选按钮组
      // 待办列表(Scroll包裹)
      // 统计操作栏
    }
    .flexGrow(1)
  }
  .width('100%')
  .height('100%')
  .backgroundColor($r('app.color.app_color_background'))
}

布局层次说明

层次 容器 作用
最外层 Column 页面根容器,设置背景色
第二层 Column 内容区域,子元素间距16
内容区 Row 输入行、筛选按钮组
内容区 Scroll 包裹列表,支持滚动
内容区 Row 底部统计操作栏

4.2 输入区域实现

Row({ space: 8 }) {
  TextInput({ placeholder: '输入待办事项...', text: this.app_newMemo })
    .width('flexGrow')
    .height(48)
    .fontSize(16)
    .backgroundColor($r('app.color.app_color_white'))
    .borderRadius(8)
    .padding({ left: 16 })
    .onChange((app_value: string) => {
      this.app_newMemo = app_value;
    })
    .onSubmit(() => this.app_addMemo())

  Button('添加')
    .width(80)
    .height(48)
    .fontSize(16)
    .fontWeight(FontWeight.Medium)
    .backgroundColor($r('app.color.app_color_primary'))
    .fontColor($r('app.color.app_color_white'))
    .borderRadius(8)
    .onClick(() => this.app_addMemo())
}
.width('100%')
.padding({ left: 16, right: 16 })

TextField配置说明

属性 说明
placeholder ‘输入待办事项…’ 占位提示文字
text this.app_newMemo 双向绑定输入值
height 48 固定高度
borderRadius 8 圆角8
onChange 箭头函数 输入值变化时更新状态
onSubmit this.app_addMemo 回车键提交

五、通用组件复用

5.1 CommonTitleBar组件

备忘录应用使用了CommonTitleBar通用标题栏组件,展示了ArkTS中组件复用的开发模式。

@Component
export struct CommonTitleBar {
  app_title: string = '';
  app_showBack: boolean = true;
  app_backCallback?: () => void;

  build() {
    Row() {
      if (this.app_showBack) {
        Button() {
          Image($r('app.media.foreground'))
            .width(24)
            .height(24)
            .fillColor($r('app.color.app_color_white'))
        }
        .width(44)
        .height(44)
        .backgroundColor($r('app.color.app_color_transparent'))
        .onClick(() => {
          if (this.app_backCallback !== undefined) {
            this.app_backCallback();
          } else {
            AppRouter.app_back();
          }
        })
      }

      Text(this.app_title)
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .fontColor($r('app.color.app_color_white'))
        .flexGrow(1)
        .textAlign(TextAlign.Center)

      Blank()
        .width(44)
    }
    .width('100%')
    .height(56)
    .backgroundColor($r('app.color.app_color_primary'))
    .padding({ left: 16, right: 16 })
  }
}

组件属性说明

属性 类型 默认值 说明
app_title string ‘’ 标题文字
app_showBack boolean true 是否显示返回按钮
app_backCallback () => void undefined 自定义返回回调

布局结构

  • 返回按钮:固定44宽度,仅在showBack为true时显示
  • 标题文字:flexGrow占满剩余空间,居中显示
  • 空白占位:固定44宽度,平衡返回按钮侧的宽度

六、技术亮点总结

6.1 声明式UI开发范式

备忘录应用充分展示了HarmonyOS ArkUI声明式开发的优势:

  1. 状态驱动UI:通过@State装饰器实现数据与UI的自动绑定,数据变化自动触发UI更新
  2. 简洁的语法:相比传统命令式UI,声明式UI代码更简洁、更易维护
  3. 组件化思想:通过@Component装饰器实现组件复用,提高代码复用性

6.2 响应式数据管理

应用的状态管理方案简洁高效:

@State app_memos: App_MemoItem[] = [];
@State app_newMemo: string = '';
@State app_filter: 'all' | 'active' | 'completed' = 'all';

三个@State变量覆盖了应用的全部动态数据:

  • 待办列表数据
  • 输入框状态
  • 筛选条件状态

6.3 数据持久化策略

采用 Preferences 本地存储方案:

  • 同步读取、异步写入的策略保证性能和可靠性
  • JSON序列化实现复杂对象的存储
  • 完善的错误处理机制

6.4 筛选模式的优雅实现

使用联合类型和switch语句实现筛选功能:

@State app_filter: 'all' | 'active' | 'completed' = 'all';

app_getFilteredMemos(): App_MemoItem[] {
  switch (this.app_filter) {
    case 'active':
      return this.app_memos.filter((app_item: App_MemoItem) => !app_item.app_completed);
    case 'completed':
      return this.app_memos.filter((app_item: App_MemoItem) => app_item.app_completed);
    default:
      return this.app_memos;
  }
}

七、完整代码对照

7.1 Memo页面完整代码

以下是备忘录应用的完整代码,供读者对照参考:

import { CommonTitleBar } from '../../components/CommonTitleBar';
import { AppStorage } from '../../utils/storage';

interface App_MemoItem {
  app_id: string;
  app_content: string;
  app_completed: boolean;
  app_createdAt: string;
}

@Entry
@Component
struct Memo {
  @State app_memos: App_MemoItem[] = [];
  @State app_newMemo: string = '';
  @State app_filter: 'all' | 'active' | 'completed' = 'all';

  aboutToAppear(): void {
    this.app_loadMemos();
  }

  app_loadMemos(): void {
    const app_stored: string = app_getString('memo_items', '[]');
    try {
      this.app_memos = JSON.parse(app_stored);
    } catch (error) {
      this.app_memos = [];
    }
  }

  app_saveMemos(): void {
    app_setString('memo_items', JSON.stringify(this.app_memos));
  }

  app_addMemo(): void {
    if (this.app_newMemo.trim() === '') {
      return;
    }
    const app_newItem: App_MemoItem = {
      app_id: Date.now().toString(),
      app_content: this.app_newMemo.trim(),
      app_completed: false,
      app_createdAt: new Date().toLocaleString('zh-CN')
    };
    this.app_memos.unshift(app_newItem);
    this.app_newMemo = '';
    this.app_saveMemos();
  }

  app_toggleMemo(app_id: string): void {
    const app_index: number = this.app_memos.findIndex((app_item: App_MemoItem) => app_item.app_id === app_id);
    if (app_index !== -1) {
      this.app_memos[app_index].app_completed = !this.app_memos[app_index].app_completed;
      this.app_saveMemos();
    }
  }

  app_deleteMemo(app_id: string): void {
    this.app_memos = this.app_memos.filter((app_item: App_MemoItem) => app_item.app_id !== app_id);
    this.app_saveMemos();
  }

  app_clearCompleted(): void {
    this.app_memos = this.app_memos.filter((app_item: App_MemoItem) => !app_item.app_completed);
    this.app_saveMemos();
  }

  app_getFilteredMemos(): App_MemoItem[] {
    switch (this.app_filter) {
      case 'active':
        return this.app_memos.filter((app_item: App_MemoItem) => !app_item.app_completed);
      case 'completed':
        return this.app_memos.filter((app_item: App_MonoItem) => app_item.app_completed);
      default:
        return this.app_memos;
    }
  }

  app_getStats(): { app_total: number; app_active: number; app_completed: number } {
    const app_total: number = this.app_memos.length;
    const app_completed: number = this.app_memos.filter((app_item: App_MemoItem) => app_item.app_completed).length;
    return {
      app_total,
      app_active: app_total - app_completed,
      app_completed
    };
  }

  build() {
    Column() {
      CommonTitleBar({
        app_title: '备忘录',
        app_showBack: true
      })

      Column({ space: 16 }) {
        Row({ space: 8 }) {
          TextInput({ placeholder: '输入待办事项...', text: this.app_newMemo })
            .width('flexGrow')
            .height(48)
            .fontSize(16)
            .backgroundColor($r('app.color.app_color_white'))
            .borderRadius(8)
            .padding({ left: 16 })
            .onChange((app_value: string) => {
              this.app_newMemo = app_value;
            })
            .onSubmit(() => this.app_addMemo())

          Button('添加')
            .width(80)
            .height(48)
            .fontSize(16)
            .fontWeight(FontWeight.Medium)
            .backgroundColor($r('app.color.app_color_primary'))
            .fontColor($r('app.color.app_color_white'))
            .borderRadius(8)
            .onClick(() => this.app_addMemo())
        }
        .width('100%')
        .padding({ left: 16, right: 16 })

        Row({ space: 8 }) {
          Button('全部')
            .width('33%')
            .height(40)
            .fontSize(14)
            .fontWeight(FontWeight.Medium)
            .backgroundColor(this.app_filter === 'all' ? $r('app.color.app_color_primary') : $r('app.color.app_color_white'))
            .fontColor(this.app_filter === 'all' ? $r('app.color.app_color_white') : $r('app.color.app_color_text_primary'))
            .borderRadius(8)
            .onClick(() => this.app_filter = 'all')

          Button('待完成')
            .width('33%')
            .height(40)
            .fontSize(14)
            .fontWeight(FontWeight.Medium)
            .backgroundColor(this.app_filter === 'active' ? $r('app.color.app_color_primary') : $r('app.color.app_color_white'))
            .fontColor(this.app_filter === 'active' ? $r('app.color.app_color_white') : $r('app.color.app_color_text_primary'))
            .borderRadius(8)
            .onClick(() => this.app_filter = 'active')

          Button('已完成')
            .width('33%')
            .height(40)
            .fontSize(14)
            .fontWeight(FontWeight.Medium)
            .backgroundColor(this.app_filter === 'completed' ? $r('app.color.app_color_primary') : $r('app.color.app_color_white'))
            .fontColor(this.app_filter === 'completed' ? $r('app.color.app_color_white') : $r('app.color.app_color_text_primary'))
            .borderRadius(8)
            .onClick(() => this.app_filter = 'completed')
        }
        .width('100%')
        .padding({ left: 16, right: 16 })

        Scroll() {
          Column({ space: 8 }) {
            ForEach(this.app_getFilteredMemos(), (app_item: App_MemoItem) => {
              Row({ space: 12 }) {
                Checkbox()
                  .select(app_item.app_completed)
                  .selectedColor($r('app.color.app_color_success'))
                  .onChange(() => this.app_toggleMemo(app_item.app_id))

                Text(app_item.app_content)
                  .width('flexGrow')
                  .fontSize(16)
                  .fontColor(app_item.app_completed ? $r('app.color.app_color_text_tertiary') : $r('app.color.app_color_text_primary'))
                  .decoration({ type: app_item.app_completed ? TextDecorationType.LineThrough : TextDecorationType.None })

                Text(app_item.app_createdAt)
                  .fontSize(12)
                  .fontColor($r('app.color.app_color_text_tertiary'))

                Button() {
                  Text('删除')
                    .fontSize(12)
                }
                .width(50)
                .height(32)
                .backgroundColor($r('app.color.app_color_danger'))
                .fontColor($r('app.color.app_color_white'))
                .borderRadius(6)
                .onClick(() => this.app_deleteMemo(app_item.app_id))
              }
              .width('100%')
              .height(56)
              .backgroundColor($r('app.color.app_color_white'))
              .borderRadius(8)
              .padding({ left: 16, right: 16 })
              .alignItems(VerticalAlign.Center)
            })

            if (this.app_getFilteredMemos().length === 0) {
              Column({ space: 8 }) {
                Text('暂无待办事项')
                  .fontSize(16)
                  .fontColor($r('app.color.app_color_text_tertiary'))
                Text('点击上方添加新的待办')
                  .fontSize(14)
                  .fontColor($r('app.color.app_color_text_secondary'))
              }
              .width('100%')
              .padding({ top: 60, bottom: 60 })
              .alignItems(HorizontalAlign.Center)
            }
          }
          .width('100%')
          .padding({ left: 16, right: 16 })
        }
        .width('100%')
        .flexGrow(1)

        if (this.app_memos.length > 0) {
          Row({ space: 16 }) {
            Text(`总数: ${this.app_getStats().app_total} | 待完成: ${this.app_getStats().app_active} | 已完成: ${this.app_getStats().app_completed}`)
              .fontSize(14)
              .fontColor($r('app.color.app_color_text_secondary'))

            if (this.app_getStats().app_completed > 0) {
              Button('清除已完成')
                .width(120)
                .height(36)
                .fontSize(14)
                .fontWeight(FontWeight.Medium)
                .backgroundColor($r('app.color.app_color_warning'))
                .fontColor($r('app.color.app_color_white'))
                .borderRadius(6)
                .onClick(() => this.app_clearCompleted())
            }
          }
          .width('100%')
          .padding({ left: 16, right: 16, bottom: 16 })
          .justifyContent(FlexAlign.SpaceBetween)
        }
      }
      .width('100%')
      .flexGrow(1)
    }
    .width('100%')
    .height('100%')
    .backgroundColor($r('app.color.app_color_background'))
  }
}

八、开发要点回顾

8.1 ArkTS开发核心概念

  1. @State装饰器:组件级状态管理,只能访问和修改组件自身状态
  2. @Component装饰器:自定义UI组件的声明方式
  3. @Entry装饰器:标记页面的入口组件
  4. build()方法:组件的UI描述方法,返回组件树

8.2 数据流管理要点

  1. 单向数据流:数据变化自动驱动UI更新,无需手动操作DOM
  2. 响应式更新:ArkUI框架会追踪@State变量的使用,自动执行最小粒度更新
  3. 持久化时机:在状态变更后立即保存,保证数据一致性

8.3 常见操作模式

操作 实现方式
添加 unshift添加到数组头部
删除 filter过滤排除目标项
修改 findIndex找到索引后修改
筛选 filter根据条件过滤数组
统计 reduce或filter计算数量

九、总结

备忘录应用虽然功能简单,但完整展示了HarmonyOS ArkUI开发的核心模式:

  1. interface定义数据结构:清晰建模待办事项
  2. @State管理响应式状态:简洁高效的状态管理方案
  3. ForEach渲染动态列表:声明式列表渲染范式
  4. AppStorage持久化数据:可靠的本地存储方案
  5. 组件化设计:CommonTitleBar的复用展示组件化思想

这些核心模式是开发复杂HarmonyOS应用的基础,掌握好备忘录应用的开发模式,能够帮助开发者快速上手HarmonyOS应用开发。

Logo

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

更多推荐