HarmonyOS开发实例:【手机备忘录】

介绍

本篇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. 搭建烧录环境。

    1. [完成DevEco Device Tool的安装]
    2. [完成RK3568开发板的烧录]
    3. 鸿蒙开发指导文档:gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到参考。
  3. 搭建开发环境。

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

搜狗高速浏览器截图20240326151450.png

代码结构解读

鸿蒙HarmonyOS与OpenHarmony开发籽料mau123789添加v可直接拿

本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在gitee中提供。

├──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();
})
  • 26
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android应用开发备忘录是一款帮助用户记录和管理待办事项的应用程序。下面是其源码实现的一些关键功能: 1. 用户注册与登录:备忘录应用需要提供用户注册与登录功能。用户可以通过用户名和密码进行注册和登录。注册成功后,用户可以创建自己的备忘录账户。 2. 备忘录创建与查看:用户登录后,可以创建新的备忘录事项。用户可以输入事项的标题、内容和截止日期等信息。创建后,用户可以查看已创建的备忘录事项列表以及每个事项的详细信息。 3. 备忘录编辑与删除:用户可以对已创建的备忘录事项进行编辑和删除操作。用户可以修改事项的标题、内容和截止日期等信息。如果事项已经完成或不再需要,用户可以将其删除。 4. 备忘录提醒功能:在备忘录事项的截止日期到达之前,应用可以通过推送通知的方式提醒用户。用户可以在应用设置中设置提醒的方式和时间。 5. 数据存储与同步:备忘录应用需要将用户创建的备忘录事项信息进行持久化存储。可以使用SQLite数据库或者其他方式进行数据的保存。同步功能可以将备忘录数据存储到云端,以便用户在不同设备上进行访问和管理。 以上是一个基本的备忘录应用的功能需求和实现方式。当然,根据实际需要,你可以根据以上要点进行源码编写。希望以上内容能够帮助你进一步了解和开发Android应用备忘录

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值