uni-app 自带 API 无法实现真悬浮窗,因其 UI 渲染在应用窗口内,后台即被系统挂起;真悬浮窗需原生插件封装 WindowManager + TYPE_APPLICATION_OVERLAY,且 Android 11+ 需适配权限检测与跳转逻辑。

不能靠 position: fixed 或 cover-view 实现真悬浮窗,App 一退到后台就消失;必须用原生插件封装 WindowManager + TYPE_APPLICATION_OVERLAY,且 Android 11+ 起权限检测和跳转逻辑稍有不同。
为什么 uni-app 自带 API 做不了悬浮窗
uni-app 的所有 UI(包括 cover-view、subNVue、position: fixed)都渲染在 App 自己的窗口内。一旦切到后台,这个窗口就被系统挂起或隐藏,上面的内容自然不可见。Android 真正能跨应用显示的悬浮层,必须走系统级 WindowManager,并使用 TYPE_APPLICATION_OVERLAY 类型窗口——这层能力 JS 无法直接触达。
-
uni.openSetting()对SYSTEM_ALERT_WINDOW权限完全无效,它只处理普通运行时权限(如存储、定位) -
plus.android.requestPermissions在 Android 6.0+ 后已不支持该权限申请 - iOS 端根本无解:系统禁止第三方 App 创建跨应用 UIWindow,所谓“iOS 悬浮窗”全是伪实现
Android 11+ 权限检测与跳转必须用 canDrawOverlays()
从 Android 11(API 30)开始,Settings.ACTION_MANAGE_OVERLAY_PERMISSION 不再响应隐式 Intent,必须先调用 Settings.canDrawOverlays(context) 判断,再构造带包名的 Uri 跳转。漏掉判断,跳转会静默失败。
- JS 层无法直接调用
canDrawOverlays(),必须由原生插件封装成checkPermission()方法返回 Promise - 跳转 Intent 必须写成:
Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:"+getPackageName())) - 跳转后不能假设用户已开启——需监听 Activity 返回(如
onActivityResult或ActivityResultLauncher),插件内部才 resolve Promise - 测试务必用真机:模拟器默认禁用 overlay 权限且不可开启
悬浮窗显示不出来?重点检查这四个参数
即使权限已开、插件已加载,悬浮窗仍可能黑屏、错位或点击无响应,问题几乎全出在 WindowManager.LayoutParams 配置上。
-
type:Android 8.0+ 必须用TYPE_APPLICATION_OVERLAY,TYPE_PHONE等已被废弃,否则addView()直接抛BadTokenException -
flags:至少含FLAG_NOT_FOCUSABLE(否则抢焦点导致主界面失活);如需点击穿透到底层 App,还得加FLAG_NOT_TOUCH_MODAL和FLAG_WATCH_OUTSIDE_TOUCH -
format:必须设为Pixelformat.TRANSLUCENT,否则半透明背景失效,内容不可见 -
x/y:初始坐标建议避开状态栏(如y = 120),部分厂商 ROM 会把 y=0 的窗口强行下压
自定义基座是调试前提,改了就得重做
所有原生插件(如 Ba-FloatWindow、dh-float-window)都依赖自定义基座运行,标准基座不加载原生模块。
- HBuilderX 中必须勾选插件 → 点击「制作自定义调试基座」,耗时约 5–10 分钟
- 每次修改插件 Java/UTS 代码、图标资源(如
drawable-xxhdpi/custom_icon.png)、或manifest.json中的权限声明,都必须重新制作基座 - 云打包前确认插件已在
manifest.json的「App原生插件配置」中启用,且云端插件已绑定当前 AppID - 图标路径填的是文件名(不含扩展名),例如
iconPath: "music_float_icon",对应文件是music_float_icon.png
最常被忽略的点是:插件返回的宽高单位是 px,但传入的 widthRatio/heightRatio 是比例值,而 rpx 到 dp 的换算必须在插件层完成——JS 层传 750rpx,插件若没按设备密度转成正确 dp,尺寸就会严重失真。这不是配置问题,是底层像素对齐逻辑没兜住。


















