【鸿蒙实战开发】基于子窗口实现应用内悬浮窗

场景描述

app应用会使用悬浮窗/悬浮球的方式来给用户展示一些应用重要&便捷功能的入口,类似android和iOS应用中常见的应用内可拖拽的悬浮球和小窗口视频悬浮窗,点击悬浮窗修改悬浮窗样式和响应事件跳转页面,在跳转页面后依然可以显示在屏幕中上个页面拖拽后的固定位置等。

应用经常会遇到如下的业务诉求:

场景一:通过事件添加和移除悬浮窗,悬浮窗样式可定制(暂定两种,无白边圆球形和小视频播放窗口类型),可代码修改位置和布局。

场景二:创建悬浮窗后,主窗口的系统侧滑返回事件可正常使用。

场景三:可响应正常点击事件,可通过触发拖动使悬浮窗的移动,根据最后手势停留位置,做动画靠屏幕左或靠右显示,跳转和返回上级页面后悬浮窗依然存在,且相对手机屏幕位置不变。

场景四:悬浮窗内组件事件触发主窗口的页面跳转(Router和Navigation两种都要有)。

场景五:悬浮窗的窗口大小自适应组件,子窗口中页面设置了宽高,需要让子窗口自适应页面组件大小。

场景六:支持控制悬浮窗隐藏和销毁。

场景七:视频类应用主动调用画中画完成后台播放,以及返回桌面时自动启动画中画。

方案描述

场景一:

通过事件添加和移除悬浮窗,悬浮窗样式可定制(暂定两种,无白边圆球形和小视频播放窗口类型),可代码修改位置和布局。

效果图

image.png

方案

通过子窗口创建windowStage.

createSubWindow(‘mySubWindow’),和windowClass.setWindowLayoutFullScreen去除白边。

核心代码

在EntryAbility中获取WindowStage。

onWindowStageCreate(windowStage: window.WindowStage): void { 
  // Main window is created, set main page for this ability 
  hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); 

  windowStage.loadContent('pages/Page', (err, data) => { 
  if (err.code) { 
  hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); 
  return; 
} 
// 保存窗口管理器 
AppStorage.setOrCreate("windowStage", windowStage); 
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? ''); 
}); 
}

创建子窗口,子窗口样式由子窗口加载的页面组件样式决定。

this.windowStage.createSubWindow("mySubWindow", (err, windowClass) => { 
  if (err.code > 0) { 
    console.error("failed to create subWindow Cause:" + err.message) 
    return; 
  } 
  try { 
    // 设置子窗口加载页 
    windowClass.setUIContent("pages/MySubWindow", () => { 
      windowClass.setWindowBackgroundColor("#00000000") 
    }); 
    // 设置子窗口左上角坐标 
    windowClass.moveWindowTo(0, 200) 
    // 设置子窗口大小 
    windowClass.resize(vp2px(75), vp2px(75)) 
    // 展示子窗口 
    windowClass.showWindow(); 
    // 设置子窗口全屏化布局不避让安全区 
    windowClass.setWindowLayoutFullScreen(true); 
  } catch (err) { 
    console.error("failed to create subWindow Cause:" + err) 
  } 
})
场景二:

创建悬浮窗后,主窗口的系统侧滑返回事件可正常使用。

效果图

image.png

方案

通过window.shiftAppWindowFocus转移窗口焦点实现创建子窗口后,主窗口依然可以响应事件。

核心代码

在子窗口中将焦点转移到主窗口。

onPageShow(): void { 
 
很遗憾,提供的引用内容中没有直接给出基于鸿蒙5.0使用ArKTS语言实现基于窗口应用内悬浮窗的完整代码。不过可以根据引用中的业务诉求和窗口管理职责,给出一个大致的代码框架示例: ```typescript // 引入必要的模块 import window from '@ohos.window'; import router from '@ohos.router'; // 定义悬浮窗类 class FloatingWindow { private window: window.Window | null = null; private isVisible: boolean = false; // 初始化悬浮窗 async init() { try { // 创建窗口 this.window = await window.createSubWindow({ name: 'floatingWindow', type: window.WindowType.SUB_WINDOW_FLOATING, // 可以根据场景需求设置初始位置和大小 x: 100, y: 200, width: 200, height: 200 }); // 加载UI界面 await this.window.loadContent('pages/FloatingWindowPage'); // 启用窗口装饰(可选) this.window.enableWindowDecoration(true); // 添加窗口显示动效 this.window.showWithAnimation(); // 设置窗口可触摸和获焦 this.window.setTouchable(true); this.window.setFocusable(true); // 处理输入事件分发 this.window.on('touchEvent', (event) => { // 处理触摸事件 if (event.type === 'down') { // 开始拖动 } else if (event.type === 'move') { // 拖动过程中更新位置 } else if (event.type === 'up') { // 拖动结束,根据手势位置做动画靠屏幕左或靠右显示 } }); // 处理组件事件触发主窗口页面跳转 const floatingWindowComponent = this.window.getComponentById('floatingComponent'); floatingWindowComponent.on('click', () => { // Router跳转 router.pushUrl({ url: 'pages/MainPage' }); // Navigation跳转 router.navigate({ url: 'pages/MainPage' }); }); this.isVisible = true; } catch (e) { console.error('Failed to create floating window:', e); } } // 隐藏悬浮窗 hide() { if (this.window && this.isVisible) { this.window.hideWithAnimation(); this.isVisible = false; } } // 销毁悬浮窗 destroy() { if (this.window) { this.window.destroy(); this.window = null; this.isVisible = false; } } } // 在主页面使用悬浮窗 @Entry @Component struct MainPage { private floatingWindow: FloatingWindow = new FloatingWindow(); build() { Column({ space: 50 }) { Button('Add Floating Window') .onClick(() => { this.floatingWindow.init(); }) Button('Hide Floating Window') .onClick(() => { this.floatingWindow.hide(); }) Button('Destroy Floating Window') .onClick(() => { this.floatingWindow.destroy(); }) } .width('100%') } } ``` 上述代码是一个简单的示例,实现了基于窗口应用内悬浮窗的基本功能,包括创建、显示、隐藏、销毁,以及处理触摸事件和页面跳转。需要根据实际需求进行进一步的完善和调整。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值