Android onAccessibilityEvent获取TextView的X和Y位置

Jay*_*yce 3 android accessibilityservice

我试图通过Accessibility服务在我的屏幕上获取TextView的X和Y位置来查找TextView,这可能吗?我发现的一切都需要您先触摸屏幕.以下是我获取节点信息的方法.

public class MyAccessibilityService extends AccessibilityService {

    @TargetApi(16)
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        AccessibilityNodeInfo source = event.getSource();
    }
}
Run Code Online (Sandbox Code Playgroud)

Chr*_*sCM 9

您可以随时从辅助功能服务中任意搜索辅助功能视图层次结构!虽然我建议在某种类型的辅助功能事件的上下文中这样做,然后确保有遍历的屏幕内容!在随机回调中这样做充其量是挑剔的.以下是用于此目的的合理的辅助功能配置XML文件:

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/accessibility_service_description"
    android:accessibilityEventTypes="typeWindowContentChanged|typeWindowsChanged|typeWindowStateChanged"
    android:accessibilityFlags="flagReportViewIds|flagIncludeNotImportantViews"
    android:canRetrieveWindowContent="true"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:notificationTimeout="1000" 
    android:settingsActivity=".SettingsActivity"
    />
Run Code Online (Sandbox Code Playgroud)

以下是对这几个领域的一些评论.

机器人:notificationTimeout = "1000"

仅每秒检索一次给定类型的辅助功能事件!对于列出的事件,任何较低的设置都将是QUITE verbose.我们只依靠它来调用我们的回调并确保我们有节点.为了这些目的,每秒一次就是DAND DANDY.根据需要调整.

机器人:accessibilityEventTypes = "typeWindowContentChanged | typeWindowsChanged | typeWindowStateChanged"

粗略地说,这是允许您捕获所有屏幕更改事件的事件子集.打开一个新窗口...扫描视图层次结构!

机器人:accessibilityFlags = "flagReportViewIds | flagIncludeNotImportantViews"

标记包括不重要的视图将包括AccessibilityNodeInfo heirarchy中的更多视图.特别是许多布局视图,并且Android OS通常不会认为是可访问性目的所必需的.我喜欢将此选中,因为这也是开发人员可调整属性,而Android开发人员在可访问性方面是出了名的愚蠢.最好只取一切并整理自己!

好的,所以有你的服务配置!现在,其余的很容易.您要做的是通过子节点子节点向下递归,直到找到TextView节点.我在下面设置了一个愚蠢的服务,它会在每个屏幕上找到第一个TextView节点更新(除非它们在上面提供服务配置XML时每秒多于一次),然后记录它的屏幕坐标.

class MyAccessibilityService extends AccessibilityService {

    @Override
    public void onAccessibilityEvent(AccessibilityEvent e) {

        //You can actually call getRootInActiveWindow() any time!
        //Doing so here ensures that the screen is in a reasonable state
        //and not in the middle of rendering or something else silly.
        final AccessibilityNodeInfo textNodeInfo = findTextViewNode(getRootInActiveWindow());

        if (textNodeInfo == null) return;

        Rect rect = new Rect();

        textNodeInfo.getBoundsInScreen(rect);

        Log.i(A11yService.class.getSimpleName(), "The TextView Node: " + rect.toString());

    }


    public AccessibilityNodeInfo findTextViewNode(AccessibilityNodeInfo nodeInfo) {

        //I highly recommend leaving this line in! You never know when the screen content will
        //invalidate a node you're about to work on, or when a parents child will suddenly be gone!
        //Not doing this safety check is very dangerous!
        if (nodeInfo == null) return null;

        Log.v(A11yService.class.getSimpleName(), nodeInfo.toString());

        //Notice that we're searching for the TextView's simple name!
        //This allows us to find AppCompat versions of TextView as well
        //as 3rd party devs well names subclasses... though with perhaps
        //a few poorly named unintended stragglers!
        if (nodeInfo.getClassName().toString().contains(TextView.class.getSimpleName())) {
            return nodeInfo;
        }

        //Do other work!

        for (int i = 0; i < nodeInfo.getChildCount(); i++) {
            AccessibilityNodeInfo result = findTextViewNode(nodeInfo.getChild(i));

            if (result != null) return result;
        }

        return null;
    }

    //Required for a valid Accessibility Service.
    @Override
    public void onInterrupt() {}
}
Run Code Online (Sandbox Code Playgroud)

您还可以找到我使用Accessibility Service Utilities构建的开源库,这使得所有这些内容变得更加容易!A11yNodeInfoMatcher类就是你想要的.