鸿蒙开发基础运用(ArkTS)-健康生活APP

健康生活应用,主要功能包括:

  1. 用户可以创建最多6个健康生活任务(早起,喝水,吃苹果,每日微笑,刷牙,早睡),并设置任务目标、是否开启提醒、提醒时间、每周任务频率。
  2. 用户可以在主页面对设置的健康生活任务进行打卡,其中早起、每日微笑、刷牙和早睡只需打卡一次即可完成任务,喝水、吃苹果需要根据任务目标量多次打卡完成。
  3. 主页可显示当天的健康生活任务完成进度,当天所有任务都打卡完成后,进度为100%,并且用户的连续打卡天数加一。

实操步骤

应用主页面

启动页

给应用添加一个启动页,启动页里需要用到定时器来实现启动页展示固定时间后跳转应用主页的功能。具体实现逻辑是:

  1. 通过修改entryability里的loadContent路径可以改变应用的入口文件,此处改为SplashPage。
  2. 在SplashPage通过首选项来实现“权限管理”弹窗。如果需要弹窗,用户点击同意后通过首选项对用户的操作做持久化保存。
// EntryAbility.ets
windowStage.loadContent('pages/SplashPage', (err, data) => {if (err.code) {...}Logger.info('windowStage', 'Succeeded in loading the content. Data: ' + JSON.stringify(data))
});// SplashPage.ets
import common from '@ohos.app.ability.common';
import data_preferences from '@ohos.data.preferences';@Entry
@Component
struct SplashIndex {context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;onConfirm() {let preferences = data_preferences.getPreferences(that.context, H_STORE);preferences.then((res) => {res.put(IS_PRIVACY, true).then(() => {res.flush();Logger.info('SplashPage', 'isPrivacy is put success');}).catch((err: Error) => {Logger.info('SplashPage', 'isPrivacy put failed. Cause: ' + err);});})this.jumpAdPage();}exitApp() {this.context.terminateSelf();}jumpAdPage() {setTimeout(() => {router.replaceUrl({ url: 'pages/AdvertisingPage' });}, Const.LAUNCHER_DELAY_TIME);}aboutToAppear() {let preferences = data_preferences.getPreferences(this.context, H_STORE);preferences.then((res) => {res.get(IS_PRIVACY, false).then((isPrivate) => {if (isPrivate === true) {this.jumpAdPage();} else {this.dialogController.open();}});});}build() {...}
}

应用入口

我们需要给APP添加底部菜单栏,用于切换不同的应用模块,由于各个模块之间属于完全独立的情况,并且不需要每次切换都进行界面的刷新,所以我们用到了Tabs,TabContent组件。 本应用一共有首页(HomeIndex),成就(AchievementIndex)和我的(MineIndex)三个模块,分别对应Tabs组件的三个子组件TabContent。

// MainPage.ets
Tabs({ barPosition: BarPosition.End, controller: this.tabController }) {TabContent() {    HomeIndex({ homeStore: $homeStore, editedTaskInfo: $editedTaskInfo, editedTaskID: $editedTaskID }).borderWidth({ bottom: 1 }).borderColor($r('app.color.primaryBgColor'))}.tabBar(this.TabBuilder(TabId.HOME)) .align(Alignment.Start)TabContent() {    AchievementIndex()  }.tabBar(this.TabBuilder(TabId.ACHIEVEMENT))  TabContent() {    MineIndex()}.tabBar(this.TabBuilder(TabId.MINE))
}

首页

首页包含了任务信息的所有入口,包含任务列表的展示,任务的编辑和新增,上下滚动的过程中顶部导航栏的渐变,日期的切换以及随着日期切换界面任务列表跟着同步的功能。具体代码实现我们将在下边分模块进行说明。

  • 导航栏背景渐变

Scroll滚动的过程中,在它的onScrollAction()方法里我们通过计算它Y轴的偏移量来改变当前界面的@State修饰的naviAlpha变量值,进而改变顶部标题的背景色。

// HomeComponent.ets
// 视图滚动的过程中处理导航栏的透明度
@State naviAlpha: number = 0;
...
onScrollAction() {  this.yOffset = this.scroller.currentOffset().yOffset;  if (this.yOffset > Const.DEFAULT_56) {    this.naviAlpha = 1;  } else {    this.naviAlpha = this.yOffset / Const.DEFAULT_56;  }
}
  • 日历组件

日历组件主要用到的是一个横向滑动的Scroll组件。手动滑动页面时,通过在onScrollEndAction()方法里计算Scroll的偏移量来实现分页的效果,同时Scroll有提供scrollPage()方法可供我们点击左右按钮的时候来进行页面切换。需要在Scroll滑动到左边边缘的时候去请求更多的历史数据以便Scroll能一直滑动,通过Scroll的onScrollEdge方法可以判断它是否已滑到边缘位置。homeStore主要是请求数据库的数据并对数据进行处理进而渲染到界面上。同时还需要知道怎么根据当天的日期计算出本周内的所有日期数据。

// WeekCalendarComponent.ets
build() {    Row() {      Column() {        Row() {...}             Scroll(this.scroller) {          Row() {            ForEach(this.homeStore.dateArr, (item: WeekDateModel, index?: number) => {              Column() {                Text(item.weekTitle)                  .fontColor(sameDate(item.date, this.homeStore.showDate) ? $r('app.color.blueColor') : $r('app.color.titleColor'))                                 Divider().color(sameDate(item.date, this.homeStore.showDate) ? $r('app.color.blueColor') : $r('app.color.white'))                Image(this.getProgressImg(item))                               } .onClick(() => WeekCalendarMethods.calenderItemClickAction(item, index, this.homeStore))            })          }       }               .onScrollStop(() => this.onScrollEndAction())        .onScrollEdge((event) => this.onScrollEdgeAction(event))      }}}
}// WeekCalendarComponent.ets
// scroll滚动停止时通过判断偏移量进行分页处理
onScrollEndAction() {// 区分是否是手动滑动,点击左右箭头按钮导致Scroll滑动时不作处理,不然会引起死循环if (this.isPageScroll === false) {let page = Math.round(this.scroller.currentOffset().xOffset / this.scrollWidth);page = (this.isLoadMore === true) ? page + 1 : page;if (this.scroller.currentOffset().xOffset % this.scrollWidth != 0 || this.isLoadMore === true) {let xOffset = page * this.scrollWidth;// 滑动到指定位置this.scroller.scrollTo({ xOffset, yOffset: 0 } as ScrollTo);this.isLoadMore = false;}// 处理当前界面展示的数据  this.currentPage = this.homeStore.dateArr.length / Const.WEEK_DAY_NUM - page - 1;let dayModel: WeekDateModel = this.homeStore.dateArr[Const.WEEK_DAY_NUM * page + this.homeStore.selectedDay];this.homeStore!.setSelectedShowDate(dayModel!.date!.getTime());}this.isPageScroll = false;
}// WeekCalendarComponent.ets
onScrollEdgeAction(side: Edge) {if (side === Edge.Top && this.isPageScroll === false) {Logger.info('HomeIndex', 'onScrollEdge: currentPage ' + this.currentPage);if ((this.currentPage + 2) * Const.WEEK_DAY_NUM >= this.homeStore.dateArr.length) {Logger.info('HomeIndex', 'onScrollEdge: load more data');let date: Date = new Date(this.homeStore.showDate);date.setDate(date.getDate() - Const.WEEK_DAY_NUM);that.homeStore.getPreWeekData(date, () => {});this.isLoadMore = true;}}
}// HomeViewModel.ets
public getPreWeekData(date: Date, callback: Function) {let weekCalendarInfo: WeekCalendarInfo = getPreviousWeek(date);    // 请求数据库数据DayInfoApi.queryList(weekCalendarInfo.strArr, (res: DayInfo[]) => {// 数据处理...  this.dateArr = weekCalendarInfo.arr.concat(...this.dateArr);})
}// WeekCalendarModel.ets
export function getPreviousWeek(showDate: Date): WeekCalendarInfo {let weekCalendarInfo: WeekCalendarInfo = new WeekCalendarInfo();let arr: Array<WeekDateModel> = [];let strArr: Array<string> = []; // 由于date的getDay()方法返回的是0-6代表周日到周六,我们界面上展示的周一-周日为一周,所以这里要将getDay()数据偏移一天let currentDay = showDate.getDay() - 1;// 将日期设置为当前周第一天的数据(周一)showDate.setDate(showDate.getDate() - currentDay);for (let index = WEEK_DAY_NUM; index > 0; index--) {let tempDate = new Date(showDate);tempDate.setDate(showDate.getDate() - index);let dateStr = dateToStr(tempDate);strArr.push(dateStr);arr.push(new WeekDateModel(WEEK_TITLES[tempDate.getDay()], dateStr, tempDate));}weekCalendarInfo.arr = arr;weekCalendarInfo.strArr = strArr;return weekCalendarInfo;
}
  • 悬浮按钮

由于首页右下角有一个悬浮按钮,所以首页整体我们用了一个Stack组件,将右下角的悬浮按钮和顶部的title放在滚动组件层的上边。

// HomeComponent.ets
build() {  Stack() {    Scroll(this.scroller) {      Column() {     ...Column() {          ForEach(this.homeStore.getTaskListOfDay(), (item: TaskInfo) => {         TaskCard({taskInfoStr: JSON.stringify(item),clickAction: (isClick: boolean) => this.taskItemAction(item, isClick)}) }, (item: TaskInfo) => JSON.stringify(item))}   }}.onScroll(() => { this.onScrollAction() })// 悬浮按钮AddBtn({ clickAction: () => { this.editTaskAction() } })  // 顶部title Row() {       Text($r('app.string.EntryAbility_label'))}.position({ x: 0, y: 0 })    .backgroundColor(`rgba(${WHITE_COLOR_0X},${WHITE_COLOR_0X},${WHITE_COLOR_0X},${this.naviAlpha})`)    CustomDialogView()  }  .allSize() .backgroundColor($r('app.color.primaryBgColor'))
}
  • 界面跳转及传参

首页任务列表长按时需要跳转到对应的任务编辑界面,同时点击悬浮按钮时需要跳转到任务列表页面。页面跳转需要在头部引入router。

// HomeComponent.ets
import router from '@ohos.router';taskItemAction(item: TaskInfo, isClick: boolean): void {if (!this.homeStore.checkCurrentDay()) {return;}if (isClick) {// 点击任务打卡let callback: CustomDialogCallback ={ confirmCallback: (taskTemp: TaskInfo) => { this.onConfirm(taskTemp) }, cancelCallback: () => {} };this.broadCast.emit(BroadCastType.SHOW_TASK_DETAIL_DIALOG, [item, callback]);} else {// 长按编辑任务let editTaskStr: string = JSON.stringify(TaskMapById[item.taskID - 1]);let editTask: ITaskItem = JSON.parse(editTaskStr);editTask.targetValue = item?.targetValue;editTask.isAlarm = item.isAlarm;editTask.startTime = item.startTime;editTask.frequency = item.frequency;editTask.isOpen = item.isOpen;router.pushUrl({ url: 'pages/TaskEditPage', params: { params: JSON.stringify(editTask) } });}
}

任务创建与编辑

功能概述

用户点击悬浮按钮进入任务列表页,点击任务列表可进入对应任务编辑的页面中,对任务进行详细的设置,之后点击完成按钮编辑任务后将返回首页。实现效果如图所示。

任务列表

  • 任务列表页

任务列表页由上部分的标题、返回按钮以及正中间的任务列表组成。使用Navigation以及List组件构成元素,使用ForEach遍历生成具体列表。这里是Navigation构成页面导航,实现效果如图所示:

// TaskListPage.ets
Navigation() {Column() {// 页面中间的列表TaskList()}.width(Const.THOUSANDTH_1000).justifyContent(FlexAlign.Center)
}
.size({ width: Const.THOUSANDTH_1000, height: Const.THOUSANDTH_1000 })
.title(Const.ADD_TASK_TITLE)
.titleMode(NavigationTitleMode.Mini)

列表右侧有一个判断是否开启的文字标识,点击某个列表需要跳转到对应的任务编辑页里。具体的列表实现:

// TaskListComponent.ets
List({ space: Const.LIST_ITEM_SPACE }) {ForEach(this.taskList, (item: ITaskItem) => {ListItem() {Row() {Row() {Image(item?.icon)Text(item?.taskName)...}.width(Const.THOUSANDTH_500)Blank().layoutWeight(1)// 状态显示if (item?.isOpen) {Text($r('app.string.already_open'))}Image($r('app.media.ic_right_grey')).width(Const.DEFAULT_8).height(Const.DEFAULT_16)}...}...// 路由跳转到任务编辑页.onClick(() => {router.pushUrl({url: 'pages/TaskEditPage',params: {params: formatParams(item)}})})...})
}

任务编辑

  • 任务编辑页

任务编辑页由上方的“编辑任务”标题以及返回按钮,主体内容的List配置项和下方的完成按钮组成。任务编辑页面,由Navigation和一个自定义组件TaskDetail构成。自定义组件由List以及其子组件ListItem构成,实现效果如图所示:

// TaskEditPage.ets
Navigation() {Column() {TaskDetail()}.width(Const.THOUSANDTH_1000).height(Const.THOUSANDTH_1000)
}
.size({ width: Const.THOUSANDTH_1000, height: Const.THOUSANDTH_1000 })
.title(Const.EDIT_TASK_TITLE).titleMode(NavigationTitleMode.Mini)// TaskDetailComponent.ets
List({ space: Const.LIST_ITEM_SPACE }) {ListItem() {TaskChooseItem()}.listItemStyle()ListItem() {TargetSetItem()}.listItemStyle()// 一些特殊情况的禁用,如每日微笑、每日刷牙的目标设置不可编辑.enabled( this.settingParams?.isOpen&& this.settingParams?.taskID !== taskType.smile&& this.settingParams?.taskID !== taskType.brushTeeth).onClick(() => {this.broadCast.emit(BroadCastType.SHOW_TARGET_SETTING_DIALOG);})ListItem() {OpenRemindItem()}.listItemStyle()// 其中做了禁用判断,需要任务打开才可以点击编辑.enabled(this.settingParams?.isOpen)ListItem() {RemindTimeItem()}.listItemStyle()// 提醒时间在开启提醒打开之后才可以编辑.enabled(this.settingParams?.isOpen && this.settingParams?.isAlarm).onClick(() => {this.broadCast.emit(BroadCastType.SHOW_REMIND_TIME_DIALOG);})ListItem() {FrequencyItem()}.listItemStyle().enabled(this.settingParams?.isOpen && this.settingParams?.isAlarm).onClick(() => {this.broadCast.emit(BroadCastType.SHOW_FREQUENCY_DIALOG);})
}
.width(Const.THOUSANDTH_940)// TaskDetailComponent.ets
addTask({// 相关参数...
})
.then((res: number) => {// 成功的状态,成功后跳转首页GlobalContext.getContext().setObject('taskListChange', true);router.back({url: 'pages/MainPage',params: {editTask: this.backIndexParams()}})Logger.info('addTaskFinished', JSON.stringify(res));
})
.catch((error: Error) => {// 失败的状态,失败后弹出提示,并打印错误日志prompt.showToast({message: Const.SETTING_FINISH_FAILED_MESSAGE})Logger.error('addTaskFailed', JSON.stringify(error));
})

  • 任务编辑弹窗

在自定义弹窗CustomDialogView组件内注册打开弹窗的事件,当点击对应任务的编辑项时触发该事件,进而打开弹窗。

// TaskDialogView.ets
targetSettingDialog: CustomDialogController = new CustomDialogController({builder: TargetSettingDialog(),autoCancel: true,alignment: DialogAlignment.Bottom,offset: { dx: Const.ZERO, dy: Const.MINUS_20 }
});
...// 注册事件
this.broadCast.on(BroadCastType.SHOW_TARGETSETTING_DIALOG, () => {this.targetSettingDialog.open();
})// HomeComponent.ets
taskItemAction(item: TaskInfo, isClick: boolean): void {...if (isClick) {let callback: CustomDialogCallback ={ confirmCallback: (taskTemp: TaskInfo) => { this.onConfirm(taskTemp) }, cancelCallback: () => {} };this.broadCast.emit(BroadCastType.SHOW_TASK_DETAIL_DIALOG, [item, callback]);} else {...}
}

任务目标设置有三种类型,早睡早起的时间、喝水的量度、吃苹果的个数。故根据任务的ID进行区分,将同一弹窗复用,如图所示:

其余弹窗实现基本类似,这里不再赘述。

// TaskSettingDialog.ets
if ([taskType.getup, taskType.sleepEarly].indexOf(this.settingParams?.taskID) > Const.HAS_NO_INDEX) {TimePicker({selected: new Date(`${new Date().toDateString()} 8:00:00`),})...
} else {TextPicker({ range: this.settingParams?.taskID === taskType.drinkWater ? this.drinkRange : this.appleRange })...
}// TaskSettingDialog.ets
// 校验规则
compareTime(startTime: string, endTime: string) {if (returnTimeStamp(this.currentTime) < returnTimeStamp(startTime) || returnTimeStamp(this.currentTime) > returnTimeStamp(endTime)) {// 弹出提示prompt.showToast({message: commonConst.CHOOSE_TIME_OUT_RANGE})return false;}return true;
}// 设置修改项
setTargetValue() {...if (this.settingParams?.taskID === taskType.sleepEarly) {if (!this.compareTime(commonConst.SLEEP_EARLY_TIME, commonConst.SLEEP_LATE_TIME)) {return;}this.settingParams.targetValue = this.currentTime;return;}this.settingParams.targetValue = this.currentValue;
}

本文是鸿蒙开发中对OpenHarmony技术的简单运用,更多的鸿蒙开发技术可以前往我主页查询,下面分享鸿蒙开发4.0技术分布(略缩图):

高清完整版技术学习路线图如下寻找保存(附鸿蒙文档)

最后是效果如图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

迟到的总结:回望 2023 年,期盼 2024 新机会、新挑战

&#x1f52d; 嗨&#xff0c;您好 &#x1f44b; 我是 vnjohn&#xff0c;在互联网企业担任 Java 开发&#xff0c;CSDN 优质创作者 &#x1f4d6; 推荐专栏&#xff1a;Spring、MySQL、Nacos、RocketMQ&#xff0c;后续其他专栏会持续优化更新迭代 &#x1f332;文章所在专栏…

航空公司管理系统(迷你版12306)

要求 今天分享一个之前辅导留学生的作业&#xff0c;作业要求如下&#xff1a; Project E: Airways Management System Overall description: Your team is employed by an Airways company for the implementation of a computer system responsible for a large part of th…

Python从入门到网络爬虫(异常处理详解)

前言 异常即是一个事件&#xff0c;该事件会在程序执行过程中发生&#xff0c;影响了程序的正常执行。一般情况下&#xff0c;在python无法正常处理程序时就会发生一个异常。异常是python对象&#xff0c;表示一个错误。当python脚本发生异常时我们需要捕获处理它&#xff0c;…

Python 全栈体系【四阶】(十一)

第四章 机器学习 机器学习&#xff1a; 传统的机器学习&#xff1a;以算法为核心深度学习&#xff1a;以数据和计算为核心 感知机 perceptron&#xff08;人工神经元&#xff09; 可以做简单的分类任务掀起了第一波 AI 浪潮 感知机不能解决线性不可分问题&#xff0c;浪潮…

Git提交代码发生冲突的场景与解决方案

问题 当我们在使用 Git 向远程仓库提交代码时&#xff0c;可能会遇到如下所述的错误提示&#xff1a; To https://github.com/xxxxx/gitmerge.git! [rejected] master -> master (fetch first) error: failed to push some refs to https://github.com/xxxxx/gitme…

Vue2:脚手架Vue-CLI的使用

一、环境准备 vue脚手架&#xff08;vue-CLI&#xff09;的使用是基于nodejs环境下的。 你可以简单理解为&#xff0c;Java项目需要再jvm虚拟机上才能编译运行 nodejs的作用就是将vue文件编译成html、css、js代码文件。 如何安装nodejs 参考&#xff1a;https://blog.csdn.net…

ARMv8-AArch64 的异常处理模型详解之异常等级、执行状态以及安全状态

ARMv8-AArch64 的异常处理模型详解 一&#xff0c;特权和异常等级1.1 异常等级 Exception levels 二&#xff0c;特权的类型2.1 内存特权2.2 访问寄存器的特权 三&#xff0c;执行状态和安全状态3.1 执行状态 Execution states3.2 执行状态切换 3.3 安全状态 Security states3.…

第16课 播放rtsp流

在现实生活中有许多rtsp摄像头&#xff0c;这些摄像头如果能充分利用起来可以生成很多有趣、有用的应用&#xff1a;比如户外互动大屏等。在第4课&#xff0c;我们实现了一个播放器&#xff0c;当时来用它播放rtmp流和mp4时它好象工作的很好。这节课我们就用它来播放rtsp流试试…

二进制介绍

十进制转相应进制 (十进制)231 转 八进制 除八取余法 从下而上取余 231/828 ....7 28/83.......4 3/80........3 (十进制)231(八进制)0o347 (十进制)231 转 16进制 除十六取余法 从下而上取余 231/1614......7 14/160..........14 (十进制) 231(十六进制)0xe7 (十进制)231.3 转…

C++动态内存分配(动态内存分配函数)栈区

内存拷贝函数&#xff1a; void *memcpy(void *dest,const void *src,size_tn);#include<string.h>功能&#xff1a;从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest内存地址的起始位置 分配内存使用new 例如&#xff1a;salarynew int[num]; 最后需要释放…

Linux文件操作命令(touch、cat、more、cp、mv、rm)

之前我们学习了对目录&#xff08;即文件夹的操作&#xff0c;那么现在我们来一起看一下怎么操作文件吧&#xff09; 1.touch命令 功能&#xff1a;创建文件 语法&#xff1a;touch 参数 参数&#xff1a;被创建的文件路径 注意&#xff1a;touch命令无选项&#xff0c;参…

【Docker-Dev】Mac M2 搭建docker的redis环境

Redis的dev环境docker搭建 1、前言2、官方文档重点信息提取2.1、创建redis实例2.2、使用自己的redis.conf文件。 3、单机版redis搭建4、redis集群版4.1、一些验证4.2、一些问题 结语 1、前言 本文主要针对M2下&#xff0c;相应进行开发环境搭建&#xff0c;然后做一个文档记录…

面试算法88:爬楼梯的最少成本

题目 一个数组cost的所有数字都是正数&#xff0c;它的第i个数字表示在一个楼梯的第i级台阶往上爬的成本&#xff0c;在支付了成本cost[i]之后可以从第i级台阶往上爬1级或2级。假设台阶至少有2级&#xff0c;既可以从第0级台阶出发&#xff0c;也可以从第1级台阶出发&#xff…

window使用cpolar实现内网穿透

文章目录 cpolar下载和安装启动和配置cpolar卸载 cpolar下载和安装 进入spolar官网&#xff0c;完成注册&#xff0c;下载相应的cploar版本解压和运行安装文件 配置安装路径&#xff0c;然后选择next&#xff0c;完成即可 启动和配置 点击首页的快捷图标打开网页&#xf…

Defi安全-Monox攻击事件Foundry复现

其它相关内容可见个人主页 Mono攻击事件的介绍见&#xff1a;Defi安全–Monox攻击事件分析–phalconetherscan 1. 前情提要和思路介绍 Monox使用单边池模型&#xff0c;创建的是代币-vCash交易对&#xff0c;添加流动性时&#xff0c;只需添加代币&#xff0c;即可进行任意代…

Jmeter相关概念

Jmeter相关概念 jmeter性能指标 Aggregate Report 是 JMeter 常用的一个 Listener&#xff0c;中文被翻译为“聚合报告”。今天再次有同行问到这个报告中的各项数据表示什么意思&#xff0c;顺便在这里公布一下&#xff0c;以备大家查阅。 如果大家都是做Web应用的性能测试&a…

C语言中关于函数调用的理解

理论 关于函数调用的方式有两类&#xff1a;传值调用和传址调用 传值调用&#xff1a;函数的形参和实参分别占有不同的内存块&#xff0c;对形参的修改不会影响实参。 传址调用&#xff1a;把函数外部创建变量的内存地址传递给函数参数的一种调用方式。可以让函数和函数外面…

每周一算法:倍增法求区间最大最小值(RMQ)

RMQ RMQ 是英文 Range Maximum/Minimum Query 的缩写&#xff0c;表示区间最大&#xff08;最小&#xff09;值。使用倍增思想解决 RMQ 问题的方法是 ST 表&#xff08;Sparse Table&#xff0c; 稀疏表 &#xff09;。ST 表是用于解决 可重复贡献问题 的数据结构。 可重复贡献…

IPv6和IPv4在技术层面的区别

随着互联网的不断发展&#xff0c;IPv4地址资源已经逐渐枯竭&#xff0c;而IPv6地址的使用逐渐成为趋势。IPv6和IPv4作为互联网协议的两个版本&#xff0c;在技术层面存在许多区别。本文将从地址空间、地址表示方法、路由协议、安全性、移动性以及网络性能等方面对IPv6和IPv4进…

Dockerfile的ENV

文章目录 环境总结测试测试1测试2测试3测试4测试5测试6 参考 环境 RHEL 9.3Docker Community 24.0.7 总结 如果懒得看测试的详细信息&#xff0c;可以直接看结果&#xff1a; 一条 ENV 指令可以定义多个环境变量。Dockerfile里可以包含多条 ENV 指令。环境变量的值不需要用…