HarmonyOS 自定义抽奖转盘开发(ArkTS)

news2024/12/23 12:29:50

介绍

本篇 Codelab 是基于画布组件、显式动画,实现的一个自定义抽奖圆形转盘。包含如下功能:

1.  通过画布组件 Canvas,画出抽奖圆形转盘。

2.  通过显式动画启动抽奖功能。

3.  通过自定义弹窗弹出抽中的奖品。

相关概念

● Stack组件:堆叠容器,子组件按照顺序依次入栈,后一个子组件覆盖前一个子组件。

● Canvas:画布组件,用于自定义绘制图形。

● CanvasRenderingContext2D对象:使用 RenderingContext 在 Canvas 组件上进行绘制,绘制对象可以是矩形、文本、图片等。

● 显式动画:提供全局 animateTo 显式动画接口来指定由于闭包代码导致的状态变化插入过渡动效。

● 自定义弹窗: 通过 CustomDialogController 类显示自定义弹窗。

完整示例

gitee源码地址

源码下载

自定义抽奖转盘(ArkTS).zip

环境搭建

我们首先需要完成 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操作。

a.  如果网络不能直接访问 Internet,需要通过代理服务器才可以访问,请参考配置开发环境。

3.  开发者可以参考以下链接,完成设备调试的相关配置:使用真机进行调试

a.  使用模拟器进行调试

代码结构解读

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

├──entry/src/main/ets	            // 代码区│  ├──common│  │  ├──constants│  │  │  ├──ColorConstants.ets      // 颜色常量类│  │  │  ├──CommonConstants.ets     // 公共常量类 │  │  │  └──StyleConstants.ets      // 样式常量类 │  │  └──utils│  │     ├──CheckEmptyUtils.ets     // 数据判空工具类│  │     └──Logger.ets              // 日志打印类│  ├──entryability│  │  └──EntryAbility.ts            // 程序入口类│  ├──pages│  │  └──CanvasPage.ets             // 主界面	│  ├──view│  │  └──PrizeDialog.ets            // 中奖信息弹窗类│  └──viewmodel│     ├──DrawModel.ets              // 画布相关方法类│     ├──FillArcData.ets            // 绘制圆弧数据实体类│     └──PrizeData.ets              // 中奖信息实体类└──entry/src/main/resources         // 资源文件目录

构建主界面

在这个章节中,我们将完成示例主界面的开发,效果如图所示:

在绘制抽奖圆形转盘前,首先需要在 CanvasPage.ets 的 aboutToAppear 方法中获取屏幕的宽高。

// CanvasPage.ets// 获取contextlet context = getContext(this);
aboutToAppear() {  // 获取屏幕的宽高  window.getLastWindow(context)    .then((windowClass: window.Window) => {      let windowProperties = windowClass.getWindowProperties();      this.screenWidth = px2vp(windowProperties.windowRect.width);      this.screenHeight = px2vp(windowProperties.windowRect.height);    })    .catch((error: Error) => {      Logger.error('Failed to obtain the window size. Cause: ' + JSON.stringify(error));    })}

在 CanvasPage.ets 布局界面中添加 Canvas 组件,在 onReady 方法中进行绘制。

// CanvasPage.etsprivate settings: RenderingContextSettings = new RenderingContextSettings(true);private canvasContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
Stack({ alignContent: Alignment.Center }) {  Canvas(this.canvasContext)    ...    .onReady(() => {      // 通过draw方法进行绘制      this.drawModel.draw(this.canvasContext, this.screenWidth, this.screenHeight);    })
  // 开始抽奖图片  Image($r('app.media.ic_center'))    ...}...

在 DrawModel.ets 中,通过 draw 方法逐步进行自定义圆形抽奖转盘的绘制。

// DrawModel.ets// 画抽奖圆形转盘draw(canvasContext: CanvasRenderingContext2D, screenWidth: number, screenHeight: number) {  if (CheckEmptyUtils.isEmptyObj(canvasContext)) {    Logger.error('[DrawModel][draw] canvasContext is empty.');    return;  }  this.canvasContext= canvasContext;  this.screenWidth = screenWidth;  this.canvasContext.clearRect(0, 0, this.screenWidth, screenHeight);  // 将画布沿X、Y轴平移指定距离  this.canvasContext.translate(this.screenWidth / CommonConstants.TWO,    screenHeight / CommonConstants.TWO);  // 画外部圆盘的花瓣  this.drawFlower();  // 画外部圆盘、小圈圈  this.drawOutCircle();  // 画内部圆盘  this.drawInnerCircle();  // 画内部扇形抽奖区域  this.drawInnerArc();  // 画内部扇形区域文字  this.drawArcText();  // 画内部扇形区域奖品对应的图片  this.drawImage();  this.canvasContext.translate(-this.screenWidth / CommonConstants.TWO,    -screenHeight / CommonConstants.TWO);}

画外部圆盘

画外部圆盘的花瓣:通过调用 rotate()方法,将画布旋转指定角度。再通过调用 save()和 restore()方法,

使画布保存最新的绘制状态。根据想要绘制的花瓣个数,改变旋转角度,循环画出花瓣效果。

// DrawModel.ets// 画外部圆盘的花瓣drawFlower() {  let beginAngle = this.startAngle + this.avgAngle;  const pointY = this.screenWidth * CommonConstants.FLOWER_POINT_Y_RATIOS;  const radius = this.screenWidth * CommonConstants.FLOWER_RADIUS_RATIOS;  const innerRadius = this.screenWidth * CommonConstants.FLOWER_INNER_RATIOS;  for (let i = 0; i < CommonConstants.COUNT; i++) {    this.canvasContext?.save();    this.canvasContext?.rotate(beginAngle * Math.PI / CommonConstants.HALF_CIRCLE);    this.fillArc(new FillArcData(0, -pointY, radius, 0, Math.PI * CommonConstants.TWO),      ColorConstants.FLOWER_OUT_COLOR);
    this.fillArc(new FillArcData(0, -pointY, innerRadius, 0, Math.PI * CommonConstants.TWO),      ColorConstants.FLOWER_INNER_COLOR);    beginAngle += this.avgAngle;    this.canvasContext?.restore();  }}
// 画弧线方法fillArc(fillArcData: FillArcData, fillColor: string) {  if (CheckEmptyUtils.isEmptyObj(fillArcData) || CheckEmptyUtils.isEmptyStr(fillColor)) {    Logger.error('[DrawModel][fillArc] fillArcData or fillColor is empty.');    return;  }  if (this.canvasContext !== undefined) {    this.canvasContext.beginPath();    this.canvasContext.fillStyle = fillColor;    this.canvasContext.arc(fillArcData.x, fillArcData.y, fillArcData.radius,      fillArcData.startAngle, fillArcData.endAngle);    this.canvasContext.fill();  }}

画外部圆盘、圆盘边上的小圈圈:在指定的 X、Y(0, 0)坐标处,画一个半径为 this.screenWidth * CommonConstants.OUT_CIRCLE_RATIOS 的圆形。接下来通过一个 for 循环,且角度每次递增 CommonConstants.CIRCLE / CommonConstants.SMALL_CIRCLE_COUNT,来绘制圆环上的小圈圈。

// DrawModel.etsdrawOutCircle() {  // 画外部圆盘  this.fillArc(new FillArcData(0, 0, this.screenWidth * CommonConstants.OUT_CIRCLE_RATIOS, 0,    Math.PI * CommonConstants.TWO), ColorConstants.OUT_CIRCLE_COLOR);
  let beginAngle = this.startAngle;  // 画小圆圈  for (let i = 0; i < CommonConstants.SMALL_CIRCLE_COUNT; i++) {    this.canvasContext?.save();    this.canvasContext?.rotate(beginAngle * Math.PI / CommonConstants.HALF_CIRCLE);    this.fillArc(new FillArcData(this.screenWidth * CommonConstants.SMALL_CIRCLE_RATIOS, 0,      CommonConstants.SMALL_CIRCLE_RADIUS, 0, Math.PI * CommonConstants.TWO),      ColorConstants.WHITE_COLOR);    beginAngle = beginAngle + CommonConstants.CIRCLE / CommonConstants.SMALL_CIRCLE_COUNT;    this.canvasContext?.restore();  }}

画内部扇形抽奖区域

画内部圆盘、内部扇形抽奖区域:使用 fillArc 方法绘制内部圆盘。通过一个 for 循环,角度每次递增 this.avgAngle。然后不断更改弧线的起始弧度 this.startAngle * Math.PI / CommonConstants.HALF_CIRCLE 和弧线的终止弧度(this.startAngle + this.avgAngle) * Math.PI / CommonConstants.HALF_CIRCLE。最后用 fill()方法对路径进行填充。

// DrawModel.ets// 画内部圆盘drawInnerCircle() {  this.fillArc(new FillArcData(0, 0, this.screenWidth * CommonConstants.INNER_CIRCLE_RATIOS, 0,    Math.PI * CommonConstants.TWO), ColorConstants.INNER_CIRCLE_COLOR);  this.fillArc(new FillArcData(0, 0, this.screenWidth * CommonConstants.INNER_WHITE_CIRCLE_RATIOS, 0,    Math.PI * CommonConstants.TWO), ColorConstants.WHITE_COLOR);}
// 画内部扇形抽奖区域drawInnerArc() {  // 颜色集合  let colors = [    ColorConstants.ARC_PINK_COLOR, ColorConstants.ARC_YELLOW_COLOR,    ColorConstants.ARC_GREEN_COLOR, ColorConstants.ARC_PINK_COLOR,    ColorConstants.ARC_YELLOW_COLOR, ColorConstants.ARC_GREEN_COLOR  ];  let radius = this.screenWidth * CommonConstants.INNER_ARC_RATIOS;  for (let i = 0; i < CommonConstants.COUNT; i++) {    this.fillArc(new FillArcData(0, 0, radius, this.startAngle * Math.PI / CommonConstants.HALF_CIRCLE,      (this.startAngle + this.avgAngle) * Math.PI / CommonConstants.HALF_CIRCLE), colors[i]);    this.canvasContext?.lineTo(0, 0);    this.canvasContext?.fill();    this.startAngle += this.avgAngle;  }}

画内部抽奖区域文字:用 for 循环,通过 drawCircularText()方法绘制每组文字。drawCircularText()方法接收三个参数,分别是字符串,起始弧度和终止弧度。绘制文本前需要设置每个字母占的弧度 angleDecrement,然后设置水平和垂直的偏移量。垂直偏移量 circleText.y - Math.sin(angle) * radius 就是朝着圆心移动的距离;水平偏移 circleText.x + Math.cos(angle) * radius,是为了让文字在当前弧范围文字居中。最后使用 fillText 方法绘制文本。

// DrawModel.ets// 画内部扇形区域文字drawArcText() {  if (this.canvasContext !== undefined) {    this.canvasContext.textAlign = CommonConstants.TEXT_ALIGN;    this.canvasContext.textBaseline = CommonConstants.TEXT_BASE_LINE;    this.canvasContext.fillStyle = ColorConstants.TEXT_COLOR;    this.canvasContext.font = StyleConstants.ARC_TEXT_SIZE + CommonConstants.CANVAS_FONT;  }  // 需要绘制的文本数组集合  let textArrays = [    $r('app.string.text_smile'),    $r('app.string.text_hamburger'),    $r('app.string.text_cake'),    $r('app.string.text_smile'),    $r('app.string.text_beer'),    $r('app.string.text_watermelon')  ];  let arcTextStartAngle = CommonConstants.ARC_START_ANGLE;  let arcTextEndAngle = CommonConstants.ARC_END_ANGLE;  for (let i = 0; i < CommonConstants.COUNT; i++) {    this.drawCircularText(this.getResourceString(textArrays[i]),      (this.startAngle + arcTextStartAngle) * Math.PI / CommonConstants.HALF_CIRCLE,      (this.startAngle + arcTextEndAngle) * Math.PI / CommonConstants.HALF_CIRCLE);    this.startAngle += this.avgAngle;  }}
// 绘制圆弧文本drawCircularText(textString: string, startAngle: number, endAngle: number) {  if (CheckEmptyUtils.isEmptyStr(textString)) {    Logger.error('[DrawModel][drawCircularText] textString is empty.');    return;  }  class CircleText {    x: number = 0;    y: number = 0;    radius: number = 0;  };  let circleText: CircleText = {    x: 0,    y: 0,    radius: this.screenWidth * CommonConstants.INNER_ARC_RATIOS  };  // 圆的半径  let radius = circleText.radius - circleText.radius / CommonConstants.COUNT;  // 每个字母占的弧度  let angleDecrement = (startAngle - endAngle) / (textString.length - 1);  let angle = startAngle;  let index = 0;  let character: string;
  while (index < textString.length) {    character = textString.charAt(index);    this.canvasContext?.save();    this.canvasContext?.beginPath();    this.canvasContext?.translate(circleText.x + Math.cos(angle) * radius,      circleText.y - Math.sin(angle) * radius);    this.canvasContext?.rotate(Math.PI / CommonConstants.TWO - angle);    this.canvasContext?.fillText(character, 0, 0);    angle -= angleDecrement;    index++;    this.canvasContext?.restore();  }}

画内部抽奖区域文字对应图片:使用 drawImage 方法绘制抽奖区域文字对应图片,该方法接收五个参数,分别是图片资源、绘制区域左上角的 X 和 Y 轴坐标、绘制区域的宽度和高度。

// DrawModel.ets// 画内部扇形区域文字对应的图片drawImage() {  let beginAngle = this.startAngle;  let imageSrc = [    CommonConstants.WATERMELON_IMAGE_URL, CommonConstants.BEER_IMAGE_URL,    CommonConstants.SMILE_IMAGE_URL, CommonConstants.CAKE_IMAGE_URL,    CommonConstants.HAMBURG_IMAGE_URL, CommonConstants.SMILE_IMAGE_URL  ];  for (let i = 0; i < CommonConstants.COUNT; i++) {    let image = new ImageBitmap(imageSrc[i]);    this.canvasContext?.save();    this.canvasContext?.rotate(beginAngle * Math.PI / CommonConstants.HALF_CIRCLE);    this.canvasContext?.drawImage(image, this.screenWidth * CommonConstants.IMAGE_DX_RATIOS,      this.screenWidth * CommonConstants.IMAGE_DY_RATIOS, CommonConstants.IMAGE_SIZE,      CommonConstants.IMAGE_SIZE);    beginAngle += this.avgAngle;    this.canvasContext?.restore();  }}

实现抽奖功能

在 CanvasPage.ets 的 Canvas 组件中添加 rotate 属性,在 Image 组件中添加点击事件 onClick。点击“开始抽奖”图片,圆形转盘开始转动抽奖。

// CanvasPage.etsStack({ alignContent: Alignment.Center }) {  Canvas(this.canvasContext)    ...    .onReady(() => {      this.drawModel.draw(this.canvasContext, this.screenWidth, this.screenHeight);    })    .rotate({      x: 0,      y: 0,      z: 1,      angle: this.rotateDegree,      centerX: this.screenWidth / CommonConstants.TWO,      centerY: this.screenHeight / CommonConstants.TWO    })
  // 开始抽奖图片  Image($r('app.media.ic_center'))   ...    .enabled(this.enableFlag)    .onClick(() => {      this.enableFlag = !this.enableFlag;      // 开始抽奖      this.startAnimator();    })}...

圆形转盘开始转动抽奖:给转盘指定一个随机的转动角度 randomAngle,保证每次转动的角度是随机的,即每次抽到的奖品也是随机的。动画结束后,转盘停止转动,抽奖结束,弹出抽中的奖品信息。

// CanvasPage.etsdialogController: CustomDialogController = new CustomDialogController({  builder: PrizeDialog({    prizeData: $prizeData,    enableFlag: $enableFlag  }),  autoCancel: false});
// 开始抽奖startAnimator() {  let randomAngle = Math.round(Math.random() * CommonConstants.CIRCLE);  // 获取中奖信息  this.prizeData = this.drawModel.showPrizeData(randomAngle);
  animateTo({    duration: CommonConstants.DURATION,    curve: Curve.Ease,    delay: 0,    iterations: 1,    playMode: PlayMode.Normal,    onFinish: () => {      this.rotateDegree = CommonConstants.ANGLE - randomAngle;      // 打开自定义弹窗,弹出抽奖信息      this.dialogController.open();    }  }, () => {    this.rotateDegree = CommonConstants.CIRCLE * CommonConstants.FIVE +      CommonConstants.ANGLE - randomAngle;  })}

弹出抽中的奖品信息:抽奖结束后,弹出抽中的文本和图片信息,通过自定义弹窗实现。

// PrizeDialog.ets@CustomDialogexport default struct PrizeDialog {  @Link prizeData: PrizeData;  @Link enableFlag: boolean;  private controller?: CustomDialogController;
  build() {    Column() {      Image(this.prizeData.imageSrc !== undefined ? this.prizeData.imageSrc : '')        ...
      Text(this.prizeData.message)        ...
      Text($r('app.string.text_confirm'))        ...        .onClick(() => {          // 关闭自定义弹窗		          this.controller?.close();          this.enableFlag = !this.enableFlag;        })    }    ...  }}

总结

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

1.  使用画布组件 Canvas,画出抽奖圆形转盘。

2.  使用显式动画启动抽奖功能。

3.  使用自定义弹窗弹出抽中的奖品。

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

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

相关文章

业务流程图基本规范要求

再描述业务流程应该如何画之前&#xff0c;需要了解几个概念 BPA&#xff08;Business Process Automation&#xff09;是指通过自动化技术来改进和优化企业的业务流程&#xff0c;以提高生产力 和效率。 BPM&#xff08;Business Process Management&#xff09;是指对企业…

java数据结构 基本查找,二分查找,分块查找

目录 算法查找基本查找二分查找优化方式 插值查找了解即可斐波那契查找 了解以上总结 分块查找 算法查找 基本查找 又叫顺序查找 从0索引开始挨个往后查找 需求 定义一个方法利用基本查找 查找某个元素是否存在 {132,135,65,86,89,6} public static void main(String[] args) …

java--while循环

1.while循环 2.示例 3.执行的流程&#xff1a; ①循环一开始&#xff0c;执行int i 0一次 ②此时 i0&#xff0c;接着计算机执行循环条件语句&#xff1a;0 < 3 返回true&#xff0c;计算机就进到循环体中执行&#xff0c;输出&#xff1a;"Hello World",然后执…

SpringBoot 源码分析(四) 内置Tomcat分析

一、Tomcat相关知识 1. tomcat目录结构 Tomcat文件的目录结构 2.启动流程 启动一个Tomcat服务是执行的bin目录下的脚本程序&#xff0c;startup.bat和 startup.sh.一个是windows的脚本&#xff0c;一个是Linux下的脚本&#xff0c;同样还可以看到两个停止的脚本 shutdown.ba…

AIGC是什么?一文读懂人工智能生成内容技术!

文章目录 前言一、AIGC是什么&#xff1f;二、AIGC的4个主要特征1、文本生成2、图像生成3、语音生成4、视频生成 三、AIGC如何运作&#xff1f;步骤1&#xff1a;收集数据步骤2&#xff1a;模型训练步骤3&#xff1a;内容生成步骤4&#xff1a;反馈和改进 四、AIGC关键技术能力…

智慧社区燃气管网监测系统

燃气易燃易爆&#xff0c;一旦操作不当或疏忽大意&#xff0c;极易引发燃气安全事故&#xff0c;造成严重后果&#xff0c;2023年10月24日&#xff0c;在吉林某小区&#xff0c;发生了燃气使用不当产生的爆炸导致了1人死亡&#xff0c;1人重伤&#xff0c;15人轻伤&#xff0c;…

网络监控硬盘录像机!

目录 认识录像机以及选购前小知识&#xff01; 1.网络监控摄像头的像素和分辨率对照参考&#xff1a; 2.录像视频的大小主要受摄像头的像素和存储编码影响&#xff0c;这些在设置录像的时候&#xff0c;都是可以改的。 3.关于录像机存储能力咨询 4.关于存储编码简介 认识录…

网页的基本结构

标签的结构 HTML网页是由HTML标签组成的描述性文本&#xff0c;HTML标签可以说明文字&#xff0c;图形、动画、声音、表格、链接等。 一个标签由开始标签&#xff0c;结束标签、标签名、标签属性组成 <div><a href"https://www.creatorblue.com/">创蓝…

酷开科技依托酷开系统推动家庭智能化加速发展

为什么越来越多的人会选择智能家居&#xff1f;因为智能家居的出现&#xff0c;大大方便了我们的生活&#xff0c;为生活提供便利舒适的体验&#xff1b;就如同洗衣机与洗碗机解放了我们的双手是一样的道理&#xff0c;智能家居是在生活的方方面面为我们提供更加便利化的可能性…

C++数据结构X篇_24_归并排序(稳定的排序)

本篇参考十大经典排序算法-归并排序算法详解进行整理和补充。 文章目录 1. 什么是归并排序1.1 概念1.2 算法原理1.3 算法实现 2. 归并排序算法特点2.1 时间复杂度2.2 空间复杂度2.3 稳定性 1. 什么是归并排序 1.1 概念 归并排序&#xff08;Merge sort&#xff09; 是建立在…

NFTScan 获 Optimism 基金会 Cycle 14 Grant 支持 35,000 枚 OP !

近期&#xff0c;Optimism 基金会官方公布了 Grants Program Cycle 14 的最终入选项目名单&#xff0c;NFTScan 团队获得了 35k OP 的 Grant 资金支持。 Cycle 14 Final: https://app.charmverse.io/op-grants/page-47078316203750115 作为 Optimism 生态的 NFT 基础设施&…

内网渗透——macOS上搭建Web服务器

# 公网访问macOS本地web服务器【内网穿透】 文章目录 1. 启动Apache服务器2. 公网访问本地web服务2.1 本地安装配置cpolar2.2 创建隧道2.3 测试访问公网地址3. 配置固定二级子域名3.1 保留一个二级子域名3.2 配置二级子域名4. 测试访问公网固定二级子域名 以macOS自带的Apache…

惊艳,这个html5播放器支持视频切换、倍速切换、视频预览

很赞哇&#xff01;&#xff01; 本文将对视频播放相关的功能进行说明&#xff08;基于云平台&#xff09;&#xff0c;包括初始化播放器、播放器尺寸设置、视频切换、倍速切换、视频预览、自定义视频播放的开始/结束时间、禁止拖拽进度、播放器皮肤、控件按钮以及播放控制等。…

使用群晖Synology Office提升生产力:多人同时编辑一个文件

使用群晖Synology Office提升生产力&#xff1a;多人同时编辑一个文件 文章目录 使用群晖Synology Office提升生产力&#xff1a;多人同时编辑一个文件本教程解决的问题是&#xff1a;1. 本地环境配置2. 制作本地分享链接3. 制作公网访问链接4. 公网ip地址访问您的分享相册5. 制…

Inbound marketing的完美闭环:将官网作为营销枢纽,从集客进化为入站

Inbound marketing即入站营销的运作方式不同于付费广告&#xff0c;你需要不断地投入才能获得持续的访问量。而你的生意表达内容一经创建、发布&#xff0c;就能远远不断地带来流量。 Inbound marketing也被翻译作集客营销&#xff0c;也就是美国知名的营销SaaS企业hubspot所主…

react-组件间的通讯

一、父传子 父组件在使用子组件时&#xff0c;提供要传递的数据子组件通过props接收数据 class Parent extends React.Component {render() {return (<div><div>我是父组件</div><Child name"张" age{16} /></div>)} }const Child …

OJ练习第183题——天际线问题

天际线问题 力扣链接&#xff1a;218. 天际线问题 题目描述 城市的 天际线 是从远处观看该城市中所有建筑物形成的轮廓的外部轮廓。给你所有建筑物的位置和高度&#xff0c;请返回 由这些建筑物形成的 天际线 。 示例 官解思路&#xff08;扫描线优先队列&#xff09; 观…

【httpd】 Apache http服务器目录显示不全解决

文章目录 1. 文件名过长问题1.1 在centos中文件所谓位置etc/httpd/conf.d/httpd-autoindex.conf1.2 在配置文件httpd-autoindex.conf中的修改&#xff1a;1.3 修改完成后重启Apache&#xff1a; 1. 文件名过长问题 1.1 在centos中文件所谓位置etc/httpd/conf.d/httpd-autoindex…

android开发使用OkHttp自带的WebSocket实现IM功能

一、背景 android app开发经常会有IM需求&#xff0c;很多新手不晓得如何入手&#xff0c;难点在于通讯不中断。其实android发展到今天&#xff0c;很多技术都很完善&#xff0c;有很多类似框架可以实现。例如有&#xff1a;okhttp自带的websocket框架、easysocket等等。本文主…

69 划分字母区间

划分字母区间 题解1 贪心1&#xff08;方法略笨&#xff0c;性能很差&#xff09;题解2 贪心2&#xff08;参考标答&#xff09; 给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段&#xff0c;同一字母最多出现在一个片段中。 注意&#xff0c;划分结果需要满足&am…