You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 边缘悬浮气泡/图标)

通过获取遮挡窗口的尺寸、位置信息,结合自定义阈值即可区分:

  1. 获取遮挡窗口参数:
    • 若使用WindowInfoListener,可直接从WindowInfo对象中获取窗口的getBounds()、getType()等信息;
    • 若使用AccessibilityService,可通过AccessibilityEvent.getSource().getBoundsInScreen()获取窗口边界。
  2. 判断逻辑:
    • 悬浮气泡/图标:窗口宽高均远小于屏幕尺寸(比如低于屏幕宽高的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 06:33:15