-
Notifications
You must be signed in to change notification settings - Fork 188
系统级浮窗
floatingx-system 提供的 SystemHost:真正的 WindowManager 窗口,能显示在应用之外。
本页讲权限三策略与降级、LayoutParams 定制、键盘与返回键、在 Service 里安装。
SystemHost 把容器挂到 WindowManager 上,是一个独立的系统窗口,可以盖在别的应用之上。
它只需要一个 application context,不依赖 Activity,所以 Service 里也能装(#192)。
模块清单里已经声明了 SYSTEM_ALERT_WINDOW 权限与透明的权限申请页 —— 接入方不需要自己配置任何东西。
FloatingX.install("sys") {
layout(R.layout.fx_input)
anchor(FxGravity.TOP_START, dx = 24f, dy = 200f)
adsorb(FxAdsorb.Edges(setOf(FxEdge.START, FxEdge.END)))
systemHost(app) {
permission(FxPermissionStrategy.auto()) // 默认:自动弹页申请
// 权限被拒时降级为 App 级浮窗(相当于 2.x 的 SYSTEM_AUTO)
fallback(AppHost.builder(app).build())
layoutParams { it.alpha = 0.9f } // 在默认 LayoutParams 之后执行,可覆盖任何字段
keyboard(R.id.etInput) // 触到这些 EditText 时窗口临时可聚焦、弹键盘
onBackPressed { true } // 键盘弹起期间才收得到返回键
}
}.show()| 策略 | 行为 |
|---|---|
FxPermissionStrategy.auto() |
默认。无权限时自动弹透明页申请,被拒后走 fallback(没配就停在 INSTALLED) |
FxPermissionStrategy.manual { request -> … } |
交给业务方:request.proceed() 去申请 / useFallback() 直接降级 / deny() 放弃 |
FxPermissionStrategy.skip() |
不检查权限直接挂窗口(已自行申请,或 type 不需要权限) |
manual 拿到的 FxPermissionRequest 三个方法只应调用一个:
systemHost(app) {
permission(
FxPermissionStrategy.manual { request ->
when {
userAgreed -> request.proceed() // 弹系统设置页申请
canDowngrade -> request.useFallback() // 直接降级到 fallback 指定的 host
else -> request.deny() // 放弃:不降级,停在 INSTALLED
}
},
)
fallback(AppHost.builder(app).build())
}被拒后拿到权限,调用 retryPermission() 即可恢复:
(control.host as? SystemHost)?.retryPermission()配了 fallback(...) 时,权限被拒会自动 requestSwap 到指定的 host(通常是 AppHost):
容器换掉,但配置、监听器、feature 与当前位置都保留 —— 这就是 2.x 的 SYSTEM_AUTO。
没配 fallback 则停在 INSTALLED,之后拿到权限再 retryPermission()。
systemHost(app) {
layoutParams { lp ->
lp.alpha = 0.9f
lp.flags = lp.flags or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
}
}customizer 在默认 LayoutParams 之后执行,可以覆盖任何字段(type / flags / alpha …)。
SystemLayoutParamsCustomizer 是 fun interface,Java 侧可以直接写 lambda。
系统窗口默认带 FLAG_NOT_FOCUSABLE(不抢焦点,不然会挡住下层应用的输入)。
用 keyboard(...) 登记内容里的 EditText id,触到这些 EditText 时窗口才临时可聚焦并弹出软键盘:
systemHost(app) {
keyboard(R.id.etInput) // 可变参数,能登记多个 EditText id
onBackPressed { // 只有窗口可聚焦(即键盘弹起)期间才收得到返回键
true // 返回 true 表示已消费
}
}收键盘的那一次返回键会被系统吞掉,不会走到 onBackPressed。
和 App 级浮窗一样,内容用 application context 创建,Material 组件需要 Material 主题:
systemHost(app) { theme(R.style.Theme_App) }SystemHost 只要 application context,前台 Service 里安装是完全合法的用法(#192):
class DemoService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
startAsForeground() // API 34+ 前台 Service 必须声明 foregroundServiceType,且与清单一致
FloatingX.install("sys") { /* … */ systemHost(application) { } }.show()
return START_NOT_STICKY
}
}两条系统限制要注意:
-
Android 10(Q)起后台起不了 Activity:应用在后台时,
auto()策略的权限申请页可能什么都不弹。 真正的后台场景请用manual {}/skip()把申请推迟,等回到前台再retryPermission()。 -
Android 13(T)起通知需用户授权:没授权时
startForeground照样成功,只是通知不显示。
浮窗的生命周期归 FloatingX 注册表(按 tag),不跟着 Service 走 —— Service 销毁时浮窗仍在,
需要收掉就自己 control.cancel() 或 FloatingX.uninstall(tag)。
SystemHost host = SystemHost.builder(app)
.layoutParams(lp -> lp.alpha = 0.9f)
.permission(FxPermissionStrategy.auto())
.fallback(AppHost.builder(app).build())
.build();
FxControl control = FloatingX.install("java-system", config, host);
control.show();屏幕级 insets 的公开入口 WindowManager.getCurrentWindowMetrics() 是 API 30(R)才有的,
R 以下 SystemHost 拿不到 safe area,safeArea 对它不起作用(可用区就是整块屏幕)。
需要在低版本避让状态栏 / 刘海时,自己用 margin(top = …) 留出来。更多问题见 常见问题。