You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android 15下Accessibility Service获取可见按钮BoundsInScreen超出屏幕高度的解决咨询

Android 15下Accessibility Service获取可见按钮BoundsInScreen超出屏幕高度的解决咨询

我理解你在Android 15设备上遇到的这个困扰——明明按钮在屏幕上可见,但通过getBoundsInScreen()拿到的坐标却超出了屏幕高度,而且Android 14及以下都正常,这确实很棘手。结合你提到的手势导航、系统对话框场景,我来分享几个可能的排查和解决方向:


1. 先确认屏幕尺寸的正确获取方式

你当前用getCurrentWindowMetrics()拿到的是当前应用窗口的尺寸,但当系统对话框弹出时,对话框是一个独立的顶层窗口,这时候应用窗口的尺寸可能不是整个屏幕的真实尺寸。你需要获取物理屏幕的完整尺寸,而不是应用窗口的:

// Android 11+ 获取物理屏幕的真实尺寸
WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
// 方式1:使用getMaximumWindowMetrics获取屏幕最大可用范围
WindowMetrics maxMetrics = windowManager.getMaximumWindowMetrics();
Rect realScreenBounds = maxMetrics.getBounds();
int realScreenHeight = realScreenBounds.height();

// 方式2:兼容旧API的真实尺寸获取(Android 17+弃用,但可用于对比)
Display display = windowManager.getDefaultDisplay();
DisplayMetrics realDisplayMetrics = new DisplayMetrics();
display.getRealMetrics(realDisplayMetrics);
int realScreenHeight2 = realDisplayMetrics.heightPixels;

对比这两个值和你之前拿到的screenHeight,就能知道是否是屏幕尺寸获取的问题。

2. 针对系统对话框,通过AccessibilityWindowInfo获取窗口上下文

系统对话框属于独立的窗口,你可以通过AccessibilityService的getWindows()方法拿到窗口列表,定位到对话框窗口后,再转换节点坐标:

List<AccessibilityWindowInfo> windowList = getWindows();
for (AccessibilityWindowInfo window : windowList) {
    // 筛选系统对话框窗口
    if (window.isActive() && window.getType() == AccessibilityWindowInfo.TYPE_SYSTEM_DIALOG) {
        Rect windowScreenBounds = new Rect();
        window.getBoundsInScreen(windowScreenBounds);
        
        // 获取按钮节点的屏幕坐标
        Rect nodeScreenBounds = new Rect();
        aniChild.getBoundsInScreen(nodeScreenBounds);
        
        // 转换为相对于对话框窗口的坐标
        Rect nodeWindowBounds = new Rect(
            nodeScreenBounds.left - windowScreenBounds.left,
            nodeScreenBounds.top - windowScreenBounds.top,
            nodeScreenBounds.right - windowScreenBounds.left,
            nodeScreenBounds.bottom - windowScreenBounds.top
        );
        
        // 此时nodeWindowBounds的范围应该在对话框窗口的0到windowScreenBounds.height()之间
        Log.d("Node in dialog", "Relative bounds: " + nodeWindowBounds.toShortString());
    }
}

这个方法可以帮你排除应用窗口和对话框窗口的坐标混淆问题。

3. 尝试切换使用getBoundsInWindow()替代getBoundsInScreen()

getBoundsInScreen()是相对于整个物理屏幕的坐标,而getBoundsInWindow()是相对于节点所在窗口的内部坐标。在系统对话框场景下,窗口本身可能已经是全屏可用区域,所以这个坐标可能更接近你需要的真实可见位置:

Rect nodeWindowBounds = new Rect();
aniChild.getBoundsInWindow(nodeWindowBounds);
Log.d("Node window bounds", nodeWindowBounds.toShortString());

如果这个值在屏幕高度范围内,那直接用这个就可以了;如果需要转换为屏幕坐标,再结合窗口的getBoundsInScreen()结果进行偏移计算。

4. 重新检查Insets的处理逻辑

你之前用了getInsetsIgnoringVisibility(WindowInsets.Type.systemBars()),但在手势导航场景下,应该关注系统手势区域的Insets,而不是系统栏:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
    WindowMetrics windowMetrics = windowManager.getCurrentWindowMetrics();
    Insets gestureInsets = windowMetrics.getWindowInsets()
        .getInsets(WindowInsets.Type.systemGestures());
    // 可用高度 = 窗口高度 - 底部手势区域高度
    int usableHeight = windowMetrics.getBounds().height() - gestureInsets.bottom;
    Log.d("Usable height with gestures", String.valueOf(usableHeight));
}

另外,Android 15可能对Insets的计算逻辑有调整,建议同时打印出gestureInsetssystemBarsInsets的各个方向值,对比后找到坐标偏移的来源。

5. 验证坐标偏移规律

既然你发现按钮是可见的,那getBoundsInScreen()返回的坐标和真实可见坐标之间应该有一个固定的差值。你可以在测试中打印出以下值,找到这个偏移量:

  • 物理屏幕真实高度
  • 按钮的getBoundsInScreen().bottom
  • 按钮的可见位置(比如手动观察的底部距离屏幕底部的像素值)
    通过对比这几个值,就能算出需要减去的偏移量,临时解决问题的同时,也能定位到根源。

内容来源于stack exchange

火山引擎 最新活动