Android无交互时检测Activity部分遮挡及区分遮挡类型方法问询
Android无交互时检测窗口遮挡及区分遮挡类型方案
一、无交互时检测窗口遮挡状态的方法
1. 监听式方案(推荐)
(1)WindowInsets 监听(API 30+)
利用系统提供的WindowInsets API,无需用户交互即可监听窗口被其他窗口覆盖的状态变化:
getWindow().getDecorView().setOnApplyWindowInsetsListener((view, insets) -> { // 判断是否存在其他窗口遮挡当前应用 boolean isObscured = insets.isVisible(WindowInsets.Type.windows()); // 此处处理遮挡状态变更逻辑 return insets.consumeSystemWindowInsets(); });
(2)WindowManager 窗口信息监听(API 30+)
通过WindowManager的窗口信息监听器,实时捕获系统中窗口的添加、移除和变化事件,以此判断当前应用是否被遮挡:
WindowManager windowManager = getSystemService(WindowManager.class); WindowInfoListener windowInfoListener = new WindowInfoListener() { @Override public void onWindowAdded(@NonNull WindowInfo windowInfo) { checkAppObscuration(windowInfo); } @Override public void onWindowRemoved(@NonNull WindowInfo windowInfo) { checkAppObscuration(windowInfo); } @Override public void onWindowChanged(@NonNull WindowInfo windowInfo) { checkAppObscuration(windowInfo); } }; // 注册监听器,指定线程执行回调 windowManager.addWindowInfoListener(windowInfoListener, Executors.newSingleThreadExecutor());
checkAppObscuration方法中,可对比当前应用窗口的边界与目标窗口边界,判断是否存在遮挡。
(3)辅助服务(AccessibilityService)
通过辅助服务监听系统窗口状态变化,适用于全版本Android系统,但需要用户手动开启辅助功能权限:
public class ObscureDetectService extends AccessibilityService { @Override public void onAccessibilityEvent(AccessibilityEvent event) { if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { // 获取窗口边界,判断是否遮挡当前应用 Rect windowBounds = event.getSource() != null ? event.getSource().getBoundsInScreen() : new Rect(); checkObscureWithBounds(windowBounds); } } @Override public void onInterrupt() {} }
需在Manifest中声明服务并配置对应的meta-data。
2. 轮询式方案
若无法使用监听API,可定期主动检测窗口可见区域,判断是否被遮挡:
private void startObscurePolling() { new Handler(Looper.getMainLooper()).postDelayed(() -> { Rect visibleFrame = new Rect(); getWindow().getDecorView().getWindowVisibleDisplayFrame(visibleFrame); DisplayMetrics metrics = getResources().getDisplayMetrics(); int totalArea = metrics.widthPixels * metrics.heightPixels; int visibleArea = visibleFrame.width() * visibleFrame.height(); // 自定义阈值,比如可见区域占比低于95%则判定为被遮挡 boolean isObscured = (float) visibleArea / totalArea < 0.95f; // 处理遮挡状态 startObscurePolling(); // 循环轮询,间隔可自定义(比如1秒) }, 1000); }
二、区分遮挡物类型(覆盖式窗口 vs 边缘悬浮气泡/图标)
通过获取遮挡窗口的尺寸、位置信息,结合自定义阈值即可区分:
- 获取遮挡窗口参数:
- 若使用WindowInfoListener,可直接从
WindowInfo对象中获取窗口的getBounds()、getType()等信息; - 若使用AccessibilityService,可通过
AccessibilityEvent.getSource().getBoundsInScreen()获取窗口边界。
- 若使用WindowInfoListener,可直接从
- 判断逻辑:
- 悬浮气泡/图标:窗口宽高均远小于屏幕尺寸(比如低于屏幕宽高的1/5),且位置靠近屏幕边缘(比如距离屏幕边缘的距离小于自身宽高的一半);
- 覆盖式窗口:窗口尺寸较大,覆盖当前应用的可见区域超过一定比例(比如20%以上),或窗口类型为普通应用窗口(
TYPE_APPLICATION)。
示例判断逻辑:
private boolean isFloatingBubble(Rect windowBounds, DisplayMetrics metrics) { int screenWidth = metrics.widthPixels; int screenHeight = metrics.heightPixels; int windowWidth = windowBounds.width(); int windowHeight = windowBounds.height(); // 判断尺寸是否为小型窗口 boolean isSmallSize = windowWidth < screenWidth / 5 && windowHeight < screenHeight / 5; // 判断是否位于屏幕边缘 boolean isAtEdge = windowBounds.left < windowWidth / 2 || windowBounds.right > screenWidth - windowWidth / 2 || windowBounds.top < windowHeight / 2 || windowBounds.bottom > screenHeight - windowHeight / 2; return isSmallSize && isAtEdge; }
内容的提问来源于stack exchange,提问作者thomasa88
相关产品推荐
相关产品推荐

