HarmonyOS Next 系列之可移动悬浮按钮实现(六)

系列文章目录

HarmonyOS Next 系列之省市区弹窗选择器实现(一)
HarmonyOS Next 系列之验证码输入组件实现(二)
HarmonyOS Next 系列之底部标签栏TabBar实现(三)
HarmonyOS Next 系列之HTTP请求封装和Token持久化存储(四)
HarmonyOS Next 系列之从手机选择图片或拍照上传功能实现(五)
HarmonyOS Next 系列之可移动悬浮按钮实现(六)


文章目录

  • 系列文章目录
  • 前言
  • 一、实现原理分析
  • 二、API简单回顾
  • 三、规避和限制移动范围
  • 四、窗口宽高、状态栏高度、底部规避区域高度获取
    • 1、窗口宽高获取
    • 2、状态栏高度获取
    • 2、底部规避区域高度获取
  • 四、完整代码实现
  • 五、其他说明


前言

HarmonyOS Next(基于API11)实现一个可移动的悬浮按钮
在这里插入图片描述在这里插入图片描述

ps:为演示作用,这边和后续代码例子随便用回到顶部图标来做演示,实际可自定义替换


一、实现原理分析

1、布局方面:使用Stack容器,让悬浮按钮堆叠在页面之上,通过postion属性x,y设置悬浮按钮位置(x,y为相对页面左上角距离)
2、事件处理:在移动过程中通过监听touch事件,获取手指在屏幕上位置与初始触摸点位置比较,计算悬浮按钮的偏移量,动态更新悬浮按钮x,y值。

二、API简单回顾

touch触摸事件

1、触摸类型TouchType

名称描述
Down手指按下时触发。
Up手指抬起时触发。
Move手指按压态在屏幕上移动时触发。

2、手指信息TouchObject

名称描述
type触摸事件的类型
windowX触摸点相对于应用窗口左上角的X坐标。
windowY触摸点相对于应用窗口左上角的Y坐标。

说明:以x轴为例,计算两个触摸点(A、B)水平方向距离只需B.windowX-A.windowX,而在我们实现悬浮按钮处理过程中这个A点就是手指刚按下去触摸点的windowX,B点就是移动过程中触摸点的windowX,在移动过程中不断计算这个差值后更新悬浮按钮坐标就能让其跟着手指移动。当然在这个过程中还需要考虑悬浮按钮移出屏幕情况,需要规避和限制。

ps:windowX、windowY单位为vp


三、规避和限制移动范围

为了让悬浮按钮不移出屏幕,需要限制x、y大小
最小值很容易想到x>=0,y>=0,也即悬浮按钮在最左上角
在这里插入图片描述

最大值位置在页面右下角

在这里插入图片描述
假设悬浮按钮半径为R,窗口宽为winWidth、窗口高winHeight,状态栏高statusHeight,底部规避区域高:bottomHeight

x最大值=winWidth-2R
y最大值=winHeight-2R-statusHeight-bottomHeight
所以x范围为0~(winWidth-2R),y范围0 ~(winHeight-2R-statusHeight-bottomHeight)


四、窗口宽高、状态栏高度、底部规避区域高度获取

1、窗口宽高获取

import { window } from '@kit.ArkUI'
.....
.....
.....
 window.getLastWindow(getContext(this), (err, windowClass) => {
      if (!err.code) {
        //获取窗口宽高
        let windowProperties = windowClass.getWindowProperties()
        this.winWidth = px2vp(windowProperties.windowRect.width)
        this.winHeight = px2vp(windowProperties.windowRect.height)
      }
    })

2、状态栏高度获取

import { window } from '@kit.ArkUI'
.....
.....
.....
 window.getLastWindow(getContext(this), (err, windowClass) => {
      if (!err.code) {
        //获取状态栏高度
        this.statusHeight = px2vp(windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM).topRect.height)
      }
    })

2、底部规避区域高度获取

import { window } from '@kit.ArkUI'
.....
.....
.....
window.getLastWindow(getContext(this), (err, windowClass) => {
      if (!err.code) {
        //获取手机底部规避区域高度
        this.bottomAvoidAreaHeight = px2vp(windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR)
          .bottomRect.height)
      }
    })

ps:需要注意的是上述获取到的宽高单位都是px需要统一转成vp单位,方便和windowXY进行计算

四、完整代码实现

SuspensionButton .ets

import {  window } from '@kit.ArkUI'

@Entry
@Component
struct SuspensionButton {
  @State statusHeight: number = 0 //状态栏高度
  @State bottomAvoidAreaHeight: number = 0 //手机底部规避区域高度
  @State curLeft: number = 0 //当前悬浮按钮距离窗口左边距离
  @State curTop: number = 0 //当前悬浮按钮距离窗口顶部距离
  private startLeft: number = 0 //开始移动那一刻悬浮按钮距离窗口左边距离
  private startTop: number = 0 //开始移动那一刻悬浮按钮距离窗口顶部距离
  private startX: number = 0 //开始移动触摸点x坐标,相对窗口左上角
  private startY: number = 0 //开始移动触摸点y坐标,相对窗口左上角
  private radius: number = 25 //悬浮按钮半径,单位vp
  private winWidth: number = 0 //窗口宽度
  private winHeight: number = 0 //窗口高度

  aboutToAppear() {
    this.getWindowInfo()

  }

  //获取窗口尺寸信息
  getWindowInfo() {
    window.getLastWindow(getContext(this), (err, windowClass) => {
      if (!err.code) {
        //状态栏高度
        this.statusHeight = px2vp(windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM).topRect.height)
        //获取手机底部规避区域高度
        this.bottomAvoidAreaHeight = px2vp(windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR)
          .bottomRect
          .height)

        //获取窗口宽高
        let windowProperties = windowClass.getWindowProperties()
        this.winWidth = px2vp(windowProperties.windowRect.width)
        this.winHeight = px2vp(windowProperties.windowRect.height)

        //设置初始位置位于屏幕右下角,演示设置可根据实际调整
        this.curLeft=this.winWidth*0.8
        this.curTop=this.winHeight*0.8
      }
    })
  }

  build() {

    Stack() {
      Column(){
        //页面内容

      }.width('100%').height('100%')

      //悬浮按钮
      Row() {
        Image($r('app.media.top')).width(25)
      }
      .width(this.radius * 2)
      .height(this.radius * 2)
      .justifyContent(FlexAlign.Center)
      .borderRadius(this.radius)
      .backgroundColor('#E8E8E8')
      .position({
        x: this.curLeft,
        y: this.curTop
      })
      .onTouch((event: TouchEvent) => {
        //手指按下记录初始触摸点坐标、悬浮按钮位置
        if (event.type === TouchType.Down) {
          this.startX = event.touches[0].windowX
          this.startY = event.touches[0].windowY
          this.startLeft = this.curLeft
          this.startTop = this.curTop

        }
        //手指拖动
        else if (event.type === TouchType.Move) {
          let touch = event.touches[0]

          //计算悬浮球与左边距离(x坐标), 当前悬浮球距离左边=开始位置(x轴)+(当前触摸点x坐标-开始移动触摸点x坐标)
          let curLeft = this.startLeft + (touch.windowX - this.startX)
          //限制悬浮球不能移除屏幕左边
          curLeft = Math.max(0, curLeft)
          //限制悬浮球不能移除屏幕右边
          this.curLeft = Math.min(this.winWidth - 2 * this.radius, curLeft)

          //计算悬浮球与顶部距离(y坐标), 当前悬浮球距离顶部=开始位置(y轴)+(当前触摸点y坐标-开始移动触摸点y坐标)
          let curTop = this.startTop + (touch.windowY - this.startY)
          //限制悬浮球不能移除屏幕上边
          curTop = Math.max(0, curTop)
          //限制悬浮球不能移除屏幕下边
          this.curTop = Math.min(this.winHeight - 2 * this.radius - this.bottomAvoidAreaHeight - this.statusHeight, curTop)
        }
      })

    }.width('100%')
    .height('100%')
    .backgroundColor('#f2f2f2')

  }
}




运行效果
在这里插入图片描述

五、其他说明

如果是想实现悬浮窗原理也一样,只不过把悬浮按钮半径计算拆开为x,y2个方向,根据悬浮窗宽高替换带入计算即可。
如果想实现不可移动悬浮按钮,类似案例中回到顶部固定在页面右下角,只需要把触摸事件去掉即可。

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

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

相关文章

基于强化学习的目标跟踪论文合集

文章目录 2020UAV Maneuvering Target Tracking in Uncertain Environments Based on Deep Reinforcement Learning and Meta-LearningUAV Target Tracking in Urban Environments Using Deep Reinforcement Learning 2021Research on Vehicle Dispatch Problem Based on Kuhn-…

BGP路由反射器实验

实验内容: 通过本实验验证bgp路由反射器的规则 1. 从client收到的路由更新,反射到non-client和client,同时发送给EBGP邻居 2. 从non-client收到的路由更新,只反射到client,同时发送给EBGP邻居 3. 从EBGP邻居收到的路…

PXE自动平台 搭建 银河麒麟 UEFI x86_64 ARM64

1. PXE自动化 原理 要实现PXE自动安装需要以下组件: DHCP服务:服务器通过网络启动时自动分配IP地址。TFTP服务:提供服务器启动下载启动引导EFI。HTTP服务:操作系统镜像下载。 各组件工作原理如下[1]: 开PXE后&…

最新版ChatGPT对话系统源码 Chat Nio系统源码

最新版ChatGPT对话系统源码 Chat Nio系统源码 支持 Vision 模型, 同时支持 直接上传图片 和 输入图片直链或 Base64 图片 功能 (如 GPT-4 Vision Preview, Gemini Pro Vision 等模型) 支持 DALL-E 模型绘图 支持 Midjourney / Niji 模型的 Imagine / Upscale / Variant / Re…

Redis-数据类型-Geospatial(地理空间索引)

文章目录 1、查看redis是否启动2、通过客户端连接redis3、切换到db5数据库4、将地理位置信息(经度和纬度)添加到 Redis 的键(key)中4.1、添加大江商厦4.2、添加西部硅谷 5、升序返回有序集key,让分数一起和值返回的结果…

Doris连接超时问题排查记录

文章目录 一、现象描述二、问题排查1、分析驱动包2、分析Mysql客户端(问题解决) 一、现象描述 先上官网部署地址,按照官网上一步步进行部署 https://doris.apache.org/zh-CN/docs/get-starting/quick-start 基本到最后都挺顺利的&#xff0c…

2022年大作业参考报告-使用C++语言开发小学生成绩管理系统、中学生成绩管理系统、大学生成绩管理系统【240621更新】

背景: 目录 第一章 需求分析 2 1.1 问题描述 2 6.1 功能需求 2 6.2 开发环境 2 6.3 开发过程 2 第二章 概要设计 3 2.1 总体设计 3 2.2 类的定义 3 2.3 接口设计 5 2.4 运行界面设计 6 第三章 详细设计 …

【滚动哈希 二分查找】1044. 最长重复子串

本文涉及知识点 滚动哈希 二分查找算法合集 LeetCode 1044. 最长重复子串 给你一个字符串 s ,考虑其所有 重复子串 :即 s 的(连续)子串,在 s 中出现 2 次或更多次。这些出现之间可能存在重叠。 返回 任意一个 可能具…

vs2022 studio控制台出现中文乱码解决

vs2022 studio控制台出现中文乱码解决 问题解决 问题 这里cout中间的中文,但控制台出现的是乱码对此需要进行修改 解决 打开运行的主文件,也就是整个程序的入口,对他另存为 之后点击编码保存 接着将编码保存的格式变为图片对应的这种 记…

ArcGIS定义1.5度带坐标系与投影转换

​ 点击下方全系列课程学习 点击学习—>ArcGIS全系列实战视频教程——9个单一课程组合系列直播回放 点击学习——>遥感影像综合处理4大遥感软件ArcGISENVIErdaseCognition 对于ArcGIS如何定义高斯克吕格3度带、6度带,我相信大部分人都是比较清楚的&#xff0…

ArcGIS批量投影转换的妙用(地理坐标系转换为平面坐标系)

​ 点击下方全系列课程学习 点击学习—>ArcGIS全系列实战视频教程——9个单一课程组合系列直播回放 这次文章我们来介绍一下,如何巧妙用要素数据集来实现要素的批量投影。不需要ArcGIS的模型构建器与解决。 例如,有多个要素要将CGCS_2000地理坐标系投…

gitlab升级16.11.3-ee

背景 这是事后一段时间补充记录的博客。 升级目的:修补漏洞CVE-2024-4835 未经认证的威胁攻击者能够利用该漏洞在跨站脚本 (XSS) 攻击中,轻松接管受害者账户。 gitlab版本为14.6.2-ee升级至16.11.3-ee 思路 翻阅文档找升级方法及升级版本路径。使用…

Python酷库之旅-第三方库openpyxl(02)

目录 一、 openpyxl库的由来 1、背景 2、起源 3、发展 4、特点 4-1、支持.xlsx格式 4-2、读写Excel文件 4-3、操作单元格 4-4、创建和修改工作表 4-5、样式设置 4-6、图表和公式 4-7、支持数字和日期格式 二、openpyxl库的优缺点 1、优点 1-1、支持现代Excel格式…

Leetcode 第 401 场周赛题解

Leetcode 第 401 场周赛题解 Leetcode 第 401 场周赛题解题目1:3178. 找出 K 秒后拿着球的孩子思路代码复杂度分析 题目2:3179. K 秒后第 N 个元素的值思路代码复杂度分析 题目3:3180. 执行操作可获得的最大总奖励 I思路代码复杂度分析 题目4…

leetcode 二分查找·系统掌握 寻找旋转排序数组中的最小值II

题目: 题解: 本题比普通的寻找旋转排序数组中的最小值多了一个数组中的元素可以重复这一点。 这会时原来的思路出现一个漏洞(大家感兴趣可以看看我做普通版寻找旋转排序数组最小值的思路),就是旋转后的数组中的第二个…

AI在线免费视频工具2:视频配声音;图片说话hedra

1、视频配声音 https://deepmind.google/discover/blog/generating-audio-for-video/ https://www.videotosoundeffects.com/ (免费在线使用) 2、图片说话在线图片生成播报hedra hedra 上传音频与图片即可合成 https://www.hedra.com/ https://www.…

论文浅读之Mamba: Linear-Time Sequence Modeling with Selective State Spaces

介绍 这篇论文提出了一种新型的"选择性状态空间模型"(Selective State Space Model, S6)来解决之前结构化状态空间模型(SSM)在离散且信息密集的数据(如文本)上效果较差的问题。 Mamba 在语言处理、基因组学和音频分析等领域的应用中表现出色。…

读AI新生:破解人机共存密码笔记08超级智能

1. 发现动作 1.1. 时间跨度长的智能行为,需要具备在多个抽象层次上分层规划和管理活动的能力,从攻读博士学位(可能涉及1万亿个动作),到给一根手指发送一个运动控制指令,从而键入求职信的字符,无…

JavaWeb——Mysql的启动/登录/卸载

目录 1.Mysql服务器 2.Mysql的简单使用 2.1 启动Mysql: 2.2 登录Mysql 2.3 退出 3. 连接别人的数据库 4.卸载mqsql 1.Mysql服务器 安装了Mysql的计算机都成为Mysql服务器 2.Mysql的简单使用 2.1 启动Mysql: 第一种方法:搜索服务&am…

用户态协议栈05—架构优化

优化部分 添加了in和out两个环形缓冲区&#xff0c;收到数据包后添加到in队列&#xff1b;经过消费者线程处理之后&#xff0c;将需要发送的数据包添加到out队列。添加数据包解析线程&#xff08;消费者线程&#xff09;&#xff0c;架构分层 #include <rte_eal.h> #inc…