最近在研究使用compose框架实现悬浮窗效果,期间遇到很多问题,各种搜索结果琳琅满目,不是插件就是非常复杂的代码,只能潜心研究,最后得出了这个几行代码就能实现悬浮窗的方案。
为了技术人的脑力能得到应有的回报,核心代码决定付费,相关思想及其他代码在此贴出。
文章目录
- 一、 申请悬浮窗权限
- 二、设计悬浮窗要显示的内容
- 三、悬浮窗响应式
- 四、核心算法
一、 申请悬浮窗权限
- 静态权限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
- 动态权限
val requestPermission = rememberLauncherForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
Log.d(TAG, "requestPermission: $it")
}
var hasOverlayPermission by remember{ mutableStateOf(Settings.canDrawOverlays(context)) }
Button(onClick = {
if (hasOverlayPermission) {
// 移除权限
try {
Settings.canDrawOverlays(context).not()
} catch (e: Exception) {
Log.e(TAG, "requestPermission: ", e)
}
hasOverlayPermission = Settings.canDrawOverlays(context)
}else{
requestPermission.launch(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION))
}
}){
Text(if(hasOverlayPermission) "取消悬浮权限" else "申请悬浮权限")
}
二、设计悬浮窗要显示的内容
此处实现的是一个计数按钮,一个退出悬浮窗的按钮
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun FloatContent(view: ComposeView, layoutParams: LayoutParams, onClose: () -> Unit) {
val drag2dState = rememberDraggable2DState {
layoutParams.x += it.x.toInt()
layoutParams.y += it.y.toInt()
windowManager.updateViewLayout(view, layoutParams)
}
Column(
Modifier
.wrapContentSize()
.background(Color(0xf0ffffff), RoundedCornerShape(10.dp))
.padding(10.dp)
.draggable2D(drag2dState),
horizontalAlignment = Alignment.CenterHorizontally
) {
var count by remember{ mutableIntStateOf(0) }
Text("Count $count")
Button({
count++
}){
Text(text = "Click")
}
Button(onClick = {
onClose()
},
colors = ButtonDefaults.buttonColors(containerColor = Color(0xffa00000))
){
Text(text = "Close")
}
}
}
三、悬浮窗响应式
private var displayFloatingView by mutableStateOf(true)
if(displayFloatingView){
FloatingView{
displayFloatingView = false
}
}
Button(onClick = {
displayFloatingView = !displayFloatingView
}) {
Text(if (displayFloatingView) "关闭悬浮窗" else "显示悬浮窗")
}
四、核心算法
@Composable
fun FloatingView(onClose: () -> Unit) { val context = LocalContext.current
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
val layoutParams by remember {
mutableStateOf(
LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT,
LayoutParams.TYPE_APPLICATION_OVERLAY,
LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
)
)
}
DisposableEffect(Unit) {
val view = ComposeView(context)
view.setContent {
FloatContent(view, layoutParams, onClose)
}
windowManager.addView(view, layoutParams)
onDispose {
windowManager.removeView(view)
}
}
}
这是都能想到的核心算法,但是运行起来之后会报各种错误,比如:
ViewTreeLifecycleOwner not found from androidx.compose.ui.platform.ComposeView
或者
Composed into the View which doesn’t propagateViewTreeSavedStateRegistryOwner!
所以本文需要付费的内容是解决这两个错误的解决方案
一共四行代码,却是其中的精华,你可以自行决定是否付费,也可以去查找其他解决方案
付费方法参考博文: https://mp.weixin.qq.com/s/NtcnF7sg8UevwrMRgoohEQ
你也可以私信我其他的付费方案