安卓基础(无障碍)

news2025/4/18 9:37:30
配置无障碍服务

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

<?xml version="1.0" encoding="utf-8"?>
<!-- 这行代码告诉电脑,这个文件是一个 XML 文件,用的是 1.0 版本,里面的文字是用 UTF - 8 这种编码方式写的。就好像告诉别人这本书是用什么规则写的一样。 -->
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    <!-- 这里开始定义一个无障碍服务,xmlns:android 就像是一个魔法咒语,告诉电脑接下来用到的一些特殊的词是和安卓系统有关的。 -->
    android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
    <!-- 这行说的是,我们这个无障碍服务要去关注哪些事情。typeViewClicked 就是当屏幕上的东西被点击的时候,typeViewFocused 就是当屏幕上的东西被选中的时候。用 | 把它们连起来,意思是两个都要关注。 -->
    android:accessibilityFeedbackType="feedbackGeneric"
    <!-- 这行是说,当服务发现了上面说的那些事情后,要给出什么样的反馈。feedbackGeneric 就是一种通用的反馈方式。就好像你做对事情了,会得到一个通用的表扬一样。 -->
    android:accessibilityFlags="flagDefault"
    <!-- 这行设置了一些额外的小规则。flagDefault 就是用默认的那些规则。就像玩游戏用默认的游戏规则一样。 -->
    android:canPerformGestures="true"
    <!-- 这行表示这个服务能不能做出一些手势动作,比如点击、滑动等。设置成 true 就是可以做这些动作。就像你有一双可以做各种动作的小手一样。 -->
    android:canRetrieveWindowContent="true"
    <!-- 这行说的是这个服务能不能拿到屏幕上窗口里的内容。设置成 true 就是可以拿。就好像你可以看到窗户里面有什么东西一样。 -->
    android:description="@string/accessibility_service_description"
    <!-- 这行是给这个无障碍服务写一个简单的说明。@string/accessibility_service_description 是从别的地方拿过来的一段文字说明,就像给一个东西贴了一个标签,告诉别人这是什么。 -->
    android:notificationTimeout="100"
    <!-- 这行设置了一个时间,当有事情发生的时候,服务要在 100 毫秒内做出反应。就像你听到别人叫你,要在很短的时间内答应一样。 -->
    android:packageNames="com.example.targetapp" />
    <!-- 这行指定了这个无障碍服务只关注哪个应用程序。com.example.targetapp 就是那个应用的名字。就像你只关注某一个小朋友做的事情一样。 -->
在 AndroidManifest.xml 中注册服务
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <service
            android:name=".MyAccessibilityService"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
            <intent-filter>
                <action android:name="android.accessibilityservice.AccessibilityService" />
            </intent-filter>
            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/accessibility_service_config" />
        </service>

    </application>

</manifest>

开始基础

// @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() {
        // 无障碍服务被中断时调用
    }
}    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2336325.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

高性能内存kv数据库Redis(续)

目录 四.主从同步与对象模型 1.Redis 淘汰策略 2.Redis 如何做到 持久化 2.1 redis为什么要实现持久化 2.2fork进程的写时复制机制 2.3大Key的影响 2.4redis做持久化的方式 2.5 aof 2.6 rdb 2.7 redis 持久化方式的优缺点 3.redis里面的高可用体现在哪里&#xff1f; 3.1r…

【指纹浏览器系列-chromium编译】

本文提供了一步一步的指导来帮助读者在Windows环境下成功编译Chromium浏览器。涵盖了系统需求、开发环境搭建、代码下载及构建等关键步骤。 官方编译文档&#xff1a;https://github.com/chromium/chromium/blob/main/docs/windows_build_instructions.md 一、系统要求 一台…

Docker华为云创建私人镜像仓库

Docker华为云创建私人镜像仓库 在华为云官网的 产品 中搜索 容器镜像服务 &#xff1a; 或者在其他页面的搜索栏中搜索 容器镜像服务 &#xff1a; 进入到页面后&#xff0c;点击 创建组织 &#xff08;华为云的镜像仓库称为组织&#xff09;&#xff1a; 设置组织名字后&…

Ubuntu 22.04安装MySQL : Qwen2.5 模型对话数据收集与微调教程

在Ubuntu 22.04安装MySQL的教程请点击下方链接进行参考: 点击这里获取MySQL安装教程 今天将为大家带来如何微调Qwen2.5模型并连接数据库进行对话的教程。快跟着小编一起试试吧~ ​ 1 大模型 Qwen2.5 微调步骤 1.1 从 github 仓库 克隆项目 克隆存储库:#拉取代码 git clo…

关于 JDK 中的 jce.jar 的详解,以及与之功能类似的主流加解密工具的详细对比分析

以下是关于 JDK 中的 jce.jar 的详细解析&#xff0c;涵盖其作用、内容、历史背景及使用注意事项&#xff1a; 一、jce.jar 的核心作用 jce.jar 是 Java Cryptography Extension (JCE) 的核心实现库&#xff0c;提供 加密算法支持&#xff0c;包括&#xff1a; 对称加密&…

React 更新state中的对象

更新 state 中的对象 state 中可以保存任意类型的 JavaScript 值&#xff0c;包括对象。但是&#xff0c;你不应该直接修改存放在 React state 中的对象。相反&#xff0c;当你想要更新一个对象时&#xff0c;你需要创建一个新的对象&#xff08;或者将其拷贝一份&#xff09;…

算法思想之模拟

欢迎拜访&#xff1a;雾里看山-CSDN博客 本篇主题&#xff1a;算法思想之模拟 发布时间&#xff1a;2025.4.14 隶属专栏&#xff1a;算法 目录 算法介绍核心特点常见问题优化方向 例题替换所有的问号题目链接题目描述算法思路代码实现 提莫攻击题目链接题目描述算法思路代码实现…

测试基础笔记第四天(html)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 html介绍1. 介绍2.骨架标签3.常用标签标题标签段落标签超链接标签图片标签换行和空格标签布局标签input标签&#xff08;变形金刚&#xff09;form标签列表标签 htm…

WPF 中的元素继承层次结构 ,以下是对图中内容的详细说明:

顶层基类 DispatcherObject&#xff1a;处于继承体系最顶端&#xff0c;是一个抽象类。它为 WPF 元素提供了与 Dispatcher&#xff08;调度器&#xff09;交互的能力&#xff0c;Dispatcher 负责管理线程间的消息传递&#xff0c;确保 UI 操作在正确的线程&#xff08;通常是 …

十九、UDP编程和IO多路复用

1、UDP编程 服务端&#xff1a; #include<stdio.h> #include <arpa/inet.h> #include<stdlib.h> #include<string.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <pthread.h> #include &l…

DeepSeek使用001:Word中配置DeepSeek AI的V3和R1模型

文章目录 Word中配置DeepSeek大模型1、勾选开发工具2、信任中心设置3、添加DeepSeek-V3模型4、获取API KEY5、添加DeepSeek-R1模型6、新建组7、测试使用 Word中配置DeepSeek大模型 1、勾选开发工具 打开【选项】 选择【自定义功能区】 2、信任中心设置 打开【信任中心】&…

华为OD机试真题——攀登者2(2025A卷:200分)Java/python/JavaScript/C++/C语言/GO六种最佳实现

2025 A卷 200分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析&#xff1b; 并提供Java、python、JavaScript、C、C语言、GO六种语言的最佳实现方式&#xff01; 华为OD机试真题《攀登者2》&#xff1a; 目录 题目名称&#xff1a;攀登者2…

Windows卸载重装Docker

卸载 删除C:\Program Files\Docker &#xff0c;如果更改了路径的就找到相关位置进行删除 删除 C:\Users\<用户名>\.docker 清理注册表&#xff0c;不然重装会报错 Exising installation is up to date 按下WindowR唤起命令输入界面&#xff0c;输入regedit打开注…

双目视觉中矩阵等参数说明及矫正

以下是标定文件中各个参数的详细解释&#xff1a; 1. 图像尺寸 (imageSize) 参数值: [1280, 1024]含义: 相机的图像分辨率&#xff0c;宽度为1280像素&#xff0c;高度为1024像素。 2. 相机内参矩阵 (leftCameraMatrix / rightCameraMatrix) 结构: yaml data: [fx, 0, cx, 0,…

PyTorch核心函数详解:gather与where的实战指南

PyTorch中的torch.gather和torch.where是处理张量数据的关键工具&#xff0c;前者实现基于索引的灵活数据提取&#xff0c;后者完成条件筛选与动态生成。本文通过典型应用场景和代码演示&#xff0c;深入解析两者的工作原理及使用技巧&#xff0c;帮助开发者提升数据处理的灵活…

Go:接口

接口既约定 Go 语言中接口是抽象类型 &#xff0c;与具体类型不同 &#xff0c;不暴露数据布局、内部结构及基本操作 &#xff0c;仅提供一些方法 &#xff0c;拿到接口类型的值 &#xff0c;只能知道它能做什么 &#xff0c;即提供了哪些方法 。 func Fprintf(w io.Writer, …

ESP32+Arduino入门(三):连接WIFI获取当前时间

ESP32内置了WIFI模块连接WIFI非常简单方便。 代码如下&#xff1a; #include <WiFi.h>const char* ssid "WIFI名称"; const char* password "WIFI密码";void setup() {Serial.begin(115200);WiFi.begin(ssid,password);while(WiFi.status() ! WL…

CSS高度坍塌?如何解决?

一、什么是高度坍塌&#xff1f; 高度坍塌&#xff08;Collapsing Margins&#xff09;是指当父元素没有设置边框&#xff08;border&#xff09;、内边距&#xff08;padding&#xff09;、内容&#xff08;content&#xff09;或清除浮动时&#xff0c;其子元素的 margin 会…

【数据结构】之散列

一、定义与基本术语 &#xff08;一&#xff09;、定义 散列&#xff08;Hash&#xff09;是一种将键&#xff08;key&#xff09;通过散列函数映射到一个固定大小的数组中的技术&#xff0c;因为键值对的映射关系&#xff0c;散列表可以实现快速的插入、删除和查找操作。在这…

空地机器人在复杂动态环境下,如何高效自主导航?

随着空陆两栖机器人(AGR)在应急救援和城市巡检等领域的应用范围不断扩大&#xff0c;其在复杂动态环境中实现自主导航的挑战也日益凸显。对此香港大学王俊铭基于阿木实验室P600无人机平台自主搭建了一整套空地两栖机器人&#xff0c;使用Prometheus开源框架完成算法的仿真验证与…