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

news2024/11/19 8:05:53

系列文章目录

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个方向,根据悬浮窗宽高替换带入计算即可。
如果想实现不可移动悬浮按钮,类似案例中回到顶部固定在页面右下角,只需要把触摸事件去掉即可。

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

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

相关文章

Huffman树——AcWing 148. 合并果子

目录 Huffman树 定义 运用情况 注意事项 解题思路 AcWing 148. 合并果子 题目描述 运行代码 代码思路 其它代码 代码思路 Huffman树 定义 它是一种最优二叉树。通过构建带权路径长度最小的二叉树,经常用于数据压缩等领域。 运用情况 在数据压缩中&a…

C语言 while循环1

在C语言里有3种循环:while循环 do while 循环 for循环 while语句 //while语法结构 while(表达式)循环语句; 比如在屏幕上打印1-10 在while循环中 break用于永久的终止循环 在while循环中,continue的作用是跳过本次循环 …

MySQL的综合运用

MySQL版的葵花宝典,欲练此功,挥刀自。。。呃,,,说错了,是先创建两个表,分别是location表和store_info表 示例表为location表和store_info表,如下图所示: 操作一&#xf…

关于小蛋の编程和小蛋编程为同一作者的说明

小蛋の编程和小蛋编程的作品为同一人制作,因前者为父母的手机号进行注册,现用本人手机号注册了新账号小蛋编程,后续文章将在新账号小蛋编程上进行刊登,同时在小蛋编程上对原账号文章进行转载。此账号不再发布帖子,请大…

算法常见手写代码

1.NMS def py_cpu_nms(dets, thresh):"""Pure Python NMS baseline."""#x1、y1、x2、y2、以及score赋值x1 dets[:, 0]y1 dets[:, 1]x2 dets[:, 2]y2 dets[:, 3]scores dets[:, 4]#每一个检测框的面积areas (x2 - x1 1) * (y2 - y1 1)#按…

运维iptables与firewalld详解

iptables与firewalld 一、iptables 1.1 iptables简介 iptables 是一个在 Linux 系统上用来配置 IPv4 数据包过滤规则的工具。它允许系统管理员控制数据包的流向,实现网络安全、网络地址转换(NAT)和端口转发等功能。 具体来说,…

如何解决app广告填充率低、广告填充异常,提升广告变现收益?

APP广告变现有助于开发者获得持续的收益来源,由于广告链路的封闭性和复杂化,一旦出现请求配置参数错误、返回广告源信息缺失、素材被拦截等异常,大部分开发者很难及时查清异常情况,导致广告填充率不理想,甚至填充率常常…

Spire.PDF for .NET【文档操作】演示:设置 PDF 文档的 XMP 元数据

XMP 是一种文件标签技术,可让您在内容创建过程中将元数据嵌入文件本身。借助支持 XMP 的应用程序,您的工作组可以以团队以及软件应用程序、硬件设备甚至文件格式易于理解的格式捕获有关项目的有意义的信息(例如标题和说明、可搜索的关键字以及…

“开源AI”到底是什么意思

开源与专有软件之间的斗争早已为人所熟知。然而,长期以来弥漫在软件圈的紧张关系已经渗透到了人工智能领域,部分原因在于没有人能在AI背景下就“开源”的真正含义达成一致。 相关阅读:GPT-4o通过整合文本、音频和视觉实现人性化的AI交互&…

上海舆情分析软件的功能和对企业的意义

随着互联网的飞速发展,人们参与讨论、发声的途径与评率也越来越多,在为自己发声的同时,公众舆论也成为企业获取民意,改进发展的重要参考。 上海 舆情分析软件的开发,为企业获取舆论,调查研究提供了便捷化的…

Spring+SpringMVC+MyBatis整合

目录 1.SSM介绍1.1 什么是SSM?1.2 SSM框架1.2.1 Spring1.2.2 SpringMVC1.2.3 MyBatis 2.SSM框架整合2.1 建库建表2.2 创建工程2.3 pom.xml2.4 log4j.properties2.5 db.properties2.6 applicationContext-dao.xml2.7.applicationContext-tx.xml2.8 applicationContex…

浅析缓存技术

缓存技术的原理 缓存技术通过在内存中存储数据副本来加速数据访问。当应用程序需要数据时,首先检查缓存是否存在数据副本,如果有则直接返回,否则再从原始数据源获取。这种机制大大减少了访问时间,提升了系统的响应速度和整体性能。…

Maven深度解析:Java项目构建

Maven是一个由Apache软件基金会维护的软件项目管理和理解工具,它主要服务于基于Java的软件项目。。 Maven深度解析:Java项目构建 引言 在Java开发领域,项目构建和管理是一个复杂而关键的任务。Maven作为这一领域的佼佼者,以其声…

vscode下无法识别node、npm的问题

node : 无法将“node”项识别为 cmdlet、函数、脚本文件或可运行程序的名称 因为node是在cmd安装的,是全局安装的,并不是在这个项目里安装的。 解决方案: 1.在vscode的控制台,针对一个项目安装特定版本的node; 2.已经…

基于Dify的智能分类方案:大模型结合KNN算法(附代码)

大模型相关目录 大模型,包括部署微调prompt/Agent应用开发、知识库增强、数据库增强、知识图谱增强、自然语言处理、多模态等大模型应用开发内容 从0起步,扬帆起航。 大模型应用向开发路径:AI代理工作流大模型应用开发实用开源项目汇总大模…

Spring中事务的传播机制

一、前言 首先事务传播机制解决了什么问题 Spring 事务传播机制是包含多个事务的方法在相互调用时,事务是如何在这些方法间传播的。 事务的传播级别有 7 个,支持当前事务的:REQUIRED、SUPPORTS、MANDATORY; 不支持当前事务的&…

华为某员工爆料:偷偷跑出去面试,被面试官鄙视了。第一句话就问:华为淘汰的吧,35岁了,这个年龄在华为能混得下去吗?身体没啥毛病吧

“你都35岁了,难不成是被华为淘汰的?在华为混不下去了吧?身体没啥毛病吧,我们这体检可是很严的。” 近日,一位华为员工在朋友圈爆料,自己在面试时遭到了面试官的无理取闹和人身攻击,原因仅仅是因…

中东文明史

转自:想要了解完整的中东文明史?这篇文章成全你 - 知乎 (zhihu.com) 写在前面 中东文明是人类历史上最古老的文明。人类祖先从东非大裂谷走出之后,首先选择定居在中东地区的新月沃土上,并建立了人类历史上有文字记载的第一个文明…

利用Frp实现内网穿透(docker实现)

文章目录 1、WSL子系统配置2、腾讯云服务器安装frps2.1、创建配置文件2.2 、创建frps容器 3、WSL2子系统Centos服务器安装frpc服务3.1、安装docker3.2、创建配置文件3.3 、创建frpc容器 4、WSL2子系统Centos服务器安装nginx服务 环境配置:一台公网服务器&#xff08…

【zabbix】zabbix客户端配置

1、部署zabbix客户端 #zabbix 5.0 版本采用 golang 语言开发的新版本客户端 agent2 。 #zabbix 服务端 zabbix_server 默认使用 10051 端口,客户端 zabbix_agent2 默认使用 10050 端口。 systemctl disable --now firewalld setenforce 0 hostnamectl set-hostname…