HarmonyOS NEXT应用开发之Navigation实现多设备适配案例

介绍

在应用开发时,一个应用需要适配多终端的设备,使用Navigationmode属性来实现一套代码,多终端适配。

效果图预览

使用说明

  1. 将程序运行在折叠屏手机或者平板上观看适配效果。

实现思路

本例涉及的关键特性和实现方案如下:

1.分屏的使用

首先介绍的是本案例的关键特性Navigationmode属性,原先采用的是NavigationMode.Stack,导航栏与内容区独立显示,相当于两个页面。 那么使用NavigationMode.Auto,在API version 10及以上:窗口宽度>=600vp时,采用Split模式显示;窗口宽度<600vp时,采用Stack模式显示。
源码参考EntryView.ets

Navigation(this.pageStack) { ... }
  .backgroundColor($r('app.color.main_background_color'))
  .hideTitleBar(true)
  .navBarWidth(this.isFullScreen)
  .navDestination(this.pageMap)
  .mode(NavigationMode.Auto)
2.模块全屏的使用以及Bug解决

EntryViewNavigation中设置navBarWidth,其值设置为由@Provide装饰器装饰过的变量,默认值为50%,作用是为了适配需要全屏的模块。 目前只有折叠屏音乐播放器案例需要全屏,在对应模块的实现文件声明由@Consume装饰器装饰过的变量,更改变量的值就可以实现与后代组件双向同步的通信,从而改变 EntryViewnavBarWidth的宽度。
这只实现了全屏变换,可是原来Navigation更换模块时会有一个动画,会与全屏变换动画冲突,右半屏会出现上一个模块画面一闪而过的bug。这需要更改isNeedFullScreen的值来控制 路由跳转时是否有动画显示。
源码参考:
DynamicsRouter.ets
MusicPlayerInfoComp.ets
EntryView.ets

 // EntryView.ets
 ...
 @Provide('isFullScreen') isFullScreen: string = '50%';
 ...
 Navigation(this.pageStack) { ... }
   .backgroundColor($r('app.color.main_background_color'))
   .hideTitleBar(true)
   .navBarWidth(this.isFullScreen)
   .navDestination(this.pageMap)
   .mode(NavigationMode.Auto)
 ...
 
 
 // MusicPlayerInfoComp.ets
 ...
 @Consume('isFullScreen') isFullScreen: string;
 ...
 aboutToAppear(): void {
   ...
   // 隐藏navigation导航栏,设置为全屏。
   animateTo({
     duration: 400,
     curve: Curve.EaseInOut,
   }, () => {
     this.isFullScreen = '0.01%';
   })
   ...
 }
 ...
 build() {
   Flex({ direction: this.curFlexDirection }) {
     Image($r('app.media.ic_public_back'))
       .width($r('app.integer.font_size_music_title'))
       .height($r('app.integer.font_size_music_title'))
       .onClick(() => {
         DynamicsRouter.pop()
         // 呈现navigation导航栏,设置为半屏。
         animateTo({
           duration: 200,
           curve: Curve.EaseInOut,
         }, () => {
           this.isFullScreen = '50%';
         })
       })
       .position({ x: $r('app.integer.image_position_x'), y: $r('app.integer.image_position_y') })
       .fillColor(Color.White)
     ...
   }
 } 
  
  
 //DynamicsRouter.ets
 public static async push(routerInfo: RouterInfo, param?: string): Promise<void> {
   ...
   // 模块是否需要转场动画
   let isNeedFullScreen: boolean = true;
   // TODO:知识点:通过动态import的方式引入模块,在需要进入页面时才加载模块,可以减少主页面的初始化时间及占用的内存
   await import(moduleName).then((result: ESObject) => {
     ...
     if (FullScreenArray.includes(moduleName)) {
       isNeedFullScreen = false;
     }
   }, (error: ESObject) => {
     // 动态加载模块失败时,打印错误日志
     logger.error(LOGGER_TAG, error);
   });
   if (isImportSucceed) {
     ...
     // 查找到对应的路由栈进行跳转
     DynamicsRouter.getNavPathStack().pushPath({ name: builderName, param: param }, isNeedFullScreen);
     ...
   }
 }
3.主页Navigation弹出路由栈

手机的Navigation采用Stack模式,手势右滑退出会自动pop路由栈,但是采用分栏可以直接点击跳转到下一模块,那么就需要在点击瀑布流的FlowItem的时刻pop上一个路由栈。
源码参考FunctionalScenes.ets。

  @Builder
  methodPoints(listData: SceneModuleInfo) {
    ...
    .onClick(() => {
      DynamicsRouter.pop();
      // 点击瀑布流Item时,根据点击的模块信息,将页面放入路由栈
      DynamicsRouter.push(listData.routerInfo, listData.param);
    })
  }

FAQ

1.页面间共享组件实例模块的适配问题

页面间共享组件实例模块中也写了Navigation组件,想要展示的效果是Stack模式,但是半屏的平板的宽度也大于600,被系统自动认为采用Split模式。
页面间共享组件实例模块中还绑定了半模态,并未设置preferType(半模态页面的样式)。设备宽度小于600vp时,默认显示底部弹窗样式。 设备宽度在600-840vp间时,默认显示居中弹窗样式。设备宽度大于840vp时,默认显示跟手弹窗样式,跟手弹窗显示在bindSheet绑定的节点下方。平板宽度大于840vp,跟手弹窗显示在节点下方导致弹窗不可见。 所以通过设备宽度来设置preferType的样式。
源码参考:
ComponentSharedInPages.ets
TakeTaxiDetailPage.ets

  //ComponentSharedInPages.ets
  build() {
    Stack({alignContent: Alignment.Bottom}) {
      ...
      // 应用主页用NavDestination承载,Navigation为空页面直接跳转到MainPage主页面
      Navigation(this.pageStackForComponentSharedPages) {
      }
      ...
      .mode(NavigationMode.Stack)
    }
    ...
  }
  
  
  //TakeTaxiDetailPage.ets
  ...
  @State windowWidth: number = 0;
  readonly componentsWindowWidth: number = 600;
  ...
  /**
   * 获取应用主窗口的宽高
   */
  aboutToAppear() {
    window.getLastWindow(getContext(this), (err: BusinessError, data: window.Window) => {
      let rect: window.Rect = data.getWindowProperties().windowRect;
      this.windowWidth = px2vp(rect.width);
    })
  }
  ...
  build() {
    NavDestination() {
      ...
      // 绑定上半模态页面,用于显示内容
      .bindSheet($$this.isShow, this.taxiContentBuilder(),
        {
          detents: TakeTaxiPageCommonConstants.SHEET_DETENTS,
          preferType: this.windowWidth > this.componentsWindowWidth ? SheetType.CENTER : SheetType.POPUP,
          ...
        }
      )
    }
    ...
  }
2.底部抽屉滑动效果模块的适配问题

底部抽屉滑动效果模块中写了一个Image组件,其资源是一个很大的地图图片,在分栏效果展示时Image图片资源会拦截Navigation导航栏的点击或者拖拽事件,可以采用Columnclip属性将超出Image的图片裁掉。
源码参考:Component.ets。

 build() {
   Column() {
     // 背景地图图片
     Image($r('app.media.map'))
       .id("bg_img")
       .height($r('app.integer.number_2000'))
       .width($r('app.integer.number_2000'))
       .translate({ x: this.offsetX, y: this.offsetY })// 以组件左上角为坐标原点进行移动
       .draggable(false) // 单指操作拖动背景地图
    }.width('100%')
    .height('100%')
    .clip(true) // 地图图片超出页面区域时裁剪掉
    ...
 }
3.适配挖孔屏模块的适配问题

适配挖孔屏模块Image组件采用ImageFit.Cover填充图片,导致图片显示不完整,采用ImageFit.Fill,虽然图片变扁了,但是能完整显示,不影响具体功能。
源码参考:DiggingHoleScreen.ets。

Image($r('app.media.2048game'))
  .objectFit(ImageFit.Fill)
  .width('100%')
  .height('100%')
4.左右拖动切换图片模块的适配问题

左右拖动切换图片模块主要功能要实时记录手势拖动的距离,以此来进行计算,所以宽度和高度要写固定数值,不能使用百分比。但是折叠屏手机折叠后会出现超出屏幕的情况,可采用缩小组件宽度的方式适配。
源码参考:
DragToSwitchPicturesView.ets
Constants.ets
integer.json

// DragToSwitchPicturesView.ets
@State dragRefOffset: number = 0; // 用来记录每次图标拖动的距离
@State imageWidth: number = 160; // 用来记录每次图标拖动完成后左侧Image的width宽度
@State leftImageWidth: number = 160; // 用来记录每次图标拖动时左侧Image的实时width宽度
@State rightImageWidth: number = 160; // 用来记录每次图标拖动时右侧Image的实时width宽度
...
PanGesture({ fingers: CONFIGURATION.PANGESTURE_FINGERS, distance: CONFIGURATION.PANGESTURE_DISTANCE })
  .onActionStart(() => {
    this.dragRefOffset = CONFIGURATION.INIT_VALUE; // 每次拖动开始时将图标拖动的距离初始化。
  })
    // TODO: 性能知识点: 该函数是系统高频回调函数,避免在函数中进行冗余或耗时操作,例如应该减少或避免在函数打印日志,会有较大的性能损耗。
  .onActionUpdate((event: GestureEvent) => {
    // 通过监听GestureEvent事件,实时监听图标拖动距离
    this.dragRefOffset = event.offsetX;
    this.leftImageWidth = this.imageWidth + this.dragRefOffset;
    this.rightImageWidth = CONFIGURATION.IMAGE_FULL_SIZE - this.leftImageWidth;
    if (this.leftImageWidth >= CONFIGURATION.LEFT_IMAGE_RIGHT_LIMIT_SIZE) { // 当leftImageWidth大于等于310vp时,设置左右Image为固定值,实现停止滑动效果。
      this.leftImageWidth = CONFIGURATION.LEFT_IMAGE_RIGHT_LIMIT_SIZE;
      this.rightImageWidth = CONFIGURATION.RIGHT_IMAGE_RIGHT_LIMIT_SIZE;
    } else if (this.leftImageWidth <= CONFIGURATION.LEFT_IMAGE_LEFT_LIMIT_SIZE) { // 当leftImageWidth小于等于30vp时,设置左右Image为固定值,实现停止滑动效果。
      this.leftImageWidth = CONFIGURATION.LEFT_IMAGE_LEFT_LIMIT_SIZE;
      this.rightImageWidth = CONFIGURATION.RIGHT_IMAGE_LEFT_LIMIT_SIZE;
    }
  })
  .onActionEnd((event: GestureEvent) => {
    if (this.leftImageWidth <= CONFIGURATION.LEFT_IMAGE_LEFT_LIMIT_SIZE) {
      this.leftImageWidth = CONFIGURATION.LEFT_IMAGE_LEFT_LIMIT_SIZE;
      this.rightImageWidth = CONFIGURATION.RIGHT_IMAGE_LEFT_LIMIT_SIZE;
      this.imageWidth = CONFIGURATION.LEFT_IMAGE_LEFT_LIMIT_SIZE;
    } else if (this.leftImageWidth >= CONFIGURATION.LEFT_IMAGE_RIGHT_LIMIT_SIZE) {
      this.leftImageWidth = CONFIGURATION.LEFT_IMAGE_RIGHT_LIMIT_SIZE;
      this.rightImageWidth = CONFIGURATION.RIGHT_IMAGE_RIGHT_LIMIT_SIZE;
      this.imageWidth = CONFIGURATION.LEFT_IMAGE_RIGHT_LIMIT_SIZE;
    } else {
      this.leftImageWidth = this.imageWidth + this.dragRefOffset; // 滑动结束时leftImageWidth等于左边原有Width+拖动距离。
      this.rightImageWidth = CONFIGURATION.IMAGE_FULL_SIZE - this.leftImageWidth; // 滑动结束时rightImageWidth等于340-leftImageWidth。
      this.imageWidth = this.leftImageWidth; // 滑动结束时ImageWidth等于leftImageWidth。
    }
  })
5.图片压缩模块的适配问题

图片压缩模块中Text组件的字号在折叠手机屏折叠状态下过大,文本会超出屏幕,可采取缩小字号适配。
源码参考:ImageCompression.ets

6.图片缩放模块的适配问题

图片缩放模块中Image组件的宽度和高度由窗口的宽度和高度决定。由于屏幕宽度大于600vp要分栏,会导致图片过大。所以要判断是否分栏,若分栏则windowWidth的宽度减半。
源码参考:ImageContentView.ets

   ...
   @State windowWidth: number = 0;
   @State windowHeight: number = 0;
   ...
   /**
   * 获取应用主窗口的宽高
   */
  aboutToAppear() {
    window.getLastWindow(getContext(this), (err: BusinessError, data: window.Window) => {
      let rect: window.Rect = data.getWindowProperties().windowRect;
      this.windowWidth = px2vp(rect.width);
      this.windowHeight = px2vp(rect.height);
      if (this.windowWidth > this.componentsWindowWidth) {
        this.windowWidth = this.windowWidth / 2;
      }
      data.on("windowSizeChange", (size: window.Size) => {
        this.windowWidth = px2vp(size.width);
        this.windowHeight = px2vp(size.height);
        if (this.windowWidth > this.componentsWindowWidth) {
          this.windowWidth = this.windowWidth / 2;
        }
      })
    })
  }
  ...
  Image(this.image)
    .width(this.windowWidth * this.imageScale.scaleValue)
    .height(this.windowHeight * this.imageScale.scaleValue)
    ...    
7.元素超出List区域模块的适配问题

元素超出List区域模块中使用ListitemGroup组件实现卡片样式,在折叠屏中展开时并未布局满全屏,原因是设置ListItemGroupStyle.CARD时,必须配合ListItemListItemStyle.CARD使用。
源码参考:AboutMe.ets

ListItemGroup({ style: ListItemGroupStyle.CARD }) {
  ListItem({ style: ListItemStyle.CARD }) {
    ...
  }.height($r("app.integer.itemoverflow_default_item_height"))
  .toastOnClick($r("app.string.listitem_overflow_toast_no_edit"))

  ListItem({ style: ListItemStyle.CARD }) {
    ...
  }.height($r("app.integer.itemoverflow_default_item_height"))
  .toastOnClick($r("app.string.listitem_overflow_toast_no_edit"))
}
.divider({ strokeWidth: 1, color: $r('app.color.aboubtme_pageBcColor') })

ListItemGroup({ style: ListItemGroupStyle.CARD }) {
  ListItem({ style: ListItemStyle.CARD }) {
    ...
  }.height($r("app.integer.itemoverflow_default_item_height"))
  .toastOnClick($r("app.string.listitem_overflow_toast_no_card"))
}
    ...

ListItemGroup({ style: ListItemGroupStyle.CARD }) {
  ListItem({ style: ListItemStyle.CARD }) {
    ...
  .toastOnClick($r("app.string.listitem_overflow_toast_no_favorite"))

  ListItem({ style: ListItemStyle.CARD }) {
    ...
  }.height($r("app.integer.itemoverflow_default_item_height"))
  .toastOnClick($r("app.string.listitem_overflow_toast_no_settings"))

  ListItem({ style: ListItemStyle.CARD }) {
    ...
  }.height($r("app.integer.itemoverflow_default_item_height"))
  .toastOnClick($r("app.string.listitem_overflow_toast_about"))
}
    ...
8.听歌识曲水波纹特效模块的适配问题

听歌识曲水波纹特效模块中使用Column容器搭配margin进行布局,但是在不同设备中就不适配了。可以使用justifyContent属性设置子组件在垂直方向上的对齐格式,再搭配margin就可适配多种终端。
源码参考:WaterRipples.ets

Column() {
  Text($r('app.string.sound_hound'))
    .fontColor(Color.White)
    .fontSize(18)
    .margin({ top: $r('app.integer.margin_large') })

  ButtonWithWaterRipples({ isListening: this.isListening })

  Text(this.isListening ? $r('app.string.is_listening') : $r('app.string.click_to_listen'))
    .fontColor(Color.White)
    .margin({ bottom: $r('app.integer.margin_large') })
}
.backgroundColor(Color.Black)
.justifyContent(FlexAlign.SpaceBetween)
.width("100%")
.height("100%")
9.模块资源命名重名

模块资源重复导致模块显示错误,修改资源命名,最好在新命名前面加上自己的模块名称。

{
  "name": "navigationparametertransferview_user_name",
  "value": "用户姓名:"
}

{
  "name": "aboubtme_pageBcColor",
  "value": "#fff1f3f5"
}

{
  "name": "customsafekeyboard_placeholder",
  "value": "请输入密码"
}

参考资料

Navigation

clip

@Provide装饰器和@Consume装饰器:与后代组件双向同步

半模态转场

Image

Column

ListItemGroup

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:

如何快速入门:https://qr21.cn/FV7h05

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

开发基础知识:https://qr21.cn/FV7h05

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

基于ArkTS 开发:https://qr21.cn/FV7h05

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

鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向

腾讯T10级高工技术,安卓全套VIP课程全网免费送:https://qr21.cn/D2k9D5

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

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

相关文章

关于 hbuild 真机调试:

当手机插上数据线&#xff0c;刷新&#xff0c;依旧找不到手机列表时&#xff0c;点击“故障排查指南” 参考官网&#xff1a;https://uniapp.dcloud.net.cn/tutorial/run/run-app-faq.html 操作步骤&#xff1a; 1、在手机设置中打开开发者模式&#xff08;根据不同手机打开…

postman进阶功能学习,别再简单的发请求了!

1.Postman数据驱动 想要批量执行接口用例&#xff0c;我们一般会将对应的接口用例放在同一个Collection中&#xff0c;然后再通过Runner批量执行。这种方式适用于接口用例参数固定的情况下&#xff0c;但也存在另一个问题&#xff0c;如果每次运行时&#xff0c;接口参数都在变…

易百纳诚挚邀请,Meetup易百纳技术社区工程师见面会,与您不见不散!

2024年3月29日(周五)&#xff0c;易百纳将携手openEuler社区&#xff0c;南京邮电大学共同举办一场openEuler Embedded Meetup会议。 本次交流活动将邀请多位业内专家围绕嵌入式前沿技术、应用案例、创新方向、芯片开发板优秀实践几个方面进行分享&#xff0c;在活动上还将成立…

在Arm 虚拟硬件(AVH)部署深度学习OCR算法

AI算法的嵌入式部署 AI算法在独立的设备上运行其实就是行业内的嵌入式AI的概念, 大致过程如下: 开发AI模型, 2.对数据集进行处理, 3.训练AI模型并验证效果, 4.转成ONNX格式(ONNX:万金油中间格式,给模型优化和部署带来了更多可能性)或者借助libtorch或者TensorFlow来部署C++版…

Java面试题总结18之springcloud四种分布式事务解决方案

XA规范&#xff1a;分布式事务规范&#xff0c;规定了分布式事务模型 四个角色&#xff1a;事务管理器&#xff08;协调者TM&#xff09;&#xff0c;资源管理器&#xff08;参与者RM&#xff09;&#xff0c;应用程序AP&#xff0c;通信资源管理器CRM 全局事务&#xff1a;一…

螺栓拧紧扭矩测量的原理、方法和影响因素——SunTorque智能扭矩系统

智能扭矩系统-智能拧紧系统-扭矩自动控制系统-SunTorque 螺栓拧紧扭矩测量是确保螺栓连接紧固性和可靠性的重要环节。在工业生产中&#xff0c;螺栓连接广泛应用于各种设备和结构中&#xff0c;因此&#xff0c;对螺栓拧紧扭矩的准确测量和控制具有重要意义。本文将详细介绍螺…

【LeetCode: 173. 二叉搜索树迭代器 + dfs + 二叉搜索树】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

以题为例浅谈文件包含

什么叫做文件包含 文件包含函数加载的参数没有经过过滤或严格定义&#xff0c;可以被用户控制&#xff0c; 包含其他恶意文件&#xff0c;导致了执行非预期代码。 文件包含漏洞&#xff08;File Inclusion Vulnerability&#xff09;是一种常见的网络安全漏洞&#xff0c;它允…

springboot283图书商城管理系统

图书商城管理系统 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本图书商城管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理…

设计模式 模板方法模式

01.如果接到一个任务&#xff0c;要求设计不同型号的悍马车 02.设计一个悍马车的抽象类&#xff08;模具&#xff0c;车模&#xff09; public abstract class HummerModel {/** 首先&#xff0c;这个模型要能够被发动起来&#xff0c;别管是手摇发动&#xff0c;还是电力发动…

软件杯 深度学习 YOLO 实现车牌识别算法

文章目录 0 前言1 课题介绍2 算法简介2.1网络架构 3 数据准备4 模型训练5 实现效果5.1 图片识别效果5.2视频识别效果 6 部分关键代码7 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于yolov5的深度学习车牌识别系统实现 该项目较…

YOLOv9改进策略:下采样涨点系列 | 一种新颖的基于 Haar 小波的下采样HWD,有效涨点系列

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文独家改进&#xff1a;HWD的核心思想是应用Haar小波变换来降低特征图的空间分辨率&#xff0c;同时保留尽可能多的信息&#xff0c;与传统的下采样方法相比&#xff0c;有效降低信息不确定性。 &#x1f4a1;&#x1f4a1;&#x1…

如何进行PLCH1U-XP系列汇川PLC在线修改程序?

在工业自动化的世界里&#xff0c;可编程逻辑控制器&#xff08;PLC&#xff09;是生产线自动化的心脏。汇川PLCH1U-XP系列以其卓越的性能和广泛的应用场景&#xff0c;赢得了众多企业的青睐。然而&#xff0c;随着生产需求的不断变化&#xff0c;PLC程序也需要相应地调整。传统…

【Linux】基础 IO(动静态库)-- 详解

一、前言 为什么要使用别人的代码&#xff1f; 主要是为了提高程序开发的效率和程序的健壮性。 当别人把功能都实现了&#xff0c;然后我们再基于别人的代码去做二次开发&#xff0c;那么效率当然就提高了。其次&#xff0c;这里基于的别人当然不是随便找的一个人&#xff0c;…

matplotlib绘制统计特征图和分布特征图

文章目录 一、统计特征图绘制1.需求2.代码方法一方法二总结 二、分布特征图绘制1.需求2.代码 一、统计特征图绘制 1.需求 我现在有两个数据集Pdata和Cdata分别在DataFrame对象中&#xff0c;我现在想对这两个数据集进行统计特征分析&#xff0c;并用直方图展示出来。 2.代码…

在线预订酒店房源小程序源码系统平台版 带完整的安装代码包以及搭建教程

近年来&#xff0c;互联网技术的飞速发展推动了各行各业的数字化转型。酒店行业也不例外&#xff0c;传统的酒店预订方式已经无法满足现代旅客的需求。旅客期望能够随时随地通过手机或电脑进行酒店预订&#xff0c;并享受到个性化的服务体验。因此&#xff0c;开发一款功能齐全…

107 在携带请求体的情况下, hutool 将 get 请求转换为了 post 请求

前言 本问题主要是来自于同事 情况大致如下, 同样的代码 一个是测试用例, 一个是生产环境的应用, 访问同一个第三方服务, 参数什么的完全一致 但是 出现的问题就是 测试用例能够拿到正确的对方的响应, 但是 生产环境的应用 却是拿到的对方的报错 然后 我开始以为是 是否…

【进阶五】Python实现SDVRP(需求拆分)常见求解算法——离散粒子群算法(DPSO)

基于python语言&#xff0c;采用经典离散粒子群算法&#xff08;DPSO&#xff09;对 需求拆分车辆路径规划问题&#xff08;SDVRP&#xff09; 进行求解。 目录 往期优质资源1. 适用场景2. 代码调整3. 求解结果4. 代码片段参考 往期优质资源 经过一年多的创作&#xff0c;目前已…

邮箱与Email有何异同?如何正确使用它们?

邮箱与Email之间有何联系&#xff1f;如何正确区分邮箱和Email&#xff1f; 电子邮箱已成为我们日常生活和工作中不可或缺的一部分。而提到电子邮箱&#xff0c;很多人会自然而然地联想到“邮箱”这个词。那么&#xff0c;邮箱与Email之间究竟有哪些异同呢&#xff1f;让AokSe…

【LeetCode每日一题】2312. 卖木头块(DFS记忆化搜索+动态规划)

文章目录 [2312. 卖木头块](https://leetcode.cn/problems/selling-pieces-of-wood/)思路1:用DFS进行记忆化搜索代码&#xff1a;思路2:动态规划代码&#xff1a; 2312. 卖木头块 思路1:用DFS进行记忆化搜索 1.要用DFS深度优先遍历每一种情况。在递归的同时&#xff0c;不断更…