安卓辅助功能(无障碍)AccessibilityService实战介绍
发布日期:2021-06-29 18:33:29
浏览次数:3
分类:技术文章
本文共 8759 字,大约阅读时间需要 29 分钟。
简要介绍
AccessibilityService是安卓平台上提供的无障碍服务,用于帮助残障人士使用手机,不过通过此功能可以完成很多事情.可以进行模拟界面操作,如点击界面上某个按钮等.
使用详细步骤
- 增加service定义 在onAccessibilityEvent函数中进行相应操作(安卓界面有变化时,都会调用此函数)
class MyAccessibilityService : AccessibilityService() { private val TAG = MyAccessibilityService::class.java.simpleName private var mContext: Context? = null override fun onCreate() { super.onCreate() Log.d(TAG, "onCreate") mContext = applicationContext AccessibilityOperator.getInstance().init(this) } override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { return Service.START_STICKY } override fun onAccessibilityEvent(event: AccessibilityEvent) { AccessibilityOperator.getInstance().updateEvent(event) val packageName = AccessibilityOperator.getInstance().rootNodeInfo?.packageName?.toString()// pasteToEditTextContent(packageName) var accessibilityService = AccessibilityOperator.getInstance() //按下返回键// accessibilityService.performGlobalAction(GLOBAL_ACTION_BACK) //向下拉出状态栏// accessibilityService.performGlobalAction(GLOBAL_ACTION_NOTIFICATIONS) //向下拉出状态栏并显示出所有的快捷操作按钮// accessibilityService.performGlobalAction(GLOBAL_ACTION_QUICK_SETTINGS) //按下HOME键// accessibilityService.performGlobalAction(GLOBAL_ACTION_HOME) //显示最近任务// accessibilityService.performGlobalAction(GLOBAL_ACTION_RECENTS) //长按电源键// accessibilityService.performGlobalAction(GLOBAL_ACTION_POWER_DIALOG) //分屏// accessibilityService.performGlobalAction(GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN) //锁屏(安卓9.0适用)// accessibilityService.performGlobalAction(GLOBAL_ACTION_LOCK_SCREEN) //截屏(安卓9.0适用)// accessibilityService.performGlobalAction(GLOBAL_ACTION_TAKE_SCREENSHOT) //打开快速设置 accessibilityService.performGlobalAction(GLOBAL_ACTION_QUICK_SETTINGS) } /** * 修改EditText输入框内容。 * 下面样例修改了QQ搜索输入框内容。 */ private fun changeEditTextContent(packageName: String?) { getNodeToOperate(packageName)?.let { val arguments = Bundle() arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, "被无障碍服务修改啦") it.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments) } } /** * 读取剪贴板内容,粘贴到EditText输入框。 * 下面样例修改了QQ搜索输入框内容。 */ private fun pasteToEditTextContent(packageName: String?) { getNodeToOperate(packageName)?.let { it.performAction(AccessibilityNodeInfo.FOCUS_INPUT) it.performAction(AccessibilityNodeInfo.ACTION_PASTE) it.recycle() } } private fun getNodeToOperate(packageName: String?): AccessibilityNodeInfo? { if (packageName != null && packageName == "com.tencent.mobileqq") { val nodes = AccessibilityOperator.getInstance().findNodesById("com.tencent.mobileqq:id/et_search_keyword") if (nodes != null && nodes.isNotEmpty()) { return nodes[0] } } return null } override fun onInterrupt() { }}
- 在Manifest文件中增加service定义
......
- AccessibilityOperator工具类
class AccessibilityOperator private constructor() { private var mAccessibilityEvent: AccessibilityEvent? = null private var accessibilityService: AccessibilityService? = null val rootNodeInfo: AccessibilityNodeInfo? get() { var nodeInfo: AccessibilityNodeInfo? = null accessibilityService?.let { nodeInfo = accessibilityService!!.rootInActiveWindow } if (nodeInfo == null && mAccessibilityEvent != null) { nodeInfo = mAccessibilityEvent!!.source } return nodeInfo } fun init(service: AccessibilityService) { accessibilityService = service } fun updateEvent(event: AccessibilityEvent) { mAccessibilityEvent = event } /** * 根据Text搜索所有符合条件的节点, 模糊搜索方式 */ fun findNodesByText(text: String): List? { val nodeInfo = rootNodeInfo return nodeInfo?.findAccessibilityNodeInfosByText(text) } /** * 根据View的ID搜索符合条件的节点,精确搜索方式; * 这个只适用于自己写的界面,因为ID可能重复 * * @param viewId */ fun findNodesById(viewId: String): List ? { val nodeInfo = rootNodeInfo return nodeInfo?.findAccessibilityNodeInfosByViewId(viewId) } fun clickByText(text: String): Boolean { return performClick(findNodesByText(text)) } fun clickParentByText(text: String, depth: Int): Boolean { return this.performClick(this.findParentNodesByText(text, depth)) } fun clickParentById(viewId: String, depth: Int): Boolean { return this.performClick(this.findParentNodesById(viewId, depth)) } fun findParentNodesByText(text: String, depth: Int): List { val rootNodeInfo = this.rootNodeInfo val resultNodeList = mutableListOf () if (rootNodeInfo != null) { val nodeList = findAccessibilityNodeInfosByText(rootNodeInfo, text) val iterator = nodeList.iterator() while (iterator.hasNext()) { val accessibilityNodeInfo = iterator.next() as AccessibilityNodeInfo resultNodeList.add(getParentNode(accessibilityNodeInfo, depth)) } } return resultNodeList } fun findParentNodesById(viewId: String, depth: Int): List { val rootNodeInfo = this.rootNodeInfo val resultNodeList = mutableListOf () if (rootNodeInfo != null) { val nodeList = rootNodeInfo.findAccessibilityNodeInfosByViewId(viewId) val iterator = nodeList.iterator() while (iterator.hasNext()) { val accessibilityNodeInfo = iterator.next() as AccessibilityNodeInfo resultNodeList.add(this.getParentNode(accessibilityNodeInfo, depth)) } } return resultNodeList } private fun findAccessibilityNodeInfosByText(node: AccessibilityNodeInfo?, text: String?): List { val resultNodeList = mutableListOf () if (node != null && text != null) { val nodeList = node.findAccessibilityNodeInfosByText(text) if (nodeList != null && !nodeList.isEmpty()) { val iterator = nodeList.iterator() while (iterator.hasNext()) { val nodeInList = iterator.next() as AccessibilityNodeInfo if (TextUtils.equals(nodeInList.text, text)) { resultNodeList.add(nodeInList) } } } return resultNodeList } else { return resultNodeList } } private fun getParentNode(nodeInfo: AccessibilityNodeInfo, depth: Int): AccessibilityNodeInfo { var resultNodeInfo = nodeInfo for (i in 0 until depth) { val parentNode = resultNodeInfo.parent resultNodeInfo = parentNode } return resultNodeInfo } /** * 根据View的ID搜索符合条件的节点,精确搜索方式; * 这个只适用于自己写的界面,因为ID可能重复 * * @param viewId * @return 是否点击成功 */ fun clickById(viewId: String): Boolean { return performClick(findNodesById(viewId)) } private fun performClick(nodeInfoList: List ?): Boolean { if (nodeInfoList != null && !nodeInfoList.isEmpty()) { var node: AccessibilityNodeInfo for (i in nodeInfoList.indices) { node = nodeInfoList[i] // 进行模拟点击 if (node.isEnabled) { return node.performAction(AccessibilityNodeInfo.ACTION_CLICK) } } } return false } fun clickBackKey(): Boolean { return performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK) } fun performGlobalAction(action: Int): Boolean { return accessibilityService!!.performGlobalAction(action) } private fun getNodeInfo(nodeInfo: AccessibilityNodeInfo?): String { var result = "" if (nodeInfo != null) { result = nodeInfo.className.toString() + ";text:" + nodeInfo.text + ";id:" + nodeInfo.viewIdResourceName + ";" } return result } fun clickTextParent(text: String): Boolean { val nodeInfo = rootNodeInfo return nodeInfo?.let { clickTextParent(it, text) } ?: false } private fun clickTextParent(rootInfo: AccessibilityNodeInfo?, text: String): Boolean { if (rootInfo != null && !TextUtils.isEmpty(rootInfo.className)) { if ("android.widget.TextView" == rootInfo.className.toString()) { if (!TextUtils.isEmpty(rootInfo.text) && rootInfo.text.toString().startsWith(text)) { val result = performClick(rootInfo.parent) Log.v(TAG, rootInfo.parent.className.toString() + ":result=" + result) return result } } for (i in 0 until rootInfo.childCount) { val result = clickTextParent(rootInfo.getChild(i), text) if (result) { return result } } return false } return false } private fun performClick(targetInfo: AccessibilityNodeInfo): Boolean { return targetInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK) } companion object { private val TAG = "AccessibilityOperator" val instance = AccessibilityOperator() }}
Demo源代码
安卓开发技术分享:
更多技术总结好文,请关注:「程序园中猿」转载地址:https://cxyxy.blog.csdn.net/article/details/88813268 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
第一次来,支持一个
[***.219.124.196]2024年05月02日 04时13分00秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
Java总结篇系列:Java多线程(一)
2019-04-30
产品设计开发要领
2019-04-30
Android线程操作类(暂停、重新开启、停止)
2019-04-30
android - JNI - 一维数组、二维数组的访问与使用
2019-04-30
在 Android Studio 2.2 中愉快地使用 C/C++
2019-04-30
C++和JNI的数据转换
2019-04-30
JNI 传递结构体参数
2019-04-30
JNI中枚举类型作为参数
2019-04-30
qlineedit tab焦点处无法输入问题
2019-04-30
android精确绘制文字位置的方法
2019-04-30
Android中UI线程与后台线程交互设计的5种方法
2019-04-30
[Android]调用字符串资源的几种方法
2019-04-30
Android更新UI的两种方法——handler与runOnUiThread()
2019-04-30
Java中new Thread的弊端及Java四种线程池的使用
2019-04-30
android线程与UI消息传递
2019-04-30
java枚举定义
2019-04-30
[翻译][Java]ExecutorService的正确关闭方法
2019-04-30
QT父子窗口事件传递与事件过滤器
2019-04-30
Qt到Cortex-A8的移植
2019-04-30
Qt4.7.3交叉编译移植
2019-04-30