-
Notifications
You must be signed in to change notification settings - Fork 188
Compose 浮窗
petterp edited this page Aug 30, 2026
·
1 revision
floatingx-compose 让浮窗内容直接是一段组合:compose { control -> … }。
组合跑在浮窗自己的 FxComposeOwner 上,所以 viewModel() / rememberSaveable 的状态不随宿主
Activity 销毁而清空。模块 minSdk 23,见 快速开始。
FloatingX.install("compose") {
compose { control ->
val vm: CounterViewModel = viewModel() // 归浮窗自己的 ViewModelStore
var count by rememberSaveable { mutableIntStateOf(0) } // 容器 detach 也不丢
val state by control.stateFlow().collectAsState() // FxState
val pos by control.positionFlow().collectAsState() // 内容左上角的屏幕坐标
Surface(shape = CircleShape, modifier = Modifier.size(110.dp)) {
Column(Modifier.clickable { count++; vm.clicks++ }) {
Text("count $count")
Text("${pos.x.toInt()},${pos.y.toInt()} $state")
}
}
}
anchor(FxGravity.CENTER_START, dy = -100f)
appHost(app)
}.show()compose {} 只是设置内容,其余配置项(锚点 / 吸附 / 手势 / 动画 / 持久化)与 View 内容完全一样,
见 配置项。
每个浮窗自带一个 FxComposeOwner,同时是 LifecycleOwner / ViewModelStoreOwner /
SavedStateRegistryOwner,归 control 所有(不归宿主 Activity)。它的生命周期跟着浮窗走:
| 浮窗动作 | owner 的 Lifecycle.State |
|---|---|
attach(容器挂上宿主) |
STARTED |
show |
RESUMED |
detach(换页、被黑名单卸下、旋转重建) |
CREATED |
cancel() |
DESTROYED(终态) |
只有 cancel() 才会 DESTROYED,所以换页、旋转、被黑名单卸下再回来,整棵组合的状态都不重来
(#210/#239)。
-
viewModel()落在 owner 的ViewModelStore,存活范围是install ~ cancel,与页面无关。 -
rememberSaveable走浮窗自己的过桥仓库:组合被 dispose(容器 detach)时存进去,重新组合时取回。
compose {} 里的 rememberSaveable 能跨进程恢复吗?
不能。它只在进程内保存:组合被 dispose(容器 detach)时存进浮窗自己的过桥仓库,
重新组合时取回;进程被杀就没了(浮窗本身也不会自动重建)。要跨进程恢复请自己持久化。
public fun FxControl.stateFlow(): StateFlow<FxState>
public fun FxControl.positionFlow(): StateFlow<FxPoint>-
stateFlow()—— 浮窗状态(INSTALLED/ATTACHED/SHOWN/CANCELLED)。 -
positionFlow()—— 内容左上角的屏幕坐标(等同control.position,拖动中每帧更新、moveTo与吸附结束时更新)。注意与FxListener.onDrag/onDragEnd回调里的 x/y 不同,那两个是 相对容器的坐标。 - 两者都必须在主线程调用,flow 也只在主线程更新;同一个 control 多次调用返回同一条 flow。
-
cancel()之后 core 会清空 listeners,flow 停在最后一个值上,不再更新。
compose {} 不关心浮窗挂在哪,App 级与系统级写法完全一样,换掉 host 那几行即可:
FloatingX.install("compose-sys") {
compose { control -> /* 与上面完全相同的内容 */ }
anchor(FxGravity.CENTER_START, dy = 60f)
systemHost(app) {
permission(FxPermissionStrategy.auto())
fallback(AppHost.builder(app).build())
}
}.show()权限被拒降级到 App 级全局浮窗 时容器换掉,但 owner 归 control 所有,组合状态照样保留。 系统浮窗的权限策略见 系统级浮窗。