大家好,今天,我将基于我的上一个项目《待办清单》开发《待办清单2.0》,我将对其修复了已知的bug、功能上进行的改进、美化了页面、添加了中英文配置以及其他更具规范性操作等等,开源代码我也会放到下面,同时我在代码总添加了更多注释,希望对初学者有所帮助。

介绍

本应用基于Stage模型下的UIAbility进行开发,是一款名为《待办清单》的任务管理应用。其界面设计简洁明了,让您能够轻松地组织和管理各项任务。无论是处理工作项目、家庭琐事还是个人目标,本应用都能满足您的需求,让您能够轻松追踪任务并保持高效的工作状态。

环境搭建

我们首先需要完成HarmonyOS开发环境搭建,可参照如下步骤进行。

软件要求

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

硬件要求

• 设备类型:华为手机或运行在DevEco Studio上的华为手机设备模拟器。 • HarmonyOS系统:3.1.0 Developer Release。

环境搭建

  1. 安装DevEco Studio,详情请参考下载和安装软件
  2. 设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境: • 如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。 • 如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境
  3. 开发者可以参考以下链接,完成设备调试的相关配置: • 使用真机进行调试使用模拟器进行调试

项目结构

PixPin_20231226_104900.png

项目展示

《待办清单》APP展示 PixPin_20231226_143409.gif

在进入应用主页后,只需点击“+”号按钮,即可轻松跳转到新增任务的页面。在该页面中,您只需输入相应的任务内容,然后点击确定,即可完成任务的添加。完成添加后,您将自动返回到应用的首页,并发现刚刚添加的任务已经成功显示在列表中。请注意,新增的任务内容不能为空,如果直接点击提交而未输入任务内容,系统将弹出一个提示弹窗,提醒您任务内容不能为空。

111.png

完成任务后,只需点击任务左侧的复选框,即可轻松标记该任务为已完成。此外,您还可以通过页面上方的筛选按钮,对任务进行筛选,以便更好地管理您的任务。当您在应用中查看任务进度时,可以清晰地了解任务的完成情况,从而更好地管理自己的工作流程。同时,看到任务进度的不断改善,您会感到一种成就感,激励自己更加努力地完成任务。

112.png

如果您发现任务输入有误或其他原因需要删除该任务,只需向左滑动该任务,然后点击任务右侧的删除按钮。此时,系统会弹出一个提示弹窗,以防止用户误触。如果您确定要删除该任务,请点击“是”,即可完成任务的删除操作。

113.png

此外,该应用还具备将数据保存到手机本地的功能。即使在清理掉手机应用后台后,再重新打开应用,您之前输入的数据仍然会被保留在应用中,供您循环使用。这种本地存储功能为您提供了更大的便利性,让您在不同设备或重新安装应用后仍能继续使用之前的数据。

114.png

至此,我们对《待办清单》APP的所有功能进行了详细展示。接下来,我们将进入源代码解析环节,深入了解其内部实现。让我们一起探索这个任务管理应用的核心部分,看看它是如何工作的。

项目解析

首页 Index

此页面是整个应用的入口组件,主要负责管理任务列表和控制页面的显示。通过这个组件,用户可以轻松地查看、添加、删除和完成各项任务。同时,该组件还提供了筛选和排序功能,使用户能够更好地组织和管理工作任务。

该组件包含了一些状态变量和方法,用于管理任务列表和控制页面的显示。它通过调用TaskHelper类提供的方法来读取和保存任务列表数据,确保数据的一致性和完整性。同时,根据displayAddPage状态变量的值,该组件能够动态控制页面的显示,以满足用户的不同需求。这种设计模式使得应用的逻辑更加清晰,提高了可维护性和可扩展性。。

// 是否显示添加页面
@State displayAddPage: boolean = false
// 任务列表
@State @Watch('saveTaskList') taskList: Array<TaskModel> = []
// 总任务数量
@State totalTask: number = 0
// 己完成任务数量
@State finishTask: number = 0

Index 组件是整个应用的入口组件,负责管理任务列表和控制页面的显示。它包含了一些状态变量和方法,用于处理任务列表的展示和用户的交互操作。在 build() 函数中,根据 displayAddPage 状态变量的值来判断当前应该显示 AddPage 还是 ListPage 组件。当 displayAddPage 为真时,页面跳转到 AddTask;反之,页面则展示 ListPage。这种动态组件切换的方式使得应用能够根据用户的需求灵活地展示不同的页面,提高了用户体验和交互性。

// 是否显示添加页面
@State displayAddPage: boolean = false
build() {
    if (this.displayAddPage) {
      AddPage({
        displayAddPage: $displayAddPage,
        taskList: $taskList,
        totalTask: $totalTask,
        finishTask: $finishTask
      })
    } else {
      ListPage({
        displayAddPage: $displayAddPage,
        taskList: $taskList,
        totalTask: $totalTask,
        finishTask: $finishTask
      })
    }
  }

saveTaskList() 函数中,应用通过调用 TaskHelper.saveTaskList() 方法将当前的任务列表数据保存到本地存储中。这种保存机制确保了即使在应用关闭或重新启动后,用户输入和修改的任务数据也能够被保留,为用户提供了更好的使用体验。

aboutToAppear() 函数中,应用通过调用 TaskHelper.readTaskList() 方法来读取本地存储中的任务列表数据。这些数据被赋值给 taskList 状态变量,以便在页面显示时使用。这种设计模式确保了数据的一致性,避免了每次打开页面时都需要重新获取数据,提高了应用的性能。

// 任务列表
@State @Watch('saveTaskList') taskList: Array<TaskModel> = []
saveTaskList() {
    TaskHelper.saveTaskList(this.taskList)
}
aboutToAppear() {
    this.taskList = TaskHelper.readTaskList()
}

新增任务页 AddTask

此页面主要用于展示新增任务页的界面,并提供一个方便用户输入和保存任务内容的平台。它是待办清单应用中一个重要的功能模块,帮助用户轻松添加新的任务。通过这个页面,用户可以快速输入任务内容,并点击提交按钮将其保存到应用中。这种设计使得用户能够更高效地管理他们的待办事项,并在需要时轻松添加新的任务。

该页面包含一个Boolean类型的状态变量displayAddPage,用于控制页面的显示与隐藏。当displayAddPage为真时,页面显示添加新任务的界面;反之,页面则隐藏添加新任务的界面。此外,该页面还包含一个用于存储输入的任务内容的字符串类型状态变量taskContent,以及一个数组类型状态变量taskList,用于存储任务列表。这些状态变量共同协作,确保了任务数据的完整性和一致性,并为用户提供了灵活的任务管理体验。

// 是否显示添加任务页面
@Link displayAddPage: boolean
// 用户输入的任务内容
@State taskContent: string = ''
// 任务列表
@Link taskList: [TaskModel]
//总任务数量
@Link totalTask: number
//已完成任务数量
@Link finishTask: number

在页面的构建函数 build() 中,使用了框架提供的 ColumnStackImageTextTextAreaButton 等组件和样式属性,用于构建页面的各个元素。这些组件和属性提供了丰富的界面元素和样式选项,使得页面更加美观和易于使用。

在确定按钮的 onClick 事件中,通过调用系统提供的 getCurrentTime() 方法获取当前时间,然后创建了一个新的 TaskModel 对象,并将其添加到 taskList 数组中。这种设计模式确保了任务数据的完整性和一致性,使得用户添加的新任务能够被正确地存储和应用。

最后,将 displayAddPage 设置为 false,隐藏该页面。这种动态控制页面显示和隐藏的方式,使得应用能够根据用户的需求灵活地展示不同的页面,提高了用户体验和交互性。

build() {
  Column({ space: 20 }) {
    Stack() {
      // 返回按钮
      Column() {
        Image($r('app.media.ic_public_back'))
          .width(30)
          .fillColor(Color.Black)
          .onClick(() => {
            this.displayAddPage = false;
          })
      }
      .width('100%')
      .alignItems(HorizontalAlign.Start)
      // 标题
      Text($r('app.string.Add_task'))
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
    }
    //用户自定义任务名
    TextArea({ text: this.taskContent, placeholder: $r('app.string.enter_task_content') })
      .backgroundColor(Color.White)
      .width(315)
      .height(201)
      .borderRadius(9)
      .borderColor(Color.Gray)
      .onChange((value) => {
        this.taskContent = value;
      })
    //提交按钮
    Button({
      stateEffect: true,
      type: ButtonType.Normal
    }) {
      Text($r('app.string.submit'))
        .fontColor(Color.White)
    }
    .width(315)
    .height(50)
    .borderRadius(5)
    .borderColor($r('app.color.button_background'))
    // 当输入框内容发生变化时触发该函数
    .onClick(() => {
      // 判断输入框内容是否为空
      if (this.taskContent.length === 0) {
        try {
          // 显示提示信息
          promptAction.showToast({
            message: $r('app.string.not_empty'),
            duration: 2000,
          })
        } catch (error) {
          console.error(`showToast args error code is ${error.code}, message is ${error.message}`);
        }
        return
      }
      // 获取当前时间
      systemDateTime.getCurrentTime(true).then((currentTime) => {
        // 创建一个新的任务模型
        let task = new TaskModel({
          // 任务ID为当前时间
          taskId: currentTime,
          // 任务内容为输入框内容
          taskContent: this.taskContent,
          // 任务状态初始化为未完成
          isCompleted: false
        })
        // 将新任务添加到任务列表中
        this.taskList.push(task)
        // 更新任务总数量
        this.totalTask = this.taskList.length
        // 隐藏添加任务页面
        this.displayAddPage = false
      })
    })
  }
  .width('100%')
  .height('100%')
  .backgroundColor($r('app.color.background'))
  .padding({ top: 30, left: 20, right: 20, bottom: 20 })
}

任务展示页 listPage

此页面是应用中实现最主要功能的组件之一,它是一个展示任务列表和管理任务的页面组件。具体来说,该组件具备以下功能:

  1. 任务筛选:用户可以通过筛选功能,按照不同的条件对任务进行筛选,以便快速找到自己关心的任务。
  2. 任务展示:该组件负责展示任务列表,每个任务以清晰、简洁的方式呈现在页面上,方便用户查看和了解任务的详情。
  3. 任务操作:用户可以对展示的任务进行相应的操作,如编辑、删除和完成等。这些操作都提供了相应的按钮或入口,用户可以方便地进行操作。

通过这个页面组件,用户可以轻松地对任务进行管理,包括查看任务的进度、修改任务的详情、删除不再需要的任务等。这种设计模式使得任务管理更加高效和便捷,提高了用户的工作效率。

该组件包含了一些状态变量和方法,用于获取任务列表、删除任务等操作。这些状态变量和方法共同协作,确保了任务数据的完整性和一致性,并为用户提供了灵活的任务管理体验。

通过调用TaskHelper类提供的方法,该组件能够从本地存储中获取任务列表数据。同时,它还提供了一些方法用于删除任务,如调用TaskHelper.deleteTask()方法来删除指定任务。这些方法的使用使得用户可以对任务进行灵活的管理,满足不同场景下的需求。

此外,该组件还包含了一些事件处理函数,如点击事件处理函数,用于响应用户的操作。通过这些事件处理函数,用户可以方便地对任务进行筛选、删除和完成等操作。

//显示添加页面的布尔值
@Link displayAddPage: boolean
//任务列表
@Link taskList: Array<TaskModel>
//任务类型 默认为全部
@State taskType: string = 'all'
//总任务数量
@Link totalTask: number
//己完成任务数量
@Link finishTask: number

build() 函数中,使用了前端框架提供的多个组件和样式属性,例如 ColumnTextRowRadioStackImageListListItemCheckboxButton 等。这些组件和样式属性提供了丰富的界面元素和布局选项,使得页面更加美观和易于使用。

通过这些组件的组合和样式属性的设置,可以构建出各种界面元素,如文本框、单选框、复选框、按钮等,同时还可以对界面进行灵活的布局和排版。这种设计模式提高了代码的可维护性和可扩展性,使得页面的构建更加高效和便捷。

此外,这些组件还提供了事件处理功能,例如点击事件、选中事件等,使得用户与页面的交互更加自然和流畅。通过合理地使用这些组件和事件处理功能,可以构建出功能丰富、用户体验良好的页面。

删除弹窗提示函数用于在删除任务时向用户显示一个弹窗提示,告知用户任务已被删除。这个函数通常会在用户点击删除按钮时被触发,并在弹窗中显示相应的提示信息。

//删除弹窗提示函数
deleteTask(taskId: number) {
  // 弹出提示框
  AlertDialog.show({
    title: $r('app.string.delete'),
    message: $r('app.string.sure_delete'),
    autoCancel: true,
    alignment: DialogAlignment.Bottom,
    // 偏移量
    offset: { dx: 0, dy: -20 },
    // 主要按钮
    primaryButton: {
      value: $r('app.string.no'),
      action: () => {
      }
    },
    // 次要按钮
    secondaryButton: {
      value: $r('app.string.yes'),
      action: () => {
        // 根据任务id获取任务索引
        let taskIndex = TaskHelper.getIndexByTaskId(this.taskList, taskId)
        // 从任务列表中删除任务
        this.taskList.splice(taskIndex, 1)
        this.handleTaskChange()
      }
    }
  })
}

删除按钮的样式,主要调用了删除弹窗提示函数

@Builder
deleteButton(taskId: number) {
  //删除按钮
  Button() {
    Image($r('app.media.ic_public_delete_filled'))
      .fillColor(Color.White)
      .width(20)
  }
  .width(40)
  .height(40)
  .type(ButtonType.Circle)
  .backgroundColor(Color.Red)
  .margin(5)
  .onClick(() => {
    this.deleteTask(taskId)
  })
}

获取任务列表的调用函数用于从数据源中获取任务列表,并在应用中展示出来。这个函数通常在应用启动或用户触发某些操作时被调用,以确保显示的任务列表是最新的。

//获取任务列表
getTaskList(): Array<TaskModel> {
  // 如果任务类型为all,则返回任务列表
  if (this.taskType == 'all') {
    return this.taskList
    // 如果任务类型为todo,则返回未完成的任务列表
  } else if (this.taskType == 'todo') {
    return this.taskList.filter((item) => {
      // 如果任务未完成,则返回true
      if (!item.isCompleted) {
        return true
      }
    })
    // 如果任务类型为finish,则返回已完成的任务列表
  } else if (this.taskType == 'finish') {
    return this.taskList.filter((item) => {
      // 如果任务已完成,则返回true
      if (item.isCompleted) {
        return true
      }
    })
  }
}


在页面中,为了方便用户筛选任务,通常会提供一个用于显示任务类型的Radio按钮组。用户可以通过选择不同的Radio按钮来筛选任务,例如选择“全部”来显示所有任务,选择“待办”来只显示待办任务,选择“完成”来只显示已完成的任务。

//标题
Text($r('app.string.my_task'))
	.fontSize(20)
	.fontWeight(FontWeight.Bold)
//三个按钮可选项
Row() {
  Column() {
   Text($r('app.string.all'))
    // 单选框,组为taskType
    Radio({ value: 'all', group: 'taskType' })
      .onChange((isSelect) => {
         if (isSelect) {
           // 任务类型设置为all
           this.taskType = 'all'
         }
       })// 设置是否被选中
       .checked(this.taskType == 'all' ? true : false)
      }
    Column() {
      Text($r('app.string.todo'))
      Radio({ value: 'todo', group: 'taskType' })
        .onChange((isSelect) => {
          if (isSelect) {
            this.taskType = 'todo'
          }
        })
        .checked(this.taskType == 'todo' ? true : false)
    }

    Column() {
      Text($r('app.string.finish'))
      Radio({ value: 'finish', group: 'taskType' })
        .onChange((isSelect) => {
          if (isSelect) {
            this.taskType = 'finish'
          }
        })
        .checked(this.taskType == 'finish' ? true : false)
    }
  }
  .width('100%')
  .justifyContent(FlexAlign.SpaceAround)

与之前的内容相比,2.0版本新增了任务进度卡片。此卡片用于展示任务的完成进度,其构建包含TextStackProgressRow等组件,并具备各种样式属性,全面定义了任务进度卡片的布局与外观。通过展示任务进度,用户可以更清晰地了解任务的完成情况,从而更有效地管理和分配工作。这种进度显示有助于团队成员及时掌握任务动态,提高工作效率。当用户看到任务进度逐渐改善,他们会充满成就感与动力,更积极地完成剩余任务。

// 任务进度卡片
Row() {
  Text('任务进度:')
    .fontSize(24)
    .fontWeight(FontWeight.Bold)

  Stack() {
    Progress({
      value: this.finishTask,
      total: this.totalTask,
      type: ProgressType.Ring
    })
      .width(70)

    Row() {
      Text(this.finishTask.toString())
        .fontSize(18)
        .fontColor($r('app.color.button_background'))
      Text('/ ' + this.totalTask.toString())
        .fontSize(18)
    }
  }

}
.card()
.margin({ top: 10 })
.justifyContent(FlexAlign.SpaceEvenly)

任务列表部分使用了 ListListItem 组件来展示任务,这样可以提供清晰、有序的列表展示效果。每个任务包含复选框、任务内容和删除按钮,这些组件的使用提供了以下功能:

  1. 复选框:通过复选框,用户可以勾选任务以标记为完成。这有助于用户跟踪任务的进度和状态。在实现时,确保复选框的状态与任务的实际完成状态保持一致,并提供适当的事件处理函数来更新任务状态。
  2. 任务内容:使用任务内容组件来展示任务的详细信息,如标题、描述或其他相关字段。确保任务内容的展示方式清晰、易于阅读,并根据需要调整样式和格式。
  3. 删除按钮:每个任务旁边都应有一个删除按钮,允许用户删除单个任务。在实现时,应确保删除操作是安全的,并遵循应用的数据管理规范。同时,提供适当的确认提示,确保用户了解删除操作是不可逆的。
  4. 事件处理:为复选框和删除按钮添加事件处理函数。当用户勾选复选框时,更新相应的任务状态;当用户点击删除按钮时,触发删除任务的逻辑。
  5. 动态更新:根据用户的操作和任务状态的改变,动态更新任务列表的展示内容。例如,已完成的任务可以显示为不同的颜色或标记,以区分未完成的任务。
  6. 响应式设计:确保任务列表在不同设备和屏幕尺寸上的展示效果良好,提供响应式布局。这包括调整组件的大小、间距等样式属性,以确保在不同屏幕尺寸下的显示效果一致。

通过这些组件和功能的组合,任务列表部分能够提供清晰、直观的任务展示和管理功能,使用户能够方便地完成任务标记和删除操作。同时,保持良好的UI/UX设计和动态更新能力,提供流畅的用户体验。

//显示任务(可滑动)
Column({ space: 10 }) {
List({ space: 10 }) {
  ForEach(
    this.getTaskList(),
    (item: TaskModel, index) => {
      ListItem() {
	Row() {
	  //完成待办后的样式
	  Text(item.taskContent)// 设置左边距
	    .margin({ left: 10 })// 根据任务是否完成,设置文本的样式
	    .decoration({
	      type: item.isCompleted ? TextDecorationType.LineThrough : TextDecorationType.None
	    })// 设置宽度
	    .width('70%')// 设置行数
	    .maxLines(1)// 设置文本溢出时的样式
	    .textOverflow({ overflow: TextOverflow.Ellipsis })

	  // 添加一个空行
	  Blank()

	  // 创建复选框
	  Checkbox()// 设置复选框的值
	    .select(item.isCompleted)// 当复选框的值发生变化时,执行回调函数
	    .onChange((value) => {
	      // 获取任务id在任务列表中的索引
	      let taskIndex = TaskHelper.getIndexByTaskId(this.taskList, item.taskId)
	      // 获取任务列表中对应任务id的任务
	      let oldTask = this.taskList[taskIndex]
	      // 创建新的任务模型
	      let newTask = new TaskModel({
		taskId: item.taskId,
		taskContent: oldTask.taskContent,
		isCompleted: value
	      })
	      // 将新的任务模型替换任务列表中对应任务id的任务
	      this.taskList[taskIndex] = newTask

	      // 更新任务状态
	      item.isCompleted = value
	      // 更新任务数量
	      this.handleTaskChange()
	    })
	}
	.card()
	.justifyContent(FlexAlign.SpaceBetween)
      }
      // @ts-ignore
      .swipeAction({ end: this.deleteButton(item.taskId) })
    }
  )
}
.width('100%')
.height('53%')
.alignListItem(ListItemAlign.Center)
}
.margin({ top: 5, bottom: 5 })

此外,页面底部通常会设置一个用于添加新任务的按钮。这个按钮的作用是提供一个入口,使用户可以方便地创建并添加新任务。

//新增任务按钮
Column() {
Button({
  // 设置按钮类型
  type: ButtonType.Circle,
  // 设置按钮状态
  stateEffect: true
}) {
  Text('+')
    .fontSize(50)
    .fontColor(Color.White)
}
.width(60)
.height(60)
.borderColor($r('app.color.button_background'))
.onClick(() => {
  // 显示添加页面
  this.displayAddPage = true
  this.handleTaskChange()
})
}
.width('100%')
.alignItems(HorizontalAlign.End)

任务模型类 TaskModel

这是一个名为TaskModel的类,它表示一个任务模型。该类具有三个属性:taskId(任务ID)、taskContent(任务内容)和isCompleted(任务是否已完成)。它还包含一个构造函数,用于初始化这些属性。 构造函数接受一个对象作为参数,对象包含了taskIdtaskContentisCompleted这三个属性的值。在构造函数中,使用传入的对象来初始化类的属性。

这个类的作用是定义一个任务的数据模型,包含了任务的基本信息和属性。通过这个类,可以创建和管理任务对象,并确保数据的一致性和完整性。

在具体实现中,这个类通常包含以下属性和方法:

  • ID:表示任务的唯一标识符。每个任务都应该有一个唯一的ID,以便在应用中进行识别和跟踪。
  • 内容:表示任务的具体内容或描述。这可以是文本、数字或其他数据类型,具体取决于任务的内容和要求。
  • 完成状态:表示任务的完成状态。通常是一个布尔值或枚举类型,用于标识任务是否已完成。

除了这些基本属性外,这个类还可以包含其他相关的方法和逻辑,以便更好地管理任务数据。

export class TaskModel {
  // 任务id
  taskId: number = 0
  // 任务内容
  taskContent: string = ''
  // 任务是否完成
  isCompleted: boolean = false

  constructor({ taskId:taskId, taskContent:taskContent, isCompleted:isCompleted}) {
    this.taskId = taskId
    this.taskContent = taskContent
    this.isCompleted = isCompleted
  }
}

文件保存工具类 FileHelper

这段代码是一个名为FileHelper的类,用于处理文件读写操作。它包含了两个静态方法:saveJsonDatareadJsonData

saveJsonData方法用于将JSON数据保存到文件中。它接受两个参数:jsonObj表示要保存的JSON对象,fileName表示要保存的文件名。在方法内部,它使用@ohos.file.fs模块来获取应用文件路径,并根据文件路径新建并打开文件。然后将JSON对象转换为字符串,并写入文件中。

// 保存Json数据
static saveJsonData(jsonObj: any, fileName: string) {
    // 获取应用文件路径
    let context = getContext(this) as common.UIAbilityContext
    let filesDir = context.filesDir // 获取应用的文件夹路径
    let filePath = `${filesDir}/${fileName}` // 将文件名与文件夹路径连接起来,得到完整的文件路径
    // 新建并打开文件
    // 尝试打开文件
    try {
        // 以写入模式打开文件,并设置为只读、清空内容、创建模式
        let file = fs.openSync(filePath, fs.OpenMode.WRITE_ONLY | fs.OpenMode.TRUNC | fs.OpenMode.CREATE)
        // 将 JSON 对象转换为字符串
        let jsonStr = JSON.stringify(jsonObj)
        // 输出 JSON 字符串
        console.log("HMOS:" + jsonStr)
        // 将 JSON 字符串写入文件
        fs.writeSync(file.fd, jsonStr)
        // 关闭文件
        fs.closeSync(file)
    } catch (e) {
        // 输出文件写入错误信息
        console.log(`write file error ${e}`);
    }
}

readJsonData方法用于从文件中读取JSON数据。它接受一个参数fileName,表示要读取的文件名。在方法内部,它同样使用@ohos.file.fs模块来获取应用文件路径,并读取文件中的文本内容。然后将读取到的文本内容转换为JSON对象并返回。

// 读取Json数据
static readJsonData(fileName: string): any {
    // 获取应用文件路径
    let context = getContext(this) as common.UIAbilityContext
    let filesDir = context.filesDir // 获取应用的文件夹路径
    let filePath = `${filesDir}/${fileName}` // 将文件名与文件夹路径连接起来,得到完整的文件路径
    var userStr = ""
    try {
        // 从文件路径中读取内容并将其存储在userStr变量中
        userStr = fs.readTextSync(filePath)
    } catch (e) {
        console.log(`read file error ${e}`);
    }
    if (userStr.length > 0) {
        console.log(userStr); // 输出userStr
        return JSON.parse(userStr); // 将userStr解析为JSON对象并返回
    }
    return {}
}

这个FileHelper类为用户提供了简单快捷的保存和读取JSON数据的方法,使其在各种应用中能够轻松处理文件读写操作。

工具调用类 TaskHelper

这是一个名为TaskHelper的类,用于处理任务列表数据的读取和保存。它包含了三个静态方法:getIndexByTaskIdsaveTaskListreadTaskList

getIndexByTaskId方法用于根据任务ID在任务列表中查找对应的索引位置。它接受两个参数:taskList表示任务列表数组,taskId表示要查找的任务ID。在方法内部,它遍历任务列表,查找匹配的任务ID,并返回对应的索引位置。如果找不到匹配的任务ID,就返回-1。

// 按任务id获取索引
static getIndexByTaskId(taskList: Array<TaskModel>, taskId: number) {
    // 遍历任务列表
    for (let i = 0; i < taskList.length; i++) {
        // 如果任务id相等
        if (taskList[i].taskId == taskId) {
            // 返回索引
            return i
        }
    }
    // 如果没有找到,返回-1
    return -1
}

saveTaskList方法用于将任务列表数据保存到文件中。它接受一个参数taskList,表示要保存的任务列表数组。在方法内部,它调用FileHelpersaveJsonData方法,将任务列表数据保存到名为'task.data'的文件中。

// 保存任务列表
static saveTaskList(taskList: Array<TaskModel>) {
    // 使用FileHelper类的saveJsonData方法,将任务列表数据保存到task.data文件中
    FileHelper.saveJsonData(taskList, 'task.data')
}

readTaskList方法用于从文件中读取任务列表数据。它调用FileHelperreadJsonData方法来读取名为'task.data'的文件中的数据,并将读取到的JSON数据转换为TaskModel对象的数组。

// 读取任务列表
static readTaskList(): Array<TaskModel> {
    // 读取任务数据
    let jsonData = FileHelper.readJsonData('task.data')
    // 创建任务列表
    let taskList: Array<TaskModel> = []
// 将json数据赋值给任务列表
Object.assign(taskList, jsonData)
// 返回任务列表
return taskList
}

这个TaskHelper类简化了任务列表数据的读取和保存过程,为应用提供了高效的任务列表数据管理功能。

总结

随着鸿蒙系统的日益普及,开发一款高效、实用的待办清单APP变得至关重要。本文将详细介绍《待办清单2.0》鸿蒙APP的开发过程,从需求分析、UI设计、业务逻辑到本地存储等方面进行阐述,旨在帮助开发者更好地理解和应用鸿蒙系统的开发框架。

首先,进行需求分析是至关重要的。通过研究用户的需求,可以更好地指导后续的UI设计、功能开发和优化方向。因此,在设计中注重简洁直观的操作界面。其次,UI设计是提升用户体验的关键环节。在《待办清单2.0》中,采用了扁平化设计风格,使得界面更加现代化和简洁。同时,通过使用卡片式布局和醒目的颜色,提高了任务的视觉冲击力,使得用户可以更快地获取任务信息。另外,业务逻辑是APP的核心部分,决定了APP如何处理数据和响应用户操作。《待办清单2.0》实现了创建、筛选、删除和完成等基础任务管理功能。此外,还实现了子任务、标签和备注等功能,以满足用户更复杂的需求。此外,本地存储是确保APP在无网络环境下正常工作的关键。在《待办清单2.0》中,我们采用了存储文件进行本地存储。这使得用户可以在没有网络的情况下添加、编辑和删除任务。

最终,我深刻地领悟到开发一款完备的APP是一项极为艰巨的任务。仅仅构思一个功能并将其实现是相对简单的,然而,在这个过程中,我们经常会遭遇大大小小的bug。修复这些bug可能需要耗费大量的时间和精力,无论是通过百度搜索解决方案,还是借鉴他人的代码,我们都需要不断地调试和优化。然而,请记住,困难只是暂时的,总会有解决的方法。在经历风雨之后,绚丽的彩虹总会出现。因此,面对挑战时,请不要轻易放弃,要相信曙光就在前方。

项目源码

https://gitee.com/bananana-ice/harmonyos-todolist.git

本文作者:好奇码客

想了解更多关于开源的内容,请访问:​

​51CTO 开源基础软件社区​

​https://ost.51cto.com/#bkwz​