OpenHarmony实战开发-如何创建悬浮窗

654 篇文章 4 订阅
648 篇文章 6 订阅

场景说明

悬浮窗功能可以基于当前任务创建一个始终在前台显示的窗口。即使创建悬浮窗的任务退至后台,悬浮窗仍然可以在前台显示,通常悬浮窗位于所有应用窗口之上。很多应用都具有悬浮窗的功能,常见的如视频应用的视频播放窗口,在视频应用切换到后台后,视频播放窗口还可以在前台以小窗形式继续播放。本例即为大家介绍如何开发悬浮窗。

效果呈现

本例效果如下:

在这里插入图片描述

运行环境

本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发:

  • IDE: DevEco Studio 4.0 Beta1
  • SDK: Ohos_sdk_public 4.0.7.5 (APIVersion 10 Beta1)

实现思路

本例中主要涉及三项关键操作,相关实现方案如下:

  • 创建悬浮窗:使用window类的createWindow方法创建窗口,窗口类型设置为window.WindowType.TYPE_FLOAT
  • 悬浮窗可拖拽:通过gesture为窗口绑定手势事件,使用PanGesture监听拖拽手势并记录窗口位置,通过moveWindowTo方法将窗口移动到拖拽位置从而实现窗口拖拽。
  • 退出悬浮窗口:使用destroyWindow方法,销毁悬浮窗。

开发步骤

由于本例重点讲解悬浮窗的创建和使用,所以开发步骤会着重讲解相关实现,不相关的内容不做介绍,全量代码可参考完整代码章节。

1.申请权限。

创建悬浮窗需要先申请ohos.permission.SYSTEM_FLOAT_WINDOW权限,要在module.json5文件的requestPermissions对象中进行配置,如下:

{
  "module": {
    "requestPermissions":[
      {
        "name" : "ohos.permission.SYSTEM_FLOAT_WINDOW",
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when":"inuse"
        }
      }
    ]
  }
}

2.创建悬浮窗。

使用window类的createWindow方法创建窗口,窗口类型设置为window.WindowType.TYPE_FLOAT。由于本例通过按钮的点击事件控制悬浮窗的创建和销毁,为了便于操作,本例将创建和销毁悬浮窗的操作写在自定义的方法中,以便绑定到按钮的点击时间中。 创建悬浮窗的操作在自定义方法createFloatWindow中实现。 具体代码如下:

// 引入window类
import window from '@ohos.window';
...
// 自定义创建悬浮窗方法
createFloatWindow() {
  let windowClass = null;
  // 窗口类型设置为window.WindowType.TYPE_FLOAT
  let config = {name: "floatWindow", windowType: window.WindowType.TYPE_FLOAT, ctx: getContext(this)};
  // 创建悬浮窗
  window.createWindow(config, (err, data) => {
    if (err.code) {
      console.error('Failed to create the floatWindow. Cause: ' + JSON.stringify(err));
      return;
    }
    console.info('Succeeded in creating the floatWindow. Data: ' + JSON.stringify(data));
    windowClass = data;
  }
}

3.设置窗口信息。

创建悬浮窗时,可以对窗口的位置、大小、内容等进行设置。 具体代码如下:

...
window.createWindow(config, (err, data) => {
  ...
  windowClass = data;
  // 设置悬浮窗位置
  windowClass.moveWindowTo(300, 300, (err) => {
    if (err.code) {
      console.error('Failed to move the window. Cause:' + JSON.stringify(err));
      return;
    }
    console.info('Succeeded in moving the window.');
  });
  // 设置悬浮窗大小
  windowClass.resize(500, 500, (err) => {
    if (err.code) {
      console.error('Failed to change the window size. Cause:' + JSON.stringify(err));
      return;
    }
    console.info('Succeeded in changing the window size.');
  });
  //为悬浮窗加载页面内容,这里可以设置在main_pages.json中配置的页面
  windowClass.setUIContent("pages/FloatContent", (err) => {
    if (err.code) {
      console.error('Failed to load the content. Cause:' + JSON.stringify(err));
      return;
    }
    console.info('Succeeded in loading the content.');
    // 显示悬浮窗。
    windowClass.showWindow((err) => {
      if (err.code) {
        console.error('Failed to show the window. Cause: ' + JSON.stringify(err));
        return;
      }
      console.info('Succeeded in showing the window.');
    });
  });
});

4.销毁悬浮窗。

使用destroyWindow方法销毁悬浮窗,为了便于通过按钮点击控制悬浮窗的销毁,我们这里将销毁逻辑写在自定义方法destroyFloatWindow中。 具体代码如下:

// 定义windowClass变量,用来接收创建的悬浮窗
private windowClass: window.Window;

createFloatWindow() {
  ...
  // 创建悬浮窗。
  window.createWindow(config, (err, data) => {
    if (err.code) {
      console.error('Failed to create the floatWindow. Cause: ' + JSON.stringify(err));
      return;
    }
    console.info('Succeeded in creating the floatWindow. Data: ' + JSON.stringify(data));
    // 用windowClass变量接收创建的悬浮窗
    this.windowClass = data;
    ...
  }
}
// 自定义销毁悬浮窗方法
destroyFloatWindow() {
  // 用windowClass调用destroyWindow销毁悬浮窗
  this.windowClass.destroyWindow((err) => {
    if (err.code) {
      console.error('Failed to destroy the window. Cause: ' + JSON.stringify(err));
      return;
    }
    console.info('Succeeded in destroying the window.');
  });
}

5.构建主页面UI。

将创建悬浮窗和销毁悬浮窗绑定到对应的按钮上。 具体代码如下:

...
build() {
  Row() {
    Column() {
      Button('创建悬浮窗')
        .onClick(() => {
          // 点击按钮调用创建悬浮窗方法
          this.createFloatWindow();
        })
      Button('销毁悬浮窗')
        .margin({top:20})
        .onClick(() => {
          // 点击按钮调用销毁悬浮窗方法
          this.destroyFloatWindow();
        })
    }
    .width('100%')
  }
  .height('100%')
}
...

6.创建悬浮窗的显示页面并实现悬浮窗可拖拽。

为页面内容绑定PanGesture拖拽事件,拖拽事件发生时获取到触摸点的位置信息,使用@Watch监听到位置变量的变化,然后调用窗口的moveWindowTo方法将窗口移动到对应位置,从而实现拖拽效果。 具体代码如下:

import window from '@ohos.window';

interface Position {
  x: number,
  y: number
}
@Entry
@Component
struct FloatContent {
  @State message: string = 'float window'
  private panOption: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.All });
  // 创建位置变量,并使用@Watch监听,变量发生变化调用moveWindow方法移动窗口
  @State @Watch("moveWindow") windowPosition: Position = { x: 0, y: 0 };
  floatWindow: window.Window
  // 通过悬浮窗名称“floatWindow”获取到创建的悬浮窗
  aboutToAppear() {
    this.floatWindow = window.findWindow("floatWindow")
  }
  // 将悬浮窗移动到指定位置
  moveWindow() {
    this.floatWindow.moveWindowTo(this.windowPosition.x, this.windowPosition.y);
  }

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
      }
      .width('100%')
    }
    .height('100%')
    .gesture(
      // 绑定PanGesture事件,监听拖拽动作
      PanGesture(this.panOption)
        .onActionStart((event: GestureEvent) => {
          console.info('Pan start');
        })
        .onActionUpdate((event: GestureEvent) => {
          // 发生拖拽时,获取到触摸点的位置,并将位置信息传递给windowPosition
          this.windowPosition.x += event.offsetX;
          this.windowPosition.y += event.offsetY;
        })
        .onActionEnd(() => {
          console.info('Pan end');
        })
    )
    .border({
      style: BorderStyle.Dotted
    })
    .backgroundColor(Color.Yellow)
  }
}

完整代码

本例完整代码如下: 主窗口代码(FloatWindow.ets):

//FloatWindow.ets
// 引入window类
import window from '@ohos.window';

@Entry
@Component
struct FloatWindow {
  // 定义windowClass变量,用来接收创建的悬浮窗
  private windowClass: window.Window;
  // 自定义创建悬浮窗方法
  createFloatWindow() {
    let windowClass = null;
    // 窗口类型设置为window.WindowType.TYPE_FLOAT
    let config = {name: "floatWindow", windowType: window.WindowType.TYPE_FLOAT, ctx: getContext(this)};
    // 创建悬浮窗
    window.createWindow(config, (err, data) => {
      if (err.code) {
        console.error('Failed to create the floatWindow. Cause: ' + JSON.stringify(err));
        return;
      }
      console.info('Succeeded in creating the floatWindow. Data: ' + JSON.stringify(data));
      windowClass = data;
      // 用windowClass变量接收创建的悬浮窗
      this.windowClass = data;
      // 设置悬浮窗位置
      windowClass.moveWindowTo(300, 300, (err) => {
        if (err.code) {
          console.error('Failed to move the window. Cause:' + JSON.stringify(err));
          return;
        }
        console.info('Succeeded in moving the window.');
      });
      // 设置悬浮窗大小
      windowClass.resize(500, 500, (err) => {
        if (err.code) {
          console.error('Failed to change the window size. Cause:' + JSON.stringify(err));
          return;
        }
        console.info('Succeeded in changing the window size.');
      });
      // 为悬浮窗加载页面内容,这里可以设置在main_pages.json中配置的页面
      windowClass.setUIContent("pages/FloatContent", (err) => {
        if (err.code) {
          console.error('Failed to load the content. Cause:' + JSON.stringify(err));
          return;
        }
        console.info('Succeeded in loading the content.');
        // 显示悬浮窗。
        windowClass.showWindow((err) => {
          if (err.code) {
            console.error('Failed to show the window. Cause: ' + JSON.stringify(err));
            return;
          }
          console.info('Succeeded in showing the window.');
        });
      });

    });
  }
  // 自定义销毁悬浮窗方法
  destroyFloatWindow() {
    // 用windowClass调用destroyWindow销毁悬浮窗
    this.windowClass.destroyWindow((err) => {
      if (err.code) {
        console.error('Failed to destroy the window. Cause: ' + JSON.stringify(err));
        return;
      }
      console.info('Succeeded in destroying the window.');
    });
  }

  build() {
    Row() {
      Column() {
        Button('创建悬浮窗')
          .backgroundColor('#F9C449')
          .onClick(() => {
            // 点击按钮调用创建悬浮窗方法
            this.createFloatWindow();
          })
        Button('销毁悬浮窗')
          .margin({top:20})
          .backgroundColor('#F9C449')
          .onClick(() => {
            // 点击按钮调用销毁悬浮窗方法
            this.destroyFloatWindow();
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

悬浮窗内容页代码(FloatContent.ets):

//FloatContent.ets
import window from '@ohos.window';

interface Position {
  x: number,
  y: number
}
@Entry
@Component
struct FloatContent {
  @State message: string = 'float window'
  private panOption: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.All });
  // 创建位置变量,并使用@Watch监听,变量发生变化调用moveWindow方法移动窗口
  @State @Watch("moveWindow") windowPosition: Position = { x: 0, y: 0 };
  floatWindow: window.Window
  // 通过悬浮窗名称“floatWindow”获取到创建的悬浮窗
  aboutToAppear() {
    this.floatWindow = window.findWindow("floatWindow")
  }
  // 将悬浮窗移动到指定位置
  moveWindow() {
    this.floatWindow.moveWindowTo(this.windowPosition.x, this.windowPosition.y);
  }

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(30)
          .fontColor(Color.White)
          .fontWeight(FontWeight.Bold)
      }
      .width('100%')
    }
    .height('100%')
    .gesture(
      // 绑定PanGesture事件,监听拖拽动作
      PanGesture(this.panOption)
        .onActionStart((event: GestureEvent) => {
          console.info('Pan start');
        })
        // 发生拖拽时,获取到触摸点的位置,并将位置信息传递给windowPosition
        .onActionUpdate((event: GestureEvent) => {
          this.windowPosition.x += event.offsetX;
          this.windowPosition.y += event.offsetY;
        })
        .onActionEnd(() => {
          console.info('Pan end');
        })
    )
    .border({
      style: BorderStyle.Dotted
    })
    .backgroundColor("#E8A49C")
  }
}

我这边特意整理了《鸿蒙语法ArkTS、TypeScript、ArkUI、实战开发视频教程》以及《鸿蒙生态应用开发白皮书V2.0PDF》《鸿蒙开发学习手册》(共计890页)鸿蒙开发资料等…希望对大家有所帮助:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

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

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

应用开发中级就业技术:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

应用开发中高级就业技术:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

南北双向高工技能基础:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

全网首发-工业级 南向设备开发就业技术: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.……

在这里插入图片描述

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要让el-select显示悬浮窗,可以使用el-cascader组件,它可以实现级联选择,也可以显示悬浮窗。具体实现方法如下: 1. 在el-select中使用el-cascader组件,设置options属性为一个数组,数组中每个元素为一个对象,该对象包含label和value属性,分别表示选项的文本和值。 2. 设置el-cascader的show-all-levels属性为true,这样就可以显示悬浮窗。 3. 设置el-cascader的expand-trigger属性为'hover',这样就可以在鼠标悬停时展开悬浮窗。 例如,以下代码实现了一个带有悬浮窗的el-select: ``` <template> <el-select v-model="value" placeholder="请选择"> <el-cascader :options="options" :show-all-levels="true" :expand-trigger="'hover'" ></el-cascader> </el-select> </template> <script> export default { data() { return { value: '', options: [ { value: 'zhinan', label: '指南', children: [ { value: 'shejiyuanze', label: '设计原则', children: [ { value: 'yizhi', label: '一致', }, { value: 'fankui', label: '反馈', }, { value: 'xiaolv', label: '效率', }, { value: 'kekong', label: '可控', }, ], }, { value: 'daohang', label: '导航', children: [ { value: 'cexiangdaohang', label: '侧向导航', }, { value: 'dingbudaohang', label: '顶部导航', }, ], }, ], }, { value: 'zujian', label: '组件', children: [ { value: 'basic', label: 'Basic', children: [ { value: 'layout', label: 'Layout 布局', }, { value: 'color', label: 'Color 色彩', }, { value: 'typography', label: 'Typography 字体', }, { value: 'icon', label: 'Icon 图标', }, { value: 'button', label: 'Button 按钮', }, ], }, { value: 'form', label: 'Form', children: [ { value: 'radio', label: 'Radio 单选框', }, { value: 'checkbox', label: 'Checkbox 多选框', }, { value: 'input', label: 'Input 输入框', }, { value: 'input-number', label: 'InputNumber 计数器', }, { value: 'select', label: 'Select 选择器', }, { value: 'cascader', label: 'Cascader 级联选择器', }, ], }, ], }, ], }; }, }; </script> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值