【鸿蒙原生应用开发--ArkUI--004】NotesApp - 笔记应用教程

NotesApp - 笔记应用教程

项目介绍

项目背景

笔记应用是现代移动设备上最实用的工具类应用之一。它展示了如何处理复杂的数据管理、多页面切换、表单输入等高级功能。通过构建一个笔记应用,你将深入理解 HarmonyOS NEXT 中的高级组件使用、数据管理、页面导航等核心概念。

应用场景

笔记应用在日常生活中有着广泛的应用场景:

  • 学习笔记:记录课堂笔记、学习心得
  • 工作记录:会议记录、项目文档
  • 生活日记:日常感悟、旅行日记
  • 灵感收集:创意想法、灵感闪现
  • 知识管理:整理知识体系、构建个人知识库

功能特性

本笔记应用实现了以下功能:

  1. 新建笔记:创建新的笔记,支持标题和内容
  2. 编辑笔记:修改现有笔记的内容
  3. 删除笔记:删除不需要的笔记
  4. 分类管理:按分类(工作/生活/学习/其他)管理笔记
  5. 搜索功能:通过关键词搜索笔记
  6. 列表展示:以卡片形式展示笔记列表

最终效果

应用包含两个主要页面:

  • 列表页面:展示所有笔记,支持搜索和筛选
  • 编辑页面:创建和编辑笔记,支持分类选择

技术栈

  • 开发框架:HarmonyOS NEXT API 23
  • 编程语言:ArkTS
  • UI 框架:ArkUI 声明式 UI
  • 页面切换:条件渲染
  • 数据管理:@State 状态管理
  • 表单组件:TextInput、TextArea

开发环境准备

1. 创建项目

创建一个新的 HarmonyOS NEXT 项目:

方式一:使用 DevEco Studio 创建

  1. 打开 DevEco Studio
  2. 选择 "Create HarmonyOS Project"
  3. 选择 "Empty Ability" 模板
  4. 设置项目名称为 "NotesApp"
  5. 选择 API 版本为 23

方式二:复制模板项目

  1. 复制 project-template 目录
  2. 重命名为 "NotesApp"
  3. 修改配置文件

2. 项目结构

复制代码
NotesApp/
├── AppScope/                    # 应用全局配置
│   ├── app.json5               # 应用级配置
│   └── resources/              # 应用级资源
├── entry/                       # 主模块
│   └── src/main/
│       ├── ets/                # ArkTS 源代码
│       │   ├── entryability/   # UIAbility 入口
│       │   └── pages/          # 页面组件
│       └── resources/          # 资源文件
├── build-profile.json5         # 构建配置
└── oh-package.json5            # 依赖配置

知识点讲解

1. TextArea 组件 - 多行文本输入详解

TextArea 组件用于创建多行文本输入框,适合输入较长的内容,如笔记正文。

基本用法:

typescript 复制代码
TextArea({ placeholder: '请输入内容', text: this.content })
  .width('100%')
  .height(200)
  .fontSize(16)
  .onChange((value: string) => {
    this.content = value;
  })

核心属性:

typescript 复制代码
TextArea({ 
  placeholder: '占位符文本',  // 输入框为空时显示的提示文本
  text: this.content          // 绑定的文本变量
})
  .width('100%')              // 宽度
  .height(200)                // 高度
  .fontSize(16)               // 字体大小
  .fontColor('#333333')       // 字体颜色
  .placeholderColor('#999999') // 占位符颜色
  .backgroundColor('#FFFFFF') // 背景颜色
  .borderRadius(8)            // 圆角
  .padding(16)                // 内边距
  .maxLength(1000)            // 最大输入长度
  .onChange((value: string) => {
    // 输入内容变化时触发
    this.content = value;
  })
  .onSubmit(() => {
    // 按下回车键时触发
    console.info('提交');
  })
  .onFocus(() => {
    // 获得焦点时触发
    console.info('获得焦点');
  })
  .onBlur(() => {
    // 失去焦点时触发
    console.info('失去焦点');
  })

与 TextInput 的区别:

  • TextInput:单行输入,适合简短内容
  • TextArea:多行输入,适合长文本

在笔记应用中的应用:

typescript 复制代码
TextArea({ placeholder: '开始写作...', text: this.content })
  .width('100%')
  .layoutWeight(1)  // 占据剩余空间
  .fontSize(16)
  .backgroundColor('#FFFBEB')  // 淡黄色背景,模拟纸张
  .borderRadius(16)
  .margin(16)
  .padding(16)
  .onChange((value: string) => {
    this.content = value;
  })

2. @Builder 装饰器 - 自定义构建函数详解

@Builder 装饰器用于定义可复用的 UI 构建函数,类似于 React 中的 render 函数。

基本语法:

typescript 复制代码
@Builder 函数名() {
  // UI 组件
}

使用场景:

typescript 复制代码
@Component
struct MyComponent {
  build() {
    Column() {
      this.Header()  // 调用 Builder 函数
      this.Content()
      this.Footer()
    }
  }

  @Builder Header() {
    Row() {
      Text('标题')
    }
  }

  @Builder Content() {
    Column() {
      Text('内容')
    }
  }

  @Builder Footer() {
    Row() {
      Text('底部')
    }
  }
}

带参数的 Builder:

typescript 复制代码
@Builder ButtonComponent(text: string, color: string) {
  Button(text)
    .backgroundColor(color)
    .onClick(() => {
      console.info(`点击了 ${text}`);
    })
}

// 使用
this.ButtonComponent('确定', '#6366F1')
this.ButtonComponent('取消', '#EF4444')

在笔记应用中的应用:

typescript 复制代码
@Entry
@Component
struct Index {
  @State showEditor: boolean = false;

  build() {
    Column() {
      if (this.showEditor) {
        this.NoteEditor()  // 显示编辑器
      } else {
        this.NoteList()    // 显示列表
      }
    }
  }

  @Builder NoteList() {
    // 列表页面内容
    Column() {
      // 头部
      // 搜索栏
      // 笔记列表
    }
  }

  @Builder NoteEditor() {
    // 编辑器页面内容
    Column() {
      // 头部
      // 标题输入
      // 内容输入
    }
  }
}

3. 条件渲染 - 页面切换详解

使用 if/else 实现页面切换,这是单页面应用常用的导航方式。

基本语法:

typescript 复制代码
Column() {
  if (condition) {
    // 条件为真时显示
    Page1()
  } else {
    // 条件为假时显示
    Page2()
  }
}

在笔记应用中的应用:

typescript 复制代码
@Entry
@Component
struct Index {
  @State showEditor: boolean = false;
  @State editingNote: Note | null = null;

  build() {
    Column() {
      if (this.showEditor) {
        // 显示编辑器页面
        this.NoteEditor()
      } else {
        // 显示列表页面
        this.NoteList()
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F8FAFC')
  }

  // 切换到编辑器
  openEditor(note?: Note): void {
    if (note) {
      this.editingNote = note;
      this.title = note.title;
      this.content = note.content;
    } else {
      this.editingNote = null;
      this.title = '';
      this.content = '';
    }
    this.showEditor = true;
  }

  // 返回列表
  goBack(): void {
    this.showEditor = false;
    this.editingNote = null;
  }
}

页面切换动画:

typescript 复制代码
Column() {
  if (this.showEditor) {
    this.NoteEditor()
      .transition(TransitionType.Slide)  // 滑动动画
  } else {
    this.NoteList()
  }
}

4. 数组排序 - sort详解

使用 sort 方法对数组进行排序,可以按照自定义规则排序。

基本语法:

typescript 复制代码
array.sort((a, b) => {
  // 返回负数:a 排在 b 前面
  // 返回正数:b 排在 a 前面
  // 返回 0:保持原顺序
})

排序示例:

typescript 复制代码
// 数字升序
[3, 1, 4, 1, 5].sort((a, b) => a - b)
// 结果: [1, 1, 3, 4, 5]

// 数字降序
[3, 1, 4, 1, 5].sort((a, b) => b - a)
// 结果: [5, 4, 3, 1, 1]

// 字符串排序
['banana', 'apple', 'cherry'].sort()
// 结果: ['apple', 'banana', 'cherry']

在笔记应用中的应用:

typescript 复制代码
// 按置顶状态排序(置顶的排在前面)
return filtered.sort((a, b) => {
  if (a.pinned && !b.pinned) return -1;  // a置顶,b不置顶,a排前面
  if (!a.pinned && b.pinned) return 1;   // a不置顶,b置顶,b排前面
  return 0;  // 保持原顺序
});

// 按日期排序(最新的在前面)
return filtered.sort((a, b) => {
  return new Date(b.date).getTime() - new Date(a.date).getTime();
});

// 按标题排序
return filtered.sort((a, b) => {
  return a.title.localeCompare(b.title);
});

5. 字符串方法详解

toLowerCase - 转换为小写:

typescript 复制代码
const str = 'Hello World';
const lower = str.toLowerCase();
// 结果: 'hello world'

includes - 检查是否包含:

typescript 复制代码
const str = 'Hello World';
const hasHello = str.includes('Hello');  // true
const hasHi = str.includes('Hi');        // false

trim - 去除首尾空格:

typescript 复制代码
const str = '  Hello World  ';
const trimmed = str.trim();
// 结果: 'Hello World'

在搜索功能中的应用:

typescript 复制代码
// 搜索过滤
if (this.searchQuery) {
  const query = this.searchQuery.toLowerCase();  // 转换为小写
  filtered = filtered.filter(n =>
    n.title.toLowerCase().includes(query) ||    // 标题包含搜索词
    n.content.toLowerCase().includes(query)     // 内容包含搜索词
  );
}

6. 数组方法详解

filter - 过滤数组:

typescript 复制代码
// 过滤出符合条件的元素
const completed = todos.filter(todo => todo.completed);
const active = todos.filter(todo => !todo.completed);

findIndex - 查找索引:

typescript 复制代码
// 查找元素的索引
const index = todos.findIndex(todo => todo.id === targetId);
if (index >= 0) {
  // 找到元素
}

find - 查找元素:

typescript 复制代码
// 查找元素
const todo = todos.find(todo => todo.id === targetId);

map - 映射数组:

typescript 复制代码
// 提取所有标题
const titles = notes.map(note => note.title);

在笔记删除中的应用:

typescript 复制代码
// 删除笔记
deleteNote(noteId: number): void {
  this.notes = this.notes.filter(n => n.id !== noteId);
}

// 编辑笔记
updateNote(noteId: number, title: string, content: string): void {
  const index = this.notes.findIndex(n => n.id === noteId);
  if (index >= 0) {
    this.notes[index].title = title;
    this.notes[index].content = content;
    this.notes[index].date = new Date().toLocaleDateString();
  }
}

7. 非空断言 - !详解

当确定一个值不为 null 或 undefined 时,可以使用非空断言操作符 !

使用场景:

typescript 复制代码
// 可能为 null 的变量
let editingNote: Note | null = null;

// 在使用时确定不为 null
const index = notes.findIndex(n => n.id === editingNote!.id);
// editingNote! 表示确定 editingNote 不为 null

注意事项:

  • 只在确定不为 null 时使用
  • 错误使用可能导致运行时错误
  • 可以使用可选链 ?. 替代

替代方案:

typescript 复制代码
// 使用可选链
const index = notes.findIndex(n => n.id === editingNote?.id);

// 使用条件判断
if (editingNote) {
  const index = notes.findIndex(n => n.id === editingNote.id);
}

8. Scroll 横向滚动详解

创建横向滚动的标签栏,适合展示多个筛选选项。

基本用法:

typescript 复制代码
Scroll() {
  Row() {
    Text('标签1')
      .margin({ right: 8 })
    Text('标签2')
      .margin({ right: 8 })
    Text('标签3')
  }
}
.scrollable(ScrollDirection.Horizontal)  // 设置为横向滚动
.width('100%')

在笔记应用中的应用:

typescript 复制代码
Scroll() {
  Row() {
    Text('全部')
      .fontSize(14)
      .fontWeight(this.selectedCategory === 0 ? FontWeight.Medium : FontWeight.Regular)
      .fontColor(this.selectedCategory === 0 ? '#8B5CF6' : '#64748B')
      .padding({ left: 16, right: 16, top: 8, bottom: 8 })
      .backgroundColor(this.selectedCategory === 0 ? '#EDE9FE' : '#FFFFFF')
      .borderRadius(999)
      .margin({ right: 8 })
      .onClick(() => { this.selectedCategory = 0 })

    Text('工作')
      .fontSize(14)
      .fontWeight(this.selectedCategory === 1 ? FontWeight.Medium : FontWeight.Regular)
      .fontColor(this.selectedCategory === 1 ? '#8B5CF6' : '#64748B')
      .padding({ left: 16, right: 16, top: 8, bottom: 8 })
      .backgroundColor(this.selectedCategory === 1 ? '#EDE9FE' : '#FFFFFF')
      .borderRadius(999)
      .margin({ right: 8 })
      .onClick(() => { this.selectedCategory = 1 })

    // ... 更多标签
  }
}
.scrollable(ScrollDirection.Horizontal)
.width('100%')
.margin({ bottom: 16 })

9. 分类颜色映射详解

使用方法返回不同的颜色值,实现分类的颜色区分。

基本实现:

typescript 复制代码
getCategoryColor(category: number): string {
  switch (category) {
    case 1: return '#EF4444';  // 红色 - 工作
    case 2: return '#F59E0B';  // 黄色 - 生活
    case 3: return '#10B981';  // 绿色 - 学习
    case 4: return '#3B82F6';  // 蓝色 - 其他
    default: return '#94A3B8'; // 灰色 - 默认
  }
}

在笔记卡片中的应用:

typescript 复制代码
Row() {
  // 分类指示器
  Column()
    .width(4)
    .height(40)
    .backgroundColor(this.getCategoryColor(note.category))
    .borderRadius(2)

  // 笔记内容
  Column() {
    // ...
  }
}

10. 多条件过滤详解

实现多条件组合过滤,支持分类筛选和搜索。

完整实现:

typescript 复制代码
getFilteredNotes(): Note[] {
  let filtered = this.notes;

  // 1. 按分类过滤
  if (this.selectedCategory > 0) {
    filtered = filtered.filter(n => n.category === this.selectedCategory);
  }

  // 2. 按搜索词过滤
  if (this.searchQuery) {
    const query = this.searchQuery.toLowerCase();
    filtered = filtered.filter(n =>
      n.title.toLowerCase().includes(query) ||
      n.content.toLowerCase().includes(query)
    );
  }

  // 3. 排序(可选)
  filtered = filtered.sort((a, b) => {
    // 按日期降序
    return new Date(b.date).getTime() - new Date(a.date).getTime();
  });

  return filtered;
}

过滤流程:

  1. 获取所有笔记
  2. 按分类筛选
  3. 按搜索词筛选
  4. 排序
  5. 返回结果

完整代码解析

数据结构定义

typescript 复制代码
// 定义笔记数据结构
interface Note {
  id: number;        // 唯一标识
  title: string;     // 标题
  content: string;   // 内容
  date: string;      // 日期
  category: number;  // 分类:1-工作,2-生活,3-学习,4-其他
}

页面组件定义

typescript 复制代码
@Entry
@Component
struct Index {
  @State notes: Note[] = [];              // 笔记数组
  @State nextId: number = 1;              // 下一个ID
  @State showEditor: boolean = false;     // 是否显示编辑器
  @State editingNote: Note | null = null; // 正在编辑的笔记
  @State title: string = '';              // 编辑中的标题
  @State content: string = '';            // 编辑中的内容
  @State searchQuery: string = '';        // 搜索词
  @State selectedCategory: number = 0;    // 选中的分类

  build() {
    Column() {
      if (this.showEditor) {
        this.NoteEditor()  // 编辑器页面
      } else {
        this.NoteList()    // 列表页面
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F8FAFC')
  }
}

列表页面 - NoteList

typescript 复制代码
@Builder NoteList() {
  Column() {
    // 头部区域
    Row() {
      Column() {
        Text('我的笔记')
          .fontSize(28)
          .fontWeight(FontWeight.Bold)
          .fontColor('#1E293B')

        Text(`${this.notes.length} 条笔记`)
          .fontSize(12)
          .fontColor('#64748B')
          .margin({ top: 4 })
      }
      .alignItems(HorizontalAlign.Start)

      Blank()

      Button('+ 新建')
        .height(44)
        .fontSize(14)
        .fontWeight(FontWeight.Medium)
        .backgroundColor('#8B5CF6')
        .fontColor('#FFFFFF')
        .borderRadius(12)
        .onClick(() => {
          this.showEditor = true;
          this.editingNote = null;
          this.title = '';
          this.content = '';
          this.selectedCategory = 0;
        })
    }
    .width('100%')
    .padding({ bottom: 16 })

    // 搜索栏
    TextInput({ placeholder: '搜索笔记...', text: this.searchQuery })
      .width('100%')
      .height(44)
      .fontSize(16)
      .backgroundColor('#FFFFFF')
      .borderRadius(12)
      .margin({ bottom: 16 })
      .onChange((value: string) => {
        this.searchQuery = value;
      })

    // 分类筛选标签
    Scroll() {
      Row() {
        this.CategoryChip('全部', 0)
        this.CategoryChip('工作', 1)
        this.CategoryChip('生活', 2)
        this.CategoryChip('学习', 3)
        this.CategoryChip('其他', 4)
      }
    }
    .scrollable(ScrollDirection.Horizontal)
    .width('100%')
    .margin({ bottom: 16 })

    // 笔记列表
    if (this.getFilteredNotes().length === 0) {
      // 空状态
      Column() {
        Text('📝')
          .fontSize(48)
          .margin({ bottom: 16 })

        Text(this.searchQuery ? '没有找到匹配的笔记' : '暂无笔记')
          .fontSize(16)
          .fontColor('#94A3B8')

        if (!this.searchQuery) {
          Text('点击右上角按钮创建第一条笔记')
            .fontSize(12)
            .fontColor('#94A3B8')
            .margin({ top: 8 })
        }
      }
      .layoutWeight(1)
      .justifyContent(FlexAlign.Center)
    } else {
      // 笔记列表
      List() {
        ForEach(this.getFilteredNotes(), (note: Note) => {
          ListItem() {
            this.NoteCard(note)
          }
          .margin({ bottom: 8 })
        }, (note: Note) => note.id.toString())
      }
      .layoutWeight(1)
      .width('100%')
    }
  }
  .padding(16)
}

分类标签组件

typescript 复制代码
@Builder CategoryChip(label: string, category: number) {
  Text(label)
    .fontSize(14)
    .fontWeight(this.selectedCategory === category ? FontWeight.Medium : FontWeight.Regular)
    .fontColor(this.selectedCategory === category ? '#8B5CF6' : '#64748B')
    .padding({ left: 16, right: 16, top: 8, bottom: 8 })
    .backgroundColor(this.selectedCategory === category ? '#EDE9FE' : '#FFFFFF')
    .borderRadius(999)
    .margin({ right: 8 })
    .onClick(() => {
      this.selectedCategory = category;
    })
}

笔记卡片组件

typescript 复制代码
@Builder NoteCard(note: Note) {
  Row() {
    // 分类指示器
    Column()
      .width(4)
      .height(40)
      .backgroundColor(this.getCategoryColor(note.category))
      .borderRadius(2)

    // 笔记内容
    Column() {
      Text(note.title)
        .fontSize(18)
        .fontWeight(FontWeight.Medium)
        .fontColor('#1E293B')
        .maxLines(1)
        .textOverflow({ overflow: TextOverflow.Ellipsis })

      Text(note.content)
        .fontSize(14)
        .fontColor('#64748B')
        .maxLines(2)
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .margin({ top: 4 })

      Row() {
        Text(note.date)
          .fontSize(12)
          .fontColor('#94A3B8')

        Blank()

        Button('编辑')
          .height(28)
          .fontSize(12)
          .backgroundColor('#EDE9FE')
          .fontColor('#8B5CF6')
          .onClick(() => {
            this.editingNote = note;
            this.title = note.title;
            this.content = note.content;
            this.selectedCategory = note.category;
            this.showEditor = true;
          })

        Button('删除')
          .height(28)
          .fontSize(12)
          .backgroundColor('#FEE2E2')
          .fontColor('#EF4444')
          .margin({ left: 8 })
          .onClick(() => {
            this.notes = this.notes.filter(n => n.id !== note.id);
          })
      }
      .width('100%')
      .margin({ top: 8 })
    }
    .layoutWeight(1)
    .margin({ left: 12 })
  }
  .width('100%')
  .padding(16)
  .backgroundColor('#FFFFFF')
  .borderRadius(16)
}

编辑器页面 - NoteEditor

typescript 复制代码
@Builder NoteEditor() {
  Column() {
    // 编辑器头部
    Row() {
      Button('← 返回')
        .height(44)
        .fontSize(14)
        .backgroundColor('#F3F4F6')
        .fontColor('#64748B')
        .onClick(() => {
          this.showEditor = false;
        })

      Text(this.editingNote ? '编辑笔记' : '新建笔记')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .fontColor('#1E293B')
        .margin({ left: 16 })

      Blank()

      Button('保存')
        .height(44)
        .fontSize(14)
        .fontWeight(FontWeight.Medium)
        .backgroundColor('#8B5CF6')
        .fontColor('#FFFFFF')
        .borderRadius(12)
        .onClick(() => {
          this.saveNote();
        })
    }
    .width('100%')
    .padding(16)

    // 分类选择
    Scroll() {
      Row() {
        this.CategoryChip('工作', 1)
        this.CategoryChip('生活', 2)
        this.CategoryChip('学习', 3)
        this.CategoryChip('其他', 4)
      }
      .padding({ left: 16, right: 16 })
    }
    .scrollable(ScrollDirection.Horizontal)
    .width('100%')
    .margin({ bottom: 16 })

    // 标题输入
    TextInput({ placeholder: '输入标题...', text: this.title })
      .width('100%')
      .height(56)
      .fontSize(20)
      .fontWeight(FontWeight.Medium)
      .backgroundColor('#FFFFFF')
      .margin({ left: 16, right: 16 })
      .onChange((value: string) => {
        this.title = value;
      })

    // 内容输入
    TextArea({ placeholder: '开始写作...', text: this.content })
      .width('100%')
      .layoutWeight(1)
      .fontSize(16)
      .backgroundColor('#FFFBEB')
      .borderRadius(16)
      .margin(16)
      .padding(16)
      .onChange((value: string) => {
        this.content = value;
      })
  }
  .width('100%')
  .height('100%')
  .backgroundColor('#FFFFFF')
}

核心方法

typescript 复制代码
// 保存笔记
saveNote(): void {
  if (this.title.trim() && this.content.trim()) {
    if (this.editingNote) {
      // 编辑现有笔记
      const index = this.notes.findIndex(n => n.id === this.editingNote!.id);
      if (index >= 0) {
        this.notes[index].title = this.title;
        this.notes[index].content = this.content;
        this.notes[index].date = new Date().toLocaleDateString();
        this.notes[index].category = this.selectedCategory;
      }
    } else {
      // 创建新笔记
      this.notes.push({
        id: this.nextId++,
        title: this.title,
        content: this.content,
        date: new Date().toLocaleDateString(),
        category: this.selectedCategory || 4  // 默认为"其他"
      });
    }
    this.showEditor = false;  // 返回列表页面
  }
}

// 获取筛选后的笔记
getFilteredNotes(): Note[] {
  let filtered = this.notes;

  // 按分类过滤
  if (this.selectedCategory > 0) {
    filtered = filtered.filter(n => n.category === this.selectedCategory);
  }

  // 按搜索词过滤
  if (this.searchQuery) {
    const query = this.searchQuery.toLowerCase();
    filtered = filtered.filter(n =>
      n.title.toLowerCase().includes(query) ||
      n.content.toLowerCase().includes(query)
    );
  }

  return filtered;
}

// 获取分类颜色
getCategoryColor(category: number): string {
  switch (category) {
    case 1: return '#EF4444';  // 红色 - 工作
    case 2: return '#F59E0B';  // 黄色 - 生活
    case 3: return '#10B981';  // 绿色 - 学习
    case 4: return '#3B82F6';  // 蓝色 - 其他
    default: return '#94A3B8'; // 灰色 - 默认
  }
}

常见问题与解决方案

问题1:搜索性能问题

问题描述:当笔记数量很多时,搜索可能卡顿。

解决方案

typescript 复制代码
// 使用防抖优化搜索
private searchTimer: number = 0;

onSearchChange(value: string): void {
  clearTimeout(this.searchTimer);
  this.searchTimer = setTimeout(() => {
    this.searchQuery = value;
  }, 300);
}

问题2:数据丢失

问题描述:应用关闭后数据丢失。

解决方案:使用 Preferences 或数据库存储数据。

问题3:长文本显示

问题描述:长文本可能超出显示区域。

解决方案

typescript 复制代码
Text(note.content)
  .maxLines(2)
  .textOverflow({ overflow: TextOverflow.Ellipsis })

扩展学习

1. 添加更多功能

  • 富文本编辑
  • 图片插入
  • 语音笔记
  • 笔记加密

2. 优化用户体验

  • 手势操作
  • 动画效果
  • 主题切换
  • 字体大小调整

3. 数据管理

  • 数据导出
  • 云同步
  • 回收站
  • 版本历史

总结

通过本教程,你学习了:

  1. TextArea - 多行文本输入组件
  2. @Builder - 自定义构建函数
  3. 条件渲染 - 页面切换
  4. 数组排序 - sort 方法
  5. 字符串方法 - toLowerCase、includes、trim
  6. 数组方法 - filter、findIndex、find、map
  7. 非空断言 - ! 操作符
  8. 横向滚动 - ScrollDirection.Horizontal
  9. 多条件过滤 - 组合过滤逻辑
  10. 分类颜色映射 - switch/case 返回值

这些知识点构成了 HarmonyOS NEXT 中复杂应用开发的基础,掌握它们后,你将能够构建更完整的笔记、日记、文档管理等应用。

相关推荐
想你依然心痛2 小时前
HarmonyOS 6(API 23)智能体驱动的沉浸式AR深海科考探索舱
华为·ar·harmonyos·智能体
Goway_Hui3 小时前
【鸿蒙原生应用开发--ArkUI--002】CalculatorApp - 计算器应用教程
华为·harmonyos
Goway_Hui4 小时前
【鸿蒙原生应用开发--ArkUI--006】WeatherApp - 天气应用教程
华为·harmonyos
不羁的木木4 小时前
HarmonyOS文件基础服务(Core File Kit)实战演练03-文件增删改查与目录操作
pytorch·华为·harmonyos
不羁的木木5 小时前
ArkWeb实战学习笔记02-环境搭建与基础配置
笔记·学习·harmonyos
技术路线图5 小时前
鸿蒙系统支付宝更新教程:华为应用市场操作步骤详解
华为·harmonyos
GitCode官方5 小时前
开源鸿蒙跨平台直播|15场·10大框架|首期:跨平台不是“权衡之选“,而是基础设施
人工智能·华为·开源·harmonyos·atomgit
互联网散修5 小时前
鸿蒙实战:图像滤镜工坊——ColorFilter 颜色矩阵与动态调节
harmonyos·图片颜色滤镜