如何阻止无障碍服务点击悬浮气泡内的控件?
问题描述
我正在为项目实现自动点击功能,希望无障碍服务启动运行时无法与悬浮气泡交互,但目前仍能点击气泡内的按钮。以下是相关代码:
onCreate Bubble Service
override fun onCreate() { super.onCreate() val pm = getSystemService(Context.POWER_SERVICE) as PowerManager mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "layout_bubble:service") mBubbleView = LayoutInflater.from(this).inflate(R.layout.layout_bubble, null) mBubbleParams = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, PixelFormat.TRANSLUCENT ) } else { WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT ) } mBubbleParams?.gravity = Gravity.CENTER_VERTICAL or Gravity.START mWindowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager mWindowManager?.addView(mBubbleView, mBubbleParams) }
AutoClick Service
override fun onServiceConnected() { super.onServiceConnected() autoClickService = this startService(Intent(this, AutoClickService::class.java) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)) } fun click(x: Float, y: Float) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return val path = Path() path.moveTo(x, y) val builder = GestureDescription.Builder() val gestureDescription = builder .addStroke(GestureDescription.StrokeDescription(path, 0L, 10L)) .build() this.dispatchGesture(gestureDescription, null, null) }
start Auto Click
if (autoClickService != null) { autoClickService?.click(x,y) Thread.sleep(40L) }
解决方案
核心思路是根据无障碍服务的运行状态,拦截悬浮气泡的触摸事件,阻止内部按钮响应点击。推荐两种实现方式:
方法一:视图层级拦截触摸事件(最简实现)
直接在悬浮气泡的根视图上添加触摸拦截逻辑,当无障碍服务处于运行状态时,消费所有触摸事件,不让事件传递到内部按钮。
修改BubbleService的onCreate方法,添加触摸监听:
override fun onCreate() { super.onCreate() val pm = getSystemService(Context.POWER_SERVICE) as PowerManager mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "layout_bubble:service") mBubbleView = LayoutInflater.from(this).inflate(R.layout.layout_bubble, null) // 新增:触摸拦截逻辑 mBubbleView.setOnTouchListener { _, _ -> // 当无障碍服务已启动(autoClickService不为null),拦截所有触摸事件 autoClickService != null } // ... 原有LayoutParams代码 ... mWindowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager mWindowManager?.addView(mBubbleView, mBubbleParams) }
方法二:动态修改WindowManager参数
通过修改悬浮窗口的布局参数,在无障碍服务运行时添加FLAG_NOT_TOUCHABLE标记,让整个窗口无法接收触摸事件。
1. 在无障碍服务中通知状态变化
修改AutoClickService的生命周期方法,通知气泡服务当前状态:
override fun onServiceConnected() { super.onServiceConnected() autoClickService = this startService(Intent(this, AutoClickService::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)) // 通知气泡服务:无障碍已启动 updateBubbleTouchState(false) } override fun onDestroy() { super.onDestroy() autoClickService = null // 通知气泡服务:无障碍已停止 updateBubbleTouchState(true) } // 用广播或全局接口传递状态,这里示例用广播 private fun updateBubbleTouchState(isTouchable: Boolean) { val intent = Intent("com.your.package.ACCESSIBILITY_STATE_CHANGE") intent.putExtra("is_touchable", isTouchable) sendBroadcast(intent) }
2. 气泡服务接收状态并更新参数
在BubbleService中注册广播接收器,动态修改窗口参数:
private val accessibilityStateReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { intent?.getBooleanExtra("is_touchable", true)?.let { updateBubbleTouchable(it) } } } override fun onCreate() { // ... 原有代码 ... // 注册广播接收器 registerReceiver(accessibilityStateReceiver, IntentFilter("com.your.package.ACCESSIBILITY_STATE_CHANGE")) } private fun updateBubbleTouchable(isTouchable: Boolean) { mBubbleParams?.let { params -> if (isTouchable) { // 恢复可触摸:移除NOT_TOUCHABLE标记 params.flags = params.flags and WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE.inv() // 恢复原有外部触摸监听标记 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { params.flags = params.flags or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH } } else { // 设置不可触摸:添加NOT_TOUCHABLE标记 params.flags = params.flags or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE // 移除外部触摸监听标记 params.flags = params.flags and WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH.inv() } // 更新窗口参数 mWindowManager?.updateViewLayout(mBubbleView, params) } } override fun onDestroy() { super.onDestroy() unregisterReceiver(accessibilityStateReceiver) }
推荐方案
优先选择方法一,实现简单且无需跨服务通信,直接通过autoClickService的非空判断就能精准控制触摸拦截逻辑,避免额外的广播或接口依赖。
内容的提问来源于stack exchange,提问作者dev
相关产品推荐
相关产品推荐

