场景描述
app应用会使用悬浮窗/悬浮球的方式来给用户展示一些应用重要&便捷功能的入口,类似android和iOS应用中常见的应用内可拖拽的悬浮球和小窗口视频悬浮窗,点击悬浮窗修改悬浮窗样式和响应事件跳转页面,在跳转页面后依然可以显示在屏幕中上个页面拖拽后的固定位置等。
应用经常会遇到如下的业务诉求:
场景一:通过事件添加和移除悬浮窗,悬浮窗样式可定制(暂定两种,无白边圆球形和小视频播放窗口类型),可代码修改位置和布局。
场景二:创建悬浮窗后,主窗口的系统侧滑返回事件可正常使用。
场景三:可响应正常点击事件,可通过触发拖动使悬浮窗的移动,根据最后手势停留位置,做动画靠屏幕左或靠右显示,跳转和返回上级页面后悬浮窗依然存在,且相对手机屏幕位置不变。
场景四:悬浮窗内组件事件触发主窗口的页面跳转(Router和Navigation两种都要有)。
场景五:悬浮窗的窗口大小自适应组件,子窗口中页面设置了宽高,需要让子窗口自适应页面组件大小。
场景六:支持控制悬浮窗隐藏和销毁。
场景七:视频类应用主动调用画中画完成后台播放,以及返回桌面时自动启动画中画。
方案描述
场景一:
通过事件添加和移除悬浮窗,悬浮窗样式可定制(暂定两种,无白边圆球形和小视频播放窗口类型),可代码修改位置和布局。
效果图
方案
通过子窗口创建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)
}
})
场景二:
创建悬浮窗后,主窗口的系统侧滑返回事件可正常使用。
效果图
方案
通过window.shiftAppWindowFocus转移窗口焦点实现创建子窗口后,主窗口依然可以响应事件。核心代码
在子窗口中将焦点转移到主窗口。
onPageShow(): void {
setTimeout(() => {
// 获取子窗口ID
let s