HarmonyOS实战开发-目标管理、如何实现一个自定义弹窗。

介绍

本篇Codelab将介绍如何使用@State、@Prop、@Link、@Watch、@Provide、@Consume管理页面级变量的状态,实现对页面数据的增加、删除、修改。要求完成以下功能:

  1. 实现一个自定义弹窗,完成添加子目标的功能。
  2. 实现一个可编辑列表,可点击指定行展开调节工作目标进度,可多选、全选删除指定行。

相关概念

  • 页面状态管理:用于管理页面级变量的状态。
  • 自定义弹窗: 通过CustomDialogController类显示自定义弹窗。
  • List列表:列表包含一系列相同宽度的列表项。

环境搭建

软件要求

  • 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.搭建开发环境。

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

代码结构解读

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

├──entry/src/main/ets                   // ArkTS代码区
│  ├──common
│  │  ├──constants
│  │  │  └──CommonConstants.ets         // 公共常量类
│  │  └──utils
│  │     ├──DateUtil.ets                // 获取格式化日期工具
│  │     └──Logger.ets                  // 日志打印工具类
│  ├──entryability
│  │  └──EntryAbility.ts                // 程序入口类
│  ├──pages
│  │  └──MainPage.ets                   // 主页面
│  ├──view
│  │  ├──TargetInformation.ets          // 整体目标详情自定义组件
│  │  ├──AddTargetDialog.ets            // 自定义弹窗
│  │  ├──ProgressEditPanel.ets          // 进展调节自定义组件
│  │  ├──TargetList.ets                 // 工作目标列表
│  │  └──TargetListItem.ets             // 工作目标列表子项
│  └──viewmodel
│     ├──DataModel.ets                  // 工作目标数据操作类
│     └──TaskItemViewModel.ets          // 任务进展实体类
└──entry/src/main/resources             // 资源文件目录

构建主界面

MainPage作为本应用的主界面,从上至下由三个自定义组件组成。

  1. 标题titleBar。
  2. 目标整体进展详情TargetInformation。
  3. 子目标列表TargetList。

MainPage主要维护五个参数:子目标数组targetData、子目标总数totalTasksNumber、已完成子目标数completedTasksNumber、最近更新时间latestUpdateDate、监听数据变化的参数overAllProgressChanged。具体作用有以下三个方面:

  1. 子组件TargetInformation接收三个参数totalTasksNumber、completedTasksNumber、latestUpdateDate,渲染整体目标详情。
  2. 子组件TargetList接收参数targetData渲染列表。
  3. 使用@Watch监听overAllProgressChanged的变化。当overAllProgressChanged改变时,回调onProgressChanged方法,刷新整体进展TargetInformation。
// MainPage.ets
@Entry
@Component
struct MainPage {
  // 子目标数组
  @State targetData: Array<TaskItemViewModel> = DataModel.getData();
  // 子目标总数
  @State totalTasksNumber: number = 0;
  // 已完成子目标数
  @State completedTasksNumber: number = 0;
  // 最近更新时间
  @State latestUpdateDate: string = CommonConstants.DEFAULT_PROGRESS_VALUE;
  // 监听数据变化的参数
  @Provide @Watch('onProgressChanged') overAllProgressChanged: boolean = false;	
  ...

  /**
   * overAllProgressChanged改变时的回调
   */
  onProgressChanged() {
    this.totalTasksNumber = this.targetData.length;
    this.completedTasksNumber = this.targetData.filter((item) => {
      return item.progressValue === CommonConstants.SLIDER_MAX_VALUE;
    }).length;
    this.latestUpdateDate = getCurrentTime();
  }

  build() {
    Column() {
      // 标题
      this.titleBar()
      // 目标整体进展详情
      TargetInformation({
        latestUpdateDate: this.latestUpdateDate,
        totalTasksNumber: this.totalTasksNumber,
        completedTasksNumber: this.completedTasksNumber
      })
      // 子目标列表
      TargetList({
        targetData: $targetData,
        onAddClick: () :void  => this.dialogController.open()
      })
        ...
    }
    ...
  }

  @Builder
  titleBar() {
    Text($r('app.string.title'))
      ...
  }
}

添加任务子目标

本章节主要介绍如何实现一个自定义弹窗,完成添加子目标的功能。效果如图所示:

在MainPage.ets中,创建dialogController对象控制弹窗隐显,传入自定义组件AddTargetDialog和点击确定的回调方法saveTask。

// MainPage.ets
@Entry
@Component
struct MainPage {
  dialogController: CustomDialogController = new CustomDialogController({
    builder: AddTargetDialog({
      onClickOk: (value: string): void => this.saveTask(value)
    }),
    alignment: DialogAlignment.Bottom,
    offset: {
      dx: CommonConstants.DIALOG_OFFSET_X,
      dy: $r('app.float.dialog_offset_y')
    },
    customStyle: true,
    autoCancel: false
  });
}

在AddTargetDialog.ets中,参数onClickOk为function类型,接收MainPage传入的saveTask方法。点击确定,调用onClickOk执行saveTask方法,关闭弹窗。

// AddTargetDialog .ets
@CustomDialog
export default struct AddTargetDialog {
  ...
  private controller?: CustomDialogController;
  onClickOk?: (value: string) => void;

  build() {
    Column() {
      ...
      Text($r('app.string.add_task_dialog'))
      ...
      TextInput({ placeholder: $r('app.string.input_target_name')})
      ...
      .onChange((value: string) => {
        this.subtaskName = value;
      })
      Blank()
      Row() {
        ...
        Button($r('app.string.confirm_button'))
          .dialogButtonStyle()
          .onClick(() => {
            if (this.onClickOk !== undefined) {
              this.onClickOk(this.subtaskName);
            }
          })
      }
      ...
    }
    ...
  }
}

在MainPage.ets中,实现saveTask方法:保存数据至DataModel中,并更新targetData的值,完成添加子目标功能。

// MainPage.ets
saveTask(taskName: string) {
  if (taskName === '') {
    promptAction.showToast({
      message: $r('app.string.cannot_input_empty'),
      duration: CommonConstants.TOAST_TIME,
      bottom: CommonConstants.TOAST_MARGIN_BOTTOM
    });
    return;
  }
  DataModel.addData(new TaskItemViewModel(taskName, 0, getCurrentTime()));
  this.targetData = DataModel.getData();
  this.overAllProgressChanged = !this.overAllProgressChanged;
  this.dialogController.close();
}

实现可编辑列表

本章节主要介绍子目标列表TargetList的实现,包括以下功能:

  • 列表项展开。
  • 列表子项点击下拉,滑动滑块更新进展。
  • 列表进入编辑状态,单选、多选、全选、删除子项。

实现列表项展开

实现以下步骤完成点击列表项展开功能:

  1. 使用@State 管理参数isExpanded,表示当前项是否展开,具体表现为自定义组件ProgressEditPanel的显示或隐藏。
  2. 使用@Link和@Watch管理参数clickIndex,表示当前点击ListItem的Index索引。clickIndex值的改变将会传递至所有的ListItem。
  3. 完成onClick点击事件,将isExpanded 值置反,修改clickIndex值为当前点击的索引。
// TargetListItem.ets
@Component
export default struct TargetListItem {
  @State latestProgress?: number = 0;
  @Link @Watch('onClickIndexChanged') clickIndex: number;
  @State isExpanded: boolean = false;
  ...
  // clickIndex改变的回调方法
  onClickIndexChanged() {
    if (this.clickIndex !== this.index) {
      this.isExpanded = false;
    }
}

  build() {
    ...
    Column() {
      this.TargetItem()
      if (this.isExpanded) {
        Blank()
        // 自定义组件:编辑面板
        ProgressEditPanel({
          slidingProgress: this.latestProgress,
          onCancel: () => this.isExpanded = false,
          onClickOK: (progress: number): void => {
              this.latestProgress = progress;
              this.updateDate = getCurrentTime();
              let result = DataModel.updateProgress(this.index, this.latestProgress, this.updateDate);
              if (result) {
                this.overAllProgressChanged = !this.overAllProgressChanged;
              }
              this.isExpanded = false;
            },
          sliderMode: $sliderMode
        })
        ...
      }  
    }
    ...
    .onClick(() => {
      ...
      if (!this.isEditMode) {
        animateTo({ duration: CommonConstants.DURATION }, () => {
          this.isExpanded = !this.isExpanded;
        })
        this.clickIndex = this.index;
      }
    })
  }
  ...
}

实现更新进展

列表某项被展开后,实现以下步骤完成更新进展功能:

  1. Slider实现滑动条,滑动滑块调节进展,使用slidingProgress保存滑动值。
  2. 点击确定调用onClickOK方法,将数据slidingProgress回调至TargetListItem。
  3. 在TargetListItem中获取回调的数据并刷新页面。
// ProgressEditPanel.ets
@Component
export default struct ProgressEditPanel {
  @Link sliderMode: number;
  @Prop slidingProgress: number = 0;
  onCancel?: () => void;
  onClickOK?: (progress: number) => void;

  build() {
    Column() {
      Slider({...})
      Row() {
        CustomButton({
          buttonText: $r('app.string.cancel_button')
        })
          .onClick(() => {
            if (this.onCancel !== undefined) {
              this.onCancel();
            }
          })
       CustomButton({
          buttonText: $r('app.string.cancel_button')
       })
          .onClick(() => {
            if (this.onClickOK !== undefined) {
              this.onClickOK(this.slidingProgress);
            }
          })
      }
    }
  }
}

在DataModel.ets中,编写updateProgress方法。该方法根据索引和进度值以及更新日期更新数据。

// DataModel.ets
updateProgress(index: number, updateValue: number, updateDate: string): boolean {
  if (!this.targetData[index]) {
    return false;
  }
  this.targetData[index].progressValue = updateValue;
  this.targetData[index].updateDate = updateDate;
  return true;
}

实现列表多选

列表进入编辑模式才可单选、多选。实现以下步骤完成列表多选功能:

  1. 维护一个boolean类型的数组selectArray,其长度始终与数据列表的长度相等,且初始值均为false。表示进入编辑状态时列表均未选中。
  2. 定义一个boolean类型的值isEditMode,表示是否进入了编辑模式。
  3. TargetListItem选中状态的初始化和点击Checkbox改变TargetListItem的选中状态。
// TargetList.ets
export default struct TargetList {
  ...
  @State isEditMode: boolean = false;
  @State selectArray: Array<boolean> = [];
  ...

  build() {
    Column() {
      ...
      if (this.isEditMode) {
        // 取消按钮
        Text($r('app.string.cancel_button'))
          ...
          .onClick(() => {
             this.selectAll = false;
             this.isEditMode = false;
             this.selectAllOrCancel(false);
          })
        ...
        // 全选按钮
        Checkbox()
          ...
          .onClick(() => {
            ...
            this.selectAllOrCancel(this.selectAll);
          })
      } else {
        // 编辑按钮
        Text($r('app.string.edit_button'))
          ...
          .onClick(() => {
            this.isEditMode = true;
            this.selectAllOrCancel(false);
          })
      }
      ...
    }
  }
}

点击全选Checkbox,将selectArray数组的值全赋值true或false,重新渲染列表为全选或者取消全选状态。

// TargetList.ets
selectAllOrCancel(selectStatus: boolean) {
  let newSelectArray: Array<boolean> = [];
  this.targetData.forEach(() => {
    newSelectArray.push(selectStatus);
  });
  this.selectArray = newSelectArray;
}

在TargetListItem中,实现以下步骤改变ListItem的选中状态:

  1. 使用@Link定义selectArr数组接收TargetList传入的selectArray。
  2. 在TargetListItem渲染时,使用this.selectArr[this.index]获取初始选中状态。
  3. 点击Checkbox时,按照当前ListItem的索引,将选中状态保存至selectArr,重新渲染列表完成单选和多选功能。
// TargetListItem.ets
export default struct TargetListItem {

  ...
  @Link selectArr: Array<boolean>;
  public index: number = 0;
  
  build() {
    Stack({ alignContent: Alignment.Start }) {
      ...
      this.TargetItem()
      ...
      Checkbox()
        // 获取初始选中状态
        .select(this.selectArr[this.index])
        ...
        .onChange((isCheck: boolean) => {
          // 改变被点击项的选中状态
          this.selectArr[this.index] = isCheck;
        })
      ...
    ...
    }
  }
}

实现删除选中列表项

当点击“删除”时,TargetList.ets的deleteSelected方法,实现以下步骤完成列表项删除功能:

  1. 调用DataModel的deleteData方法删除数据。
  2. 更新targetData的数据重新渲染列表。
  3. 修改overAllProgressChanged的值,通知主页刷新整体进展详情TargetInformation。
// TargetList.ets
deleteSelected() {
  DataModel.deleteData(this.selectArray);
  this.targetData = DataModel.getData();
  this.overAllProgressChanged = !this.overAllProgressChanged;
  this.isEditMode = false;
}

在DataModel.ets中,遍历数据列表,删除被选中的数据项。

// DataModel.ets
export class DataModel {
  ...
  deleteData(selectArr: Array<boolean>) {
    if (!selectArr) {
      Logger.error(TAG, 'Failed to delete data because selectArr is ' + selectArr);
    }
    let dataLen = this.targetData.length - CommonConstants.ONE_TASK;
    for (let i = dataLen; i >= 0; i--) {
      if (selectArr[i]) {
        this.targetData.splice(i, CommonConstants.ONE_TASK);
      }
    }
  }
  getData(): Array<TaskItemViewModel> {
    return this.targetData;
  }
  ...
}

总结

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

  1. @State、@Prop、@Link、@Watch、@Provide、@Consume的使用。
  2. List组件的使用。
  3. 自定义弹窗的使用。
  4. Slider组件的使用。

为了帮助大家更深入有效的学习到鸿蒙开发知识点,小编特意给大家准备了一份全套最新版的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)开发学习手册

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/508057.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

商品服务 - 三级分类

1.递归查询树形结构 Overridepublic List<CategoryEntity> listWithTree() {//1.查出所有分类List<CategoryEntity> all this.list();//2.组装成父子的属性结构List<CategoryEntity> level1Menus all.stream().filter(c -> c.getParentCid().equals(0L)…

2004-2022年上市公司企业战略激进度数据(含原始数据+计算代码+计算结果)

2004-2022年上市公司企业战略激进度数据&#xff08;含原始数据计算代码计算结果&#xff09; 1、时间2004-2022年 2、来源&#xff1a;原始数据整理自csmar 3、指标&#xff1a; 证券代码、统计截止日期、员工人数、证券简称、报表类型、固定资产净额、无形资产净额、资产…

算法学习——LeetCode力扣动态规划篇6(121. 买卖股票的最佳时机、122. 买卖股票的最佳时机 II、123. 买卖股票的最佳时机 III)

算法学习——LeetCode力扣动态规划篇6 121. 买卖股票的最佳时机 121. 买卖股票的最佳时机 - 力扣&#xff08;LeetCode&#xff09; 描述 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&…

Transformers -- 深入研究 - part 3

公众号:Halo咯咯,欢迎关注~ 前文回顾: Transformers -- 以通俗易懂的方式解释 - Part 1Transformers -- 未知英雄 - Part 2世界正在为人工智能和生成式人工智能而疯狂,特别是 2023 年的 ChatGPT 和大型语言模型。在我们讨论本系列后续部分的技术细节之前,让我们先从它的想…

用Servlet实现一个简单的表白墙

1. 准备工作 创建项目,引入依赖...... 将静态页面放到项目中(放在webapp目录下): 当前,这个表白墙页面,已经可以输入内容,点击提交之后也能显示内容,后续后端要做的工作即: ①存档 用户点提交的时候,把刚才输入的内容通过网络传输给服务器,由服务器保存这个数据. ②读档 …

NAT地址转换内外网通信

实验要求&#xff1a;内网地址通过nat转换成外网地址&#xff0c;联通外网服务器&#xff0c;达到内网外网互通 拓扑结构&#xff1a; 配置完成后&#xff0c;在ar1的G1口设置抓包&#xff0c;在pc1设备上ping ar2的地址&#xff0c;通过查看抓包信息&#xff0c;可以看到访问…

49 el-input 的 模型 视图 双向同步

前言 这里来看一下 el-input 这边的 数据 和 视图的双向绑定 最开始 我以为 这部分的处理应该是 vue 这边实现的, 但是跟踪调试了一下 发现这部分的处理是业务这边 自己实现的 这部分 还是有一些 值得记录的东西, 从这里 要去理解的而是 vue 这边从宏观的框架上面来说 帮我们…

python如何画奥运五环

绘制奥运五环主要涉及到Python中的turtle绘图库运用&#xff1a; 程序源代码为&#xff1a; import turtle turtle.width(10) turtle.color(black) turtle.circle(50) turtle.penup() turtle.goto(120,0) turtle.pendown() turtle.color(red) turtle.circle(50) turtle.penup()…

HWOD:整型数组排序

一、知识点 while(1){}表示永久循环 使用break结束循环 二、题目 1、描述 输入整型数组和排序标识&#xff0c;对其元素按照升序或降序进行排序 2、数据范围 1<n<1000 0<val<100000 3、输入 第一行输入数组元素个数 第二行输入待排序的数组&#x…

第十四届蓝桥杯(八题C++ 题目+代码+注解)

目录 题目一&#xff08;日期统计 纯暴力&#xff09;&#xff1a; 代码&#xff1a; 题目二&#xff08;01串的熵 模拟&#xff09;&#xff1a; 代码&#xff1a; 题目三&#xff08;治炼金属&#xff09;&#xff1a; 代码&#xff1a; 题目四&#xff08;飞机降落 深度…

【JAVASE】学习数组的定义与使用

✅作者简介&#xff1a;大家好&#xff0c;我是橘橙黄又青&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a; 再无B&#xff5e;U&#xff5e;G-CSDN博客 目标&#xff1a; 1. 理解数组基本概念 2. 掌握数组的基本用法…

星际门计划:微软与OpenAI联手打造未来AI超级计算机

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

windows安装mysql

win r cmd 以管理员权限运行。数据库配置文件my.ini放到安装包里&#xff0c;配置文件内路径根据实际情况修改&#xff0c;配置文件字符集根据实际需要修改。 1、cd c:\mysql\bin切换目录 2、mysqld --initialize --console 初始化数据库&#xff0c;初始化完成…

[操作系统课设]GeeKOS操作系统的研究与实现

一.GeekOS操作系统概论 1.1教学操作系统 &#xff08;1&#xff09;针对RISC结构MIPS处理器 操作系统&#xff1a;Nachos、OS/161 &#xff08;2&#xff09;针对CISC结构Intel IA-32 (or x86)通用处理 操作系统&#xff1a;MINIX、GeekOS 我们用到的是&#xff1a;GeekOS 1&…

SpringMvc执行流程源码解析

一、简介 Spring web Mvc是基于ServletApi构建的原始Web模块&#xff0c;从一开始就包含在Spring框架中&#xff1b; 从Servlet到SpringMvc 最典型的MVc就是JSPServletjavaBean的模式&#xff1b; 弊端&#xff1a; 1、xml下配置Servlet的映射非常麻烦&#xff0c;效率低&…

OpenHarmony实战:命令行工具hdc安装应用指南

一、工具概述 hdc&#xff08;OpenHarmony Device Connector&#xff09;是为开发人员提供的用于设备连接调试的命令行工具&#xff0c;该工具需支持部署在 Windows/Linux/Mac 等系统上与 OpenHarmony 设备&#xff08;或模拟器&#xff09;进行连接调试通信。 简言之&#xf…

16进制的字符串转byte[]数组 以及将字节数组转换成十六进制的字符串

16进制的字符串转byte[]数组 public class ClientString16 {@Testpublic void get16Str(){String str="48 47 12 00 14 12 16 08 15 0d 30 0f 02 30 30 30 30 30 30 30 30 30 30 00 c2";byte[] bytes = hexStringToByteArray(str);getBytetoString(bytes);//String …

Redis实战篇-添加优惠卷

3.3 添加优惠卷 每个店铺都可以发布优惠券&#xff0c;分为平价券和特价券。平价券可以任意购买&#xff0c;而特价券需要秒杀抢购&#xff1a; tb_voucher&#xff1a;优惠券的基本信息&#xff0c;优惠金额、使用规则等 tb_seckill_voucher&#xff1a;优惠券的库存、开始抢…

(一)基于IDEA的JAVA基础10

相信最近许多朋友学习语言可能会有焦虑&#xff0c;“现在人工智能这么发达&#xff0c;丢个指令进去它就还给你一个结果&#xff0c;我们学习它还有意义吗&#xff1f;”。 对于这个问题&#xff0c;就像我们小学学习算数&#xff0c;我们明知道有计算器这么方便的东西&#…

Java 处理Mysql获取树形的数据

Mysql数据&#xff1a; 代码如下&#xff1a; Entity&#xff1a; Data Accessors(chain true) public class Region {private BigInteger id;//名称private String name;//父idprivate BigInteger parentId;private List<Region> children;private Integer createTim…