OpenHarmony实战开发-如何使用position绝对定位实现应用内悬浮窗。

介绍

本示例使用position绝对定位实现应用内悬浮窗,并且通过animateTo结合curves动画曲线实现悬浮窗拖拽跟手和松手吸附边缘的弹性动画效果。

效果图预览

在这里插入图片描述

使用说明

按住悬浮窗可以拖拽,松开后悬浮窗自动靠左或靠右,如果悬浮窗超出内容区上下边界,自动吸附在边界位置。

实现思路

1.悬浮窗组件使用Stack嵌套video布局,使用属性position绝对定位使组件悬浮。

Stack({ alignContent: Alignment.Bottom }) {
  Video({
    src: $rawfile('float_window_video.mp4'),
    controller: this.videoController
  })
    .controls(false)
    .autoPlay(true)
    .loop(true)
    .muted(true)
    .width($r('app.string.float_window_full_size'))
    .onClick(() => {
      this.videoController.requestFullscreen(true);
    })
    .borderRadius($r('app.string.ohos_id_corner_radius_default_l'))
  Text($r('app.string.float_window_live_text'))
    .width($r('app.string.float_window_full_size'))
    .fontSize($r('app.string.ohos_id_text_size_body1'))
    .fontColor($r('app.color.ohos_id_color_background'))
    .textAlign(TextAlign.Center)
    .backgroundColor($r('app.color.ohos_id_color_list_alert'))
    .borderRadius({
      bottomLeft: $r('app.string.ohos_id_corner_radius_default_l'),
      bottomRight: $r('app.string.ohos_id_corner_radius_default_l')
    })
}
.clip(true)
.border({
  width: $r('app.integer.float_window_border_width'),
  color: $r('app.color.ohos_id_color_background')
})
.borderRadius($r('app.string.ohos_id_corner_radius_default_l'))
.width(Constants.FLOAT_WINDOW_WIDTH)
.height(Constants.FLOAT_WINDOW_HEIGHT)
.backgroundColor($r('app.color.ohos_id_color_foreground'))
.position({ x: this.positionX, y: this.positionY })
.onTouch((event: TouchEvent) => {
  this.onTouchEvent(event);
})

2.在悬浮窗组件的aboutToAppear中获取应用窗口尺寸,使用窗口宽度减去悬浮窗宽度和右边距让悬浮窗初始靠右。

try {
    const properties = windowClass.getWindowProperties();
    // 获取应用窗口宽高
    this.windowRectWidth = px2vp(properties.windowRect.width);
    this.windowRectHeight = px2vp(properties.windowRect.height)
    // 窗口宽度减去悬浮窗宽度和右边距让悬浮窗初始靠右
    this.positionX = this.windowRectWidth - Constants.FLOAT_WINDOW_WIDTH - Constants.PAGE_PADDING;
  } catch (exception) {
    logger.error(TAG, 'Failed to obtain the window properties. Cause: ' + JSON.stringify(exception));
  }

3.使用getWindowAvoidArea获取顶部状态栏高度和底部导航栏高度。

try {
    const avoidArea = windowClass.getWindowAvoidArea(type);
    // 获取顶部状态栏高度
    this.topRectHeight = px2vp(avoidArea.topRect.height);
    // 获取底部导航栏高度
    this.bottomRectHeight = px2vp(avoidArea.bottomRect.height);
  } catch (exception) {
    logger.error(TAG, 'Failed to obtain the area. Cause:' + JSON.stringify(exception));
  }

4.悬浮窗组件添加onTouchEvent回调,在手指按下时保存触摸点与悬浮窗左上角的偏移量offsetX和offsetY,用于移动时悬浮窗位置的计算。

case TouchType.Down: {
    this.offsetX = event.touches[0].x;
    this.offsetY = event.touches[0].y;
    break;
  }

5.手指移动时,获取触摸点相对于应用窗口左上角的X和Y坐标,通过计算设置悬浮窗的position坐标实现拖拽,使用默认参数的弹性跟手动画曲线curves.responsiveSpringMotion结合animateTo实现跟手动画效果。

 case TouchType.Move: {
    const windowX: number = event.touches[0].windowX;
    const windowY: number = event.touches[0].windowY;
    // TODO:知识点:跟手动画,推荐使用默认参数的弹性跟手动画曲线curves.responsiveSpringMotion。
    animateTo({ curve: curves.responsiveSpringMotion() }, () => {
      this.positionX = windowX - this.offsetX - Constants.PAGE_PADDING;
      this.positionY = windowY - this.offsetY - this.topRectHeight - Constants.PAGE_PADDING; // 减去手指位置到悬浮窗左上角的y轴偏移和设备顶部状态栏高度
    })
    break;
  }

6.手指抬起时,通过判断悬浮窗中心在水平方向位于窗口中心的左侧或右侧设置悬浮窗靠左或靠右,如果悬浮窗超出内容区上下边界,则将悬浮窗设置在边界位置,使用curves.springMotion弹性动画曲线实现吸附边界时的弹性动画效果。

case TouchType.Up: {
    // TODO:知识点:通过判断悬浮窗在窗口中的位置,设置悬浮窗贴边,使用curves.springMotion()弹性动画曲线,可以实现阻尼动画效果
    animateTo({ curve: curves.springMotion() }, () => {
      // 判断悬浮窗中心在水平方向是否超过窗口宽度的一半,根据结果设置靠左或靠右
      if (this.positionX > (this.windowRectWidth - Constants.FLOAT_WINDOW_WIDTH) / 2) {
        this.positionX = this.windowRectWidth - Constants.FLOAT_WINDOW_WIDTH - Constants.PAGE_PADDING; // 悬浮窗靠右
      } else {
        this.positionX = Constants.PAGE_PADDING; // 悬浮窗靠左
      }
      // 页面高度
      const pageHeight: number = this.windowRectHeight - this.topRectHeight - this.bottomRectHeight;
      // 判断悬浮窗是否超出内容区上下边界,根据结果将悬浮窗设置在边界位置
      if (this.positionY < Constants.PAGE_PADDING) {
        this.positionY = Constants.PAGE_PADDING;
      } else if (this.positionY > pageHeight - Constants.FLOAT_WINDOW_HEIGHT - Constants.PAGE_PADDING) {
        this.positionY = pageHeight - Constants.FLOAT_WINDOW_HEIGHT - Constants.PAGE_PADDING;
      }
    })
    break;
  }

高性能知识点

不涉及

工程结构&模块类型

floatwindow                                  // har类型
|---/src/main/ets/common                        
|   |---Constants.ets                        // 常量
|---/src/main/ets/pages                        
|   |---FloatWindowMainPage.ets              // 视图层-悬浮窗首页

模块依赖

  1. 本实例依赖common模块中的日志工具类logger。
  2. 本示例依赖动态路由模块来实现页面的动态加载。

如果大家还没有掌握鸿蒙,现在想要在最短的时间里吃透它,我这边特意整理了《鸿蒙语法ArkTS、TypeScript、ArkUI等…视频教程》以及《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

鸿蒙语法ArkTS、TypeScript、ArkUI等…视频教程:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

OpenHarmony APP应用开发教程步骤:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

《鸿蒙开发学习手册》:

如何快速入门:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

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

在这里插入图片描述

开发基础知识:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

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

在这里插入图片描述

基于ArkTS 开发:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

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

在这里插入图片描述

鸿蒙生态应用开发白皮书V2.0PDF:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值