【问题标题】:How can I use AccessibilityService.getWindows() to obtain a traversable AccessibilityNodeInfo?如何使用 AccessibilityService.getWindows() 获取可遍历的 AccessibilityNodeInfo?
【发布时间】:2015-04-25 01:33:19
【问题描述】:

我正在为 Android 编写一个 AccessibilityService,直到 API 级别 20,我一直在使用 AccessibilityEvent.getSource() 方法在触发 onAccessibilityEvent(AccessibilityEvent event) 时获取可遍历的 AccessibilityNodeInfo。虽然生成的AccessibilityNodeInfo 并不总是反映屏幕的内容,但仍然可以使用它。

从 API 级别 21 开始,新的 AccessibilityService.getWindows() 应该不仅能够更好地表示视图层次结构(即,尊重 Z 顺序),而且还应该能够公开一个节点包括当前输入法 (IME) 中的所有视图。我想利用这一点,但我无法这样做,我不知道我到底做错了什么。顺便说一句,除了极少的 java 文档之外,我找不到任何有关如何执行此操作的更详细信息。

我已经做了以下事情:

  • 配置服务以检索窗口内容 (android:canRetrieveWindowContent="true")
  • 在服务标志中添加了flagRetrieveInteractiveWindows

我的代码如下:

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
            ArrayList<AccessibilityNodeInfo> nodes = getNodesFromWindows();
                switch (event_type) {
                case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED:
                case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
                case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
                //case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
                case AccessibilityEvent.TYPE_VIEW_FOCUSED:
                case AccessibilityEvent.TYPE_VIEW_SELECTED:
                case AccessibilityEvent.TYPE_VIEW_SCROLLED:
                //case AccessibilityEvent.TYPE_VIEW_CLICKED:
                    updateTargetLeafs(nodes);
                }
}

getNodesFromWindows() 在哪里执行以下操作:

private ArrayList<AccessibilityNodeInfo> getNodesFromWindows() {
    List<AccessibilityWindowInfo> windows = getWindows();
    ArrayList<AccessibilityNodeInfo> nodes =
            new ArrayList<AccessibilityNodeInfo>();
    if (windows.size() > 0) {
        for (AccessibilityWindowInfo window : windows) {
            nodes.add(window.getRoot());
        }
    }
    return nodes;
}

在此之后,updateTargetLeafs() 将所有可点击、启用和可见的节点收集到一个单独的 AccessibilityNodeInfo ArrayList 中,以便我可以随意索引和访问它们(见下文)。在 API 级别 20 及以下使用 AccessibilityEvent.getSource() 时,此数组的大小始终接近屏幕上的视图数量,但当我使用 AccessibilityService.getWindows() 时,大小几乎始终为 1(有时为 0),并且边界列表中唯一的AccessibilityNodeInfo 总是在屏幕之外。

编辑:添加用于遍历所有子节点的代码(其中mNodesgetNodesFromWindows() 的输出):

        ...
        ArrayList<AccessibilityNodeInfo> theseleafs =
                    new ArrayList<AccessibilityNodeInfo>();
        AccessibilityNodeInfo thisnode;
        Queue<AccessibilityNodeInfo> q =
                new LinkedList<AccessibilityNodeInfo>();
        for (AccessibilityNodeInfo n : mNodes) {
            q.add(n);
        }
        while (!q.isEmpty()) {
            thisnode = q.poll();
            if (shouldIncludeNode(thisnode)) {
                //Add only if it fulfills all requirements!
                theseleafs.add(thisnode);
            }
            for (i=0; i<thisnode.getChildCount(); ++i) {
                AccessibilityNodeInfo n = thisnode.getChild(i);
                if (n != null) q.add(n); // Add only if not null!
            }
        };
        LogD("MyTag", theseleafs.size() + " leafs in this node!");
        ...

奇怪,我知道,但我做错了什么?

【问题讨论】:

    标签: java android accessibility android-5.0-lollipop accessibilityservice


    【解决方案1】:

    您可以使用getChild() 方法访问 Windows 内容。 在您的onAccessibilityEvent(AccessibilityEvent event) 中,您可以执行以下操作。

    @Override
        public void onAccessibilityEvent(AccessibilityEvent event) {
              AccessibilityNodeInfo mSource = event.getSource();
              int child = mSource.getChildCount();
              // iterate through all child of parent view
              for (int i=0; i<child; i++){
                AccessibilityNodeInfo childNodeView = mParent.getChild(i);
                // Do something with this window content
              }
    }
    

    【讨论】:

    • 我有一个疑问。我们得到所有子节点没关系,但如何获得所有子节点的来源?这可能吗?通过事件我们可以获得由某个事件触发的节点的来源。对于子节点,我们如何获取源? @vsvankahede
    • 首先通过getParent()获取源节点的父节点。然后通过 getChildCount() 找到父母的孩子的数量。使用 for 循环并遍历所有子节点并在那里存储对 ArrayList 的引用。现在你可以用这个可访问性节点做任何你想做的事
    • 是的,但是如何获取子节点的来源?
    • 子节点的“来源”是什么意思?在AccessibilityService 中,您会收到AccessibilityEvents,当您在一个事件上调用getSource() 时,您会得到一个AccessibilityNodeInfo 对象。所以,节点就是源,就是事件的源头。
    【解决方案2】:

    getNodesFromWindows() 仅返回根节点列表。您需要遍历其子节点以收集窗口中的所有节点。

    【讨论】:

    • 是的,这正是 updateTargetLeafs() 所做的。我已更新问题以显示这一点。
    猜你喜欢
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多