OpenHarmony如何实现备忘录增删改查功能

介绍

本篇Codelab基于用户首选项,实现了备忘录新增、更新、删除以及查找等功能。效果如图所示:

相关概念

  • 用户首选项:提供Key-Value键值型的数据处理能力,应用持久化轻量级数据,并对其修改和查询。
  • Navigator:路由容器组件,支持路由跳转以及子组件嵌入。

环境搭建

软件要求

  • DevEco Studio版本:DevEco Studio 3.1 Release。
  • OpenHarmony SDK版本:API version 9。

硬件要求

  • 开发板类型:润和RK3568开发板。
  • OpenHarmony系统:3.2 Release。

环境搭建

完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:

  1. 获取OpenHarmony系统版本:标准系统解决方案(二进制)。以3.2 Release版本为例:

2.搭建烧录环境。

  • 完成DevEco Device Tool的安装
    • 完成RK3568开发板的烧录

3.搭建开发环境。

  • 开始前请参考工具准备,完成DevEco Studio的安装和开发环境配置。
    • 开发环境配置完成后,请参考使用工程向导创建工程(模板选择“Empty Ability”)。
    • 工程创建完成后,选择使用真机进行调测。

代码结构解读

本篇Codelab只对核心代码进行讲解

├──entry/src/main/ets           // 代码区
│  ├──common
│  │  ├──constants
│  │  │  ├──CommonConstants.ets // 常量类 
│  │  │  └──StyleConstants.ets  // 样式常量类 
│  │  └──utils
│  │     ├──Format.ets          // 日期格式化函数
│  │     └──Logger.ets          // 日志打印类
│  ├──entryability
│  │  └──EntryAbility.ts        // 程序入口类
│  ├──model
│  │  └──NotesDataModel.ets     // 备忘录方法类
│  ├──pages
│  │  ├──NoteHomePage.ets       // 备忘录主页面(列表页)
│  │  └──NotesDetail.ets        // 备忘录详情页	
│  ├──view
│  │  ├──BottomBar.ets          // 备忘录编辑页底部栏
│  │  ├──ConfirmDialog.ets      // 自定义弹窗
│  │  └──MemoItem.ets           // 自定义备忘录列表组件
│  └──viewmodel
│     └──NotesInfoViewModel.ets // 备忘录默认数据实体	
└──entry/src/main/resources     // 资源文件目录

备忘录初始化

在这个章节中,需要实现备忘录数据的初始化,并且通过List组件将其渲染出来。效果如图所示:

在saveDefaultData方法中先通过getPreferences方法获取preferences实例,然后调用has方法查找数据库中是否存在“noteIdArr”这个key值,如果不存在调用实例的put方法将noteIdArr以及备忘录数据写入,最后通过flush方法进行数据持久化。

// NotesDataModel.ets
import dataStorage from '@ohos.data.preferences';
...
class NotesDataModel {
  private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;

  /**
   * 写入备忘录数据.
   */
  async saveDefaultData() {
    try {
      let preferences = await dataStorage.getPreferences(this.context, CommonConstants.PREFERENCE_INSTANCE_NAME);
      let isExist = await preferences.has(CommonConstants.PREFERENCE_NOTE_KEY);
      if (!isExist) {
        preferences.put(CommonConstants.PREFERENCE_NOTE_KEY, JSON.stringify(noteIdArray));
        preferences.flush();
        notesItemArr.forEach((item: NotesInfoBean) => {
          let notes: NotesInfoBean = item;
          let res = preferences.put(item.noteId, JSON.stringify(notes));
          preferences.flush();
          res.then(() => {
            Logger.info('Put the value successfully.' + item.noteId);
          }).catch((err: Error) => {
            Logger.error(`Put the value failed with err: ${err}`);
          })
        })
      }
    } catch (err) {
      Logger.error(`Failed to get preferences. Error = ${err}`);
    }
  }

  /**
   * 基于笔记类型获取对应备忘录数据.
   *
   * @param flag the folder type.
   * @param allNotes all of notes.
   * @returns subNotes.
   */
  getSelectNotes(flag: FolderType, allNotes: Array<NotesInfoBean>): Array<NotesInfoBean> {
    return allNotes.filter((item: NotesInfoBean) => item.folder === flag);
  }
}

在NoteHomePage.ets文件中调用saveDefaultData函数先将本地数据写入数据库,再调用实例的get方法进行查询操作。

// NoteHomePage.ets
import dataStorage from '@ohos.data.preferences';
...
@Entry
@Component
struct NoteHomePage {
  @State folderType: Resource = $r('app.string.notes_all');
  @State allNotes: Array<NotesInfoBean> = [];
  @State selectNotes: Array<NotesInfoBean> = this.allNotes.sort();
  private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
  ...
  build() {
    Column() {
      ...
      List({ space: StyleConstants.MEMO_LIST_SPACE }) {
        ForEach(this.selectNotes, (item: NotesInfoBean) => {
          ListItem() {
            MemoItem({ noteItem: item })
          }
        }, (item: NotesInfoBean) => JSON.stringify(item))
      }
      .margin({ top: $r('app.float.list_container_margin') })
      .height(StyleConstants.NOTE_CONTENT_HEIGHT)
      .width(StyleConstants.FULL_WIDTH)
    }
    .backgroundColor($r('app.color.page_background'))
    .height(StyleConstants.FULL_HEIGHT)
  }

  onPageShow() {
    this.getAllNotes();
  }

  async getAllNotes() {
    await NotesDataModel.saveDefaultData();
    try {
      let preferences = await dataStorage.getPreferences(this.context, CommonConstants.PREFERENCE_INSTANCE_NAME);
      let noteIds = await preferences.get(CommonConstants.PREFERENCE_NOTE_KEY, '');
      while (this.allNotes.length >= 1) {
        this.allNotes.pop();
      }
      JSON.parse(noteIds.toString()).forEach(async (item: NotesInfoBean) => {
        let note = await preferences.get(item.noteId, '');
        this.allNotes.push(JSON.parse(note.toString()));
      })
    } catch (err) {
      Logger.error('Get the value of noteIdArr failed with err:', err);
    }
  }
}

新增备忘录

此章节介绍新增备忘录功能,点击列表页右上角加号进入编辑页,支持输入标题、备忘录内容以及添加图片。效果如图所示:

首先在列表页NoteHomePage.ets中添加跳转逻辑,设置路由参数params,其中operationType字段代表此次操作是新增还是修改。

// NoteHomePage.ets
Navigator({ target: 'pages/NotesDetail', type: NavigationType.Replace }) {
  Row() {
    Image($rawfile('ic_title_add.svg'))
      ...
  }
  .margin({ right: $r('app.float.note_add_margin') })
}
.params({
  notesInfo: {
    'noteId': new Date().getTime().toString(),
    'title': '',
    'folder': FolderType.Personal,
    'content': '',
    'imageArr': [],
    'time': new Date().toTimeString().split(' ')[0],
    'isFavorite': false
  },
  operationType: CommonConstants.ADD_NOTE
})

进入编辑页NotesDetail.ets后可以输入标题、内容以及选择对应的笔记类型等,确认保存后备忘录数据实时更新。

// NotesDetail.ets
build() {
  ...
  TextInput({
    text: this.notesInfo.title != '' ? this.notesInfo.title : '',
    placeholder: this.notesInfo.title != '' ? '' : $r('app.string.note_title_placeholder')
  })
    ...
    .onChange((value: string) => {
      if (value !== this.notesInfo.title) {
        this.notesInfo.title = value;
        this.isDataChanged = true;
      }
    })
  ...
  TextArea({
    text: this.notesInfo.content !== '' ? this.notesInfo.content : '',
    placeholder: this.notesInfo.content !== '' ? '' : $r('app.string.note_content_placeholder')
  })
    .onChange((value: string) => {
      if (value !== this.notesInfo.content) {
        this.notesInfo.content = value;
        this.isDataChanged = true;
      }
    })
  ...
}

onBackPress() {
  if (this.isDataChanged || this.notesFolder !== this.notesInfo.folder || this.isCollectChange) {
    this.saveDialogController.open();
  } else {
    router.replaceUrl({
      url: 'pages/NoteHomePage'
    });
  }
  return true;
}

// ConfirmDialog.ets
if (this.type === CommonConstants.SAVE_DIALOG) {
  this.confirm = async () => {
    let preferences = await dataStorage.getPreferences(this.context, CommonConstants.PREFERENCE_INSTANCE_NAME);
    // 保存备忘录数据实时更新
    if (this.operationType === CommonConstants.ADD_NOTE) {
      this.noteIdArray.push(new NoteIdBean(this.notesInfo.noteId));
      preferences.put(CommonConstants.PREFERENCE_NOTE_KEY, JSON.stringify(this.noteIdArray));
      preferences.flush();
    }
    let newNotes = this.notesInfo;
    await preferences.put(this.notesInfo.noteId, JSON.stringify(newNotes));
    await preferences.flush();
    router.replaceUrl({
      url: 'pages/NoteHomePage'
    });
  }
}

更新备忘录

此章节介绍更新数据库操作,与新增备忘录逻辑类似。效果如图所示:

首先在NotesDetail.ets中设置isDataChange和isCollectChange属性,来表示对应noteId的备忘录数据是否已更改。如果isDataChange或者isCollectChange为true表示已更改,在返回列表页时会拉起确认弹窗,确认保存后执行put方法去更改备忘录数据。

// NotesDetail.ets
build() {
  Column() {
    ...
    Stack({ alignContent: Alignment.Bottom }) {
      Scroll(this.scroller) {
        Column() {
          TextInput({
            text: this.notesInfo.title != '' ? this.notesInfo.title : '',
            placeholder: this.notesInfo.title != '' ? '' : $r('app.string.note_title_placeholder')
          })
            ...
            .onChange((value: string) => {
              if (value !== this.notesInfo.title) {
                this.notesInfo.title = value;
                this.isDataChanged = true;
              }
            })
          ...
          TextArea({
            text: this.notesInfo.content !== '' ? this.notesInfo.content : '',
            placeholder: this.notesInfo.content !== '' ? '' : $r('app.string.note_content_placeholder')
          })
            .onChange((value: string) => {
              if (value !== this.notesInfo.content) {
                this.notesInfo.content = value;
                this.isDataChanged = true;
              }
            })
          ...
        }
      }
      ...
      BottomBar({
        imageArr: $imageArr,
        notesInfo: $notesInfo,
        operationType: $operationType,
        noteIdArray: $noteIdArray,
        isDataChanged: $isDataChanged
      })
    }
    ...
  }
  .height(StyleConstants.FULL_HEIGHT)
  .backgroundColor($r('app.color.white_color'))
}
...
onBackPress() {
  if (this.isDataChanged || this.notesFolder !== this.notesInfo.folder || this.isCollectChange) {
    this.saveDialogController.open();
  } else {
    router.replaceUrl({
      url: 'pages/NoteHomePage'
    });
  }
  return true;
}

// BottomBar.ets
// 点击收藏
this.clickCollect = () => {
  this.notesInfo.isFavorite = !this.notesInfo.isFavorite;
  this.isFavorite = !this.isFavorite;
  this.collectImgSrc = this.notesInfo.isFavorite ?
    'ic_bottom_star_selected.svg' : 'ic_bottom_star_normal.svg';
}
...
// 点击插入图片
this.clickAddPicture = () => {
  this.imageSrc = this.chooseImage();
  if (this.imageSrc === '') {
    prompt.showToast({
      message: 'Not anymore pictures'
    });
  } else {
    this.imageArr = this.notesInfo.imageArr;
    this.imageArr.push(this.imageSrc);
    this.isDataChanged = true;
  }
}

// ConfirmDialog.ets
if (this.type === CommonConstants.SAVE_DIALOG) {
  this.confirm = async () => {
    let preferences = await dataStorage.getPreferences(this.context, CommonConstants.PREFERENCE_INSTANCE_NAME);
    if (this.operationType === CommonConstants.ADD_NOTE) {
      this.noteIdArray.push(new NoteIdBean(this.notesInfo.noteId));
      preferences.put(CommonConstants.PREFERENCE_NOTE_KEY, JSON.stringify(this.noteIdArray));
      preferences.flush();
    }
    // 保存备忘录数据实时更新
    let newNotes = this.notesInfo;
    await preferences.put(this.notesInfo.noteId, JSON.stringify(newNotes));
    await preferences.flush();
    router.replaceUrl({
      url: 'pages/NoteHomePage'
    });
  }
}

删除备忘录

上述章节介绍了数据库的新增与更新,此章节来介绍删除操作。效果如图所示:

在BottomBar.ets中点击删除按钮,弹出自定义弹窗选择“是否删除”。在ConfirmDialog.ets中添加删除逻辑,删除操作会调用preferences实例的delete方法,将对应noteId的备忘录数据从数据库中删除,最后执行实例的flush方法实现持久化。

// BottomBar.ets
export default struct BottomBar {
  ...
  deleteDialogController: CustomDialogController = new CustomDialogController({
    builder: ConfirmDialog({
      notesInfo: $notesInfo,
      operationType: $operationType,
      noteIdArray: $noteIdArray,
      type: CommonConstants.DELETE_DIALOG
    }),
    autoCancel: true,
    alignment: DialogAlignment.Bottom,
    offset: { dx: $r('app.float.dialog_offset_x'), dy: $r('app.float.dialog_margin_bottom') }
  });
  ...
  build() {
    ...
    Column() {
      Image($r('app.media.ic_bottom_delete'))
        .width($r('app.float.ic_bottom_picture_size'))
        .aspectRatio(1)
      Text($r('app.string.delete_note'))
        .fontSize($r('app.float.font_size_smallest'))
        .margin({ top: $r('app.float.bottom_txt_margin') })
    }
    .onClick(() => {
      this.clickDelete = () => {
        if (this.operationType === CommonConstants.MODIFY_NOTE) {
          this.deleteDialogController.open();
        } else {
          prompt.showToast({
            message: 'The addition operation cannot be deleted'
          });
        }
      }
      this.clickDelete();
    })
    ...
  }
  ...
}

// ConfirmDialog.ets
if (this.type === CommonConstants.SAVE_DIALOG) {
  ...
} else {
  // 删除备忘录数据
  this.confirm = async () => {
    let preferences = await dataStorage.getPreferences(this.context, CommonConstants.PREFERENCE_INSTANCE_NAME);
    await preferences.delete(this.notesInfo.noteId);
    await preferences.flush();
    router.replaceUrl({
      url: 'pages/NoteHomePage'
    });
  }
}
this.confirm();
})

总结

您已经完成了本次Codelab的学习,并了解到以下知识点:

  1. 使用preferences用户首选项实现一个简易备忘录。
  2. 使用Navigator实现路由跳转。

为了帮助大家更深入有效的学习到鸿蒙开发知识点,小编特意给大家准备了一份全套最新版的HarmonyOS NEXT学习资源,获取完整版方式请点击→《HarmonyOS教学视频

HarmonyOS教学视频

鸿蒙语法ArkTS、TypeScript、ArkUI等…视频教程

鸿蒙生态应用开发白皮书V2.0PDF:

获取完整版白皮书方式请点击→《鸿蒙生态应用开发白皮书V2.0PDF

在这里插入图片描述

鸿蒙 (Harmony OS)开发学习手册

一、入门必看

  1. 应用开发导读(ArkTS)
  2. .……

在这里插入图片描述


二、HarmonyOS 概念

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全

在这里插入图片描述

三、如何快速入门?鸿蒙基础入门学习指南

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. .……

在这里插入图片描述


四、开发基础知识

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. .……

在这里插入图片描述


五、基于ArkTS 开发

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 7.网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. .……

在这里插入图片描述


更多了解更多鸿蒙开发的相关知识可以参考:《鸿蒙 (Harmony OS)开发学习手册

相关推荐

  1. VUE实现增删功能

    2024-03-25 17:46:01       38 阅读
  2. 图表管理功能(前后端实现增删

    2024-03-25 17:46:01       47 阅读
  3. SpringBoot实现增删

    2024-03-25 17:46:01       40 阅读
  4. MyBatisPlus实现增删

    2024-03-25 17:46:01       22 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-25 17:46:01       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-25 17:46:01       106 阅读
  3. 在Django里面运行非项目文件

    2024-03-25 17:46:01       87 阅读
  4. Python语言-面向对象

    2024-03-25 17:46:01       96 阅读

热门阅读

  1. 【NC19989】容易题(EASY)

    2024-03-25 17:46:01       32 阅读
  2. 中国象棋C++

    2024-03-25 17:46:01       37 阅读
  3. Python实现WebSocket通信

    2024-03-25 17:46:01       33 阅读
  4. 第十五届蓝桥杯模拟赛 第三期 (C++)

    2024-03-25 17:46:01       40 阅读
  5. golang实现枚举

    2024-03-25 17:46:01       43 阅读
  6. Qt笔记 计时器

    2024-03-25 17:46:01       44 阅读
  7. Day28:学习SpringCloud

    2024-03-25 17:46:01       40 阅读
  8. 为什么大家都在推美国MSB?

    2024-03-25 17:46:01       42 阅读
  9. 编程生活day3--Left Pad、出生年

    2024-03-25 17:46:01       43 阅读
  10. 服务器如何检查端口是否被占用并杀死进程

    2024-03-25 17:46:01       41 阅读
  11. AWTK-MODBUS 服务器

    2024-03-25 17:46:01       42 阅读