安卓基础(无障碍)

配置无障碍服务

在 res/xml 目录下创建一个 accessibility_service_config.xml 文件,用于配置无障碍服务的相关信息,例如要监听的事件类型、反馈类型等。




    android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
    
    android:accessibilityFeedbackType="feedbackGeneric"
    
    android:accessibilityFlags="flagDefault"
    
    android:canPerformGestures="true"
    
    android:canRetrieveWindowContent="true"
    
    android:description="@string/accessibility_service_description"
    
    android:notificationTimeout="100"
    
    android:packageNames="com.example.targetapp" />
    
在 AndroidManifest.xml 中注册服务


    

        
            
                
            
            
        

    

开始基础

// @Override 是一个特殊的标记,它告诉计算机,下面这个方法是要重写父类里的方法。就好像你要按照一个固定的模板来画画一样。
@Override
// 这是一个方法,名字叫 onAccessibilityEvent,当系统里有和无障碍相关的事情发生时,就会调用这个方法。event 就像是一个小信使,它带来了发生的事情的信息。
public void onAccessibilityEvent(AccessibilityEvent event) {
    // 从这个小信使(event)那里拿到事件发生的源头,也就是哪个东西上面发生了这个事件。就像知道是哪个小朋友做了某件事情一样。把这个源头存到 source 这个小盒子里。
    AccessibilityNodeInfo source = event.getSource();
    // 检查 source 这个小盒子里是不是有东西。如果没有东西,那就说明没有找到事件的源头,就不用再往下做了。
    if (source != null) {
        // 这是一个注释,告诉我们下面要做的事情是查找一个写着“点击我”的东西。
        // 查找要点击的视图
        // 在这个事件源头里,找一找有没有写着“点击我”的东西。把找到的结果存到 targetNode 这个小盒子里。
        AccessibilityNodeInfo targetNode = source.findAccessibilityNodeInfosByText("点击我");
        // 检查 targetNode 这个小盒子里是不是有东西,并且找到的东西数量要大于 0。如果满足条件,说明找到了写着“点击我”的东西。
        if (targetNode != null && targetNode.size() > 0) {
            // 从找到的那些写着“点击我”的东西里,拿出第一个。把它存到 node 这个小盒子里。
            AccessibilityNodeInfo node = targetNode.get(0);
            // 检查这个拿出来的东西是不是可以被点击。就像检查一个按钮是不是能按一样。
            if (node.isClickable()) {
                // 如果这个东西可以被点击,那就让计算机模拟点击这个动作。就像你用手指去按按钮一样。
                node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
            }
        }
    }
}
  • 未找到匹配元素:要是在 source 对应的界面范围内,没有任何元素的文本内容是 “点击我”,那么 targetNodeList 会是一个空列表(即列表长度为 0)。
  • 找到匹配元素:若找到了包含文本 “点击我” 的元素,targetNodeList 就会包含这些元素对应的 AccessibilityNodeInfo 对象。每个 AccessibilityNodeInfo 对象都代表一个界面元素,并且包含了该元素的各种属性和信息,像元素的位置、大小、是否可点击等。
打印 node 的结果

当你尝试打印 node 时,通常会调用 node.toString() 方法。打印的内容会包含该界面元素的一些基本信息,示例如下:

AccessibilityNodeInfo[
  packageName: com.example.app, 
  className: android.widget.Button, 
  text: 点击我, 
  contentDescription: null, 
  boundsInParent: Rect(0, 0 - 100, 50), 
  boundsInScreen: Rect(100, 200 - 200, 250), 
  childCount: 0, 
  enabled: true, 
  focusable: true, 
  focused: false, 
  clickable: true, 
  longClickable: false, 
  checked: false, 
  selected: false, 
  actions: [ACTION_CLICK]
]

找到输入框

// 引入安卓系统里和无障碍服务相关的工具包,这样我们就能使用里面的功能啦,就像打开一个装满工具的盒子。
import android.accessibilityservice.AccessibilityService;
// 引入安卓系统里和无障碍事件相关的工具包,方便我们处理各种无障碍事件,就像拿了一个专门处理事情的小本子。
import android.view.accessibility.AccessibilityEvent;
// 引入安卓系统里和无障碍节点信息相关的工具包,能让我们了解界面上各种元素的信息,就像有了一个查看元素信息的望远镜。
import android.view.accessibility.AccessibilityNodeInfo;

// 定义一个公共的类,名字叫 MyAccessibilityService,这个类继承自 AccessibilityService,就像盖房子用了一个特定的房子框架。
public class MyAccessibilityService extends AccessibilityService {

    // 重写父类里的 onAccessibilityEvent 方法,当系统里有和无障碍相关的事件发生时,就会调用这个方法。
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        // 从事件里拿到事件发生的源头,也就是哪个界面元素上发生了这个事件,把它存到 source 这个小盒子里。
        AccessibilityNodeInfo source = event.getSource();
        // 检查 source 这个小盒子里是不是有东西,如果有东西,说明找到了事件源头。
        if (source != null) {
            // 这是一个注释,告诉我们下面要做的事情是查找一个提示文本为“请输入内容”的输入框。
            // 查找输入框,假设输入框的提示文本为“请输入内容”
            // 调用 findNodeByHintText 方法,在 source 里找提示文本是“请输入内容”的输入框,把找到的结果存到 inputNode 这个小盒子里。
            AccessibilityNodeInfo inputNode = findNodeByHintText(source, "请输入内容");
            // 检查 inputNode 这个小盒子里是不是有东西,如果有,说明找到了输入框。
            if (inputNode != null) {
                // 这是一个注释,告诉我们下面要做的事情是创建一个用来装要输入文本的包裹。
                // 创建一个包含要输入文本的 Bundle
                // 创建一个 Bundle 对象,它就像一个包裹,用来装我们要输入的文本。
                android.os.Bundle arguments = new android.os.Bundle();
                // 把要输入的文本“要输入的文本”放到这个包裹里,并且给它取个名字,名字是系统规定的一个特殊名字。
                arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, "要输入的文本");
                // 这是一个注释,告诉我们下面要做的事情是执行设置文本的操作。
                // 执行设置文本的操作
                // 让 inputNode 这个输入框执行设置文本的操作,把包裹里的文本设置进去。
                inputNode.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
            }
        }
    }

    // 这是一个注释,告诉我们下面这个方法的作用是根据提示文本查找节点。
    // 根据提示文本查找节点
    // 定义一个私有的方法,名字叫 findNodeByHintText,这个方法接收两个参数,一个是根节点,一个是提示文本。
    private AccessibilityNodeInfo findNodeByHintText(AccessibilityNodeInfo root, String hintText) {
        // 检查根节点是不是为空,如果为空,说明没有东西可以找,就直接返回空。
        if (root == null) {
            return null;
        }
        // 检查根节点的提示文本是不是和我们要找的提示文本一样,如果一样,就把根节点返回。
        if (hintText.equals(root.getHintText())) {
            return root;
        }
        // 拿到根节点的子节点数量,就像知道一个大家庭里有几个小朋友。
        int childCount = root.getChildCount();
        // 用一个循环,一个一个地检查根节点的子节点。
        for (int i = 0; i < childCount; i++) {
            // 从根节点里拿出第 i 个子节点,存到 child 这个小盒子里。
            AccessibilityNodeInfo child = root.getChild(i);
            // 递归调用 findNodeByHintText 方法,在子节点里继续找符合提示文本的节点,把结果存到 result 这个小盒子里。
            AccessibilityNodeInfo result = findNodeByHintText(child, hintText);
            // 检查 result 这个小盒子里是不是有东西,如果有,说明找到了符合条件的节点,就把它返回。
            if (result != null) {
                return result;
            }
        }
        // 如果上面都没有找到符合条件的节点,就返回空。
        return null;
    }

    // 重写父类里的 onInterrupt 方法,当无障碍服务被中断时,就会调用这个方法。
    @Override
    public void onInterrupt() {
        // 这是一个注释,告诉我们这个方法是在无障碍服务被中断时调用。
        // 无障碍服务被中断时调用
    }
}    

获取输入框文本内容

import android.accessibilityservice.AccessibilityService;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;

public class MyAccessibilityService extends AccessibilityService {

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        AccessibilityNodeInfo source = event.getSource();
        if (source != null) {
            // 查找输入框,假设输入框的提示文本为“请输入内容”
            AccessibilityNodeInfo inputNode = findNodeByHintText(source, "请输入内容");
            if (inputNode != null) {
                // 检测输入框是否已经有内容
                CharSequence inputText = inputNode.getText();
                if (inputText != null && inputText.length() > 0) {
                    // 输入框已经有内容
                    System.out.println("输入框已经有内容:" + inputText);
                } else {
                    // 输入框没有内容,执行输入操作
                    // 创建一个包含要输入文本的 Bundle
                    android.os.Bundle arguments = new android.os.Bundle();
                    arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, "要输入的文本");
                    // 执行设置文本的操作
                    inputNode.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
                }
            }
        }
    }

    // 根据提示文本查找节点
    private AccessibilityNodeInfo findNodeByHintText(AccessibilityNodeInfo root, String hintText) {
        if (root == null) {
            return null;
        }
        if (hintText.equals(root.getHintText())) {
            return root;
        }
        int childCount = root.getChildCount();
        for (int i = 0; i < childCount; i++) {
            AccessibilityNodeInfo child = root.getChild(i);
            AccessibilityNodeInfo result = findNodeByHintText(child, hintText);
            if (result != null) {
                return result;
            }
        }
        return null;
    }

    @Override
    public void onInterrupt() {
        // 无障碍服务被中断时调用
    }
}    

你可能感兴趣的:(android)