Android 14 TYPE_APPLICATION_OVERLAY悬浮按钮首次点击位置闪回问题
Android 14悬浮气泡拖动后首次点击闪回问题
代码更新[2024-11-28]
问题描述
将悬浮气泡从位置A拖动到位置B后,首次点击位置B处的气泡时,它会短暂闪回/跳转到位置A,随后回到位置B。该问题仅在移动到新位置后的首次点击时出现。
环境信息
- Android Studio Ladybug | 2024.2.1 Patch 2
- Compose版本: 2024.04.01
- 测试设备:
- Android 14(问题复现)
- Android 13及以下版本(正常工作)
相关代码
class FloatingBubbleService : Service() { private lateinit var windowManager: WindowManager private var bubbleView: ComposeView? = null // 记录最后确认的位置 private var lastKnownX = 0 private var lastKnownY = 0 private val viewOwner = object : LifecycleOwner, SavedStateRegistryOwner { val lifecycleRegistry = LifecycleRegistry(this) private val savedStateRegistryController = SavedStateRegistryController.create(this).apply { // 立即初始化SavedStateRegistry performRestore(Bundle()) } init { lifecycleRegistry.currentState = Lifecycle.State.CREATED } override val lifecycle: Lifecycle get() = lifecycleRegistry override val savedStateRegistry: SavedStateRegistry get() = savedStateRegistryController.savedStateRegistry } private val params = WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, PixelFormat.TRANSLUCENT ).apply { gravity = Gravity.TOP or Gravity.START x = 0 y = 0 } override fun onBind(intent: Intent?): IBinder? = null override fun onCreate() { super.onCreate() windowManager = getSystemService(WINDOW_SERVICE) as WindowManager setupFloatingBubble() viewOwner.lifecycleRegistry.currentState = Lifecycle.State.RESUMED } private fun setupFloatingBubble() { bubbleView = ComposeView(this).apply { setViewTreeLifecycleOwner(viewOwner) setViewTreeSavedStateRegistryOwner(viewOwner) var initialX = 0 var initialY = 0 var initialTouchX = 0f var initialTouchY = 0f var isDragging = false setContent { var isMenuVisible by remember{ mutableStateOf(false)} FloatingBubbleTheme { FloatingBubbleContent( isMenuVisible = isMenuVisible, onBubbleClick = { /* 不再需要 */ }, interceptTouchEvents = false ) } setOnTouchListener { view, event -> Log.i(LOGTAG, "接收到触摸事件: ${event.action}") when (event.action) { MotionEvent.ACTION_DOWN -> { Log.i(LOGTAG, "ACTION_DOWN++") isDragging = false initialX = params.x initialY = params.y initialTouchX = event.rawX initialTouchY = event.rawY true } MotionEvent.ACTION_MOVE -> { if (!isDragging && (Math.abs(event.rawX - initialTouchX) > 10 || Math.abs(event.rawY - initialTouchY) > 10)) { isDragging = true } if (isDragging) { val newX = initialX + (event.rawX - initialTouchX).toInt() val newY = initialY + (event.rawY - initialTouchY).toInt() Log.i(LOGTAG, "移动到 ${newX}: ${newY}") params.x = newX params.y = newY try { windowManager.updateViewLayout(view, params) } catch (e: Exception) { e.printStackTrace() } } true } MotionEvent.ACTION_UP -> { if (!isDragging) { // 使用可变状态切换菜单可见性 isMenuVisible = !isMenuVisible } true } else -> false } } } } try { windowManager.addView(bubbleView, params) } catch (e: Exception) { e.printStackTrace() } } override fun onDestroy() { super.onDestroy() bubbleView?.let { windowManager.removeView(it) } viewOwner.lifecycleRegistry.currentState = Lifecycle.State.DESTROYED } }
预期行为
悬浮气泡被点击时应停留在位置B,无位置闪回现象。
Android 14上的实际行为
- 将悬浮气泡从位置A拖动到B
- 点击位置B处的气泡
- 气泡短暂闪回至位置A
- 气泡回到位置B
已尝试方案
- 不同WindowManager.LayoutParams标志组合
- 在onDraw中检查位置更新
- 验证触摸坐标与窗口参数
疑问
这是Android 14 WindowManager的已知问题吗?有没有可避免点击悬浮视图时出现位置闪回的解决方法?
感谢任何帮助或见解!
更新[2024-11-26]
@Composable fun FloatingBubbleContent( isMenuVisible: Boolean, onBubbleClick: () -> Unit, interceptTouchEvents: Boolean = false ) { Box( modifier = Modifier .width(174.dp) // 固定宽度 .height(56.dp) // 固定高度 .then( if (interceptTouchEvents) { Modifier.clickable(onClick = onBubbleClick) } else { Modifier } ) ) { Box( modifier = Modifier .size(56.dp) // 固定气泡大小 .align(Alignment.CenterEnd) // 气泡靠右对齐 ) { BubbleIcon() } AnimatedMenu( isVisible = isMenuVisible, modifier = Modifier.align(Alignment.CenterStart) ) } } @Composable private fun AnimatedMenu( isVisible: Boolean, modifier: Modifier = Modifier ) { AnimatedVisibility( visible = isVisible, enter = fadeIn() + slideIn(initialOffset = { IntOffset(-50, 0) }), exit = fadeOut() + slideOut(targetOffset = { IntOffset(-50, 0) }), modifier = modifier ) { Box( modifier = Modifier .width(118.dp) // 70dp + 48dp .height(56.dp) // 匹配气泡高度 ) { Column( modifier = Modifier .offset(x = (-70).dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { MenuButton( icon = Icons.Rounded.Call, backgroundColor = Color(0xFF4CAF50), onClick = { /* 处理电话事件 */ } ) MenuButton( icon = Icons.Rounded.Email, backgroundColor = Color(0xFF2196F3), onClick = { /* 处理邮件事件 */ } ) } } } }
更新[2024-11-28]
清单文件
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.FloatableIcon" tools:targetApi="31"> <activity android:name=".MainActivity" android:exported="true" android:label="@string/app_name" android:theme="@android:style/Theme.Translucent.NoTitleBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name="com.example.floatableicon.service.FloatingBubbleService" android:enabled="true" android:exported="false" /> </application> </manifest>
内容的提问来源于stack exchange,提问作者Sameer Meena
相关产品推荐
相关产品推荐

