参考资料:https://github.com/PopFisher/AccessibilitySample

1.Android文档里AccessibilityService简介

Android辅助功能(无障碍)使用---AccessibilityService_第1张图片

辅助功能只在帮助残障人士使用Android设备和app的时候使用。
服务进程被杀掉后,下次启动,需再次申请权限

2.实现辅助功能服务

实现辅助功能,需要实现AccessibilityService类,并重写onAccessibilityEventonInterrupt方法。

代码:

public class AccessibilityTestService extends AccessibilityService {    @Override    public void onAccessibilityEvent(AccessibilityEvent event) {        String packageName = event.getPackageName().toString();        int eventType = event.getEventType();        Log.i("accessibility", "packageName = " + packageName + " eventType = " + eventType);    }    @Override    public void onInterrupt() {    }}

开启服务权限后运行,不同页面,打印不同的packageNameeventType
结果:

packageName = com.android.mms eventType = 2048packageName = com.miui.home eventType = 32packageName = com.miui.home eventType = 1packageName = com.android.camera eventType = 2048packageName = com.android.camera eventType = 32packageName = com.android.camera eventType = 2048packageName = com.android.camera eventType = 2048packageName = com.miui.home eventType = 32packageName = com.miui.securitycenter eventType = 64packageName = com.android.systemui eventType = 2048

3.注册服务

AndroidManifest中注册服务完成配置。
代码:

<service    android:name=".android.accessibility.AccessibilityTestService"    android:exported="true"    android:label="@string/testAccessibility"    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_config" />service>

label标签是在申请权限的设置页面显示该服务的名字。其中需要的permission是必须的,缺少任何一个系统个都会无视这个服务。是该服务的一些配置,在xml路径下。
 

配置文件代码:

<?xml version="1.0" encoding="utf-8"?><accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"    android:accessibilityEventTypes="typeAllMask"    android:accessibilityFeedbackType="feedbackGeneric"    android:accessibilityFlags="flagReportViewIds"    android:canRetrieveWindowContent="true"    android:description="@string/accessibility_desc"    android:notificationTimeout="100" />

其中accessibilityFlags是一些额外声明,如果代码重要获取View的ID(通过调用AccessibilityNodeInfogetViewIdResourceName()方法),就必须加上权限flagReportViewIds

4.跳转设置页面开启服务

先判断是否开启服务

if (!OpenAccessibilitySettingHelper.isAccessibilitySettingsOn(this,        AccessibilityTestService.class.getName())) {// 判断服务是否开启    OpenAccessibilitySettingHelper.jumpToSettingPage(this);// 跳转到开启页面} else {    ToastUtil.showShortToast(this, "服务已开启");    //do other things...}

OpenAccessibilitySettingHelper类:

public class OpenAccessibilitySettingHelper {    //跳转到设置页面无障碍服务开启自定义辅助功能服务    public static void jumpToSettingPage(Context context) {        Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);        context.startActivity(intent);    }    //判断自定义辅助功能服务是否开启    public static boolean isAccessibilitySettingsOn(Context context, String className) {        if (context == null) {            return false;        }        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);        if (activityManager != null) {            List<ActivityManager.RunningServiceInfo> runningServices =                    activityManager.getRunningServices(100);// 获取正在运行的服务列表            if (runningServices.size() < 0) {                return false;            }            for (int i = 0; i < runningServices.size(); i++) {                ComponentName service = runningServices.get(i).service;                if (service.getClassName().equals(className)) {                    return true;                }            }            return false;        } else {            return false;        }    }}

5.辅助功能具体实现类

模拟的一些点击,退出,拖动,获取界面信息等具体的实现方法,主要是通过节点查找,找到要操作的控件。
首先要在实现的AccessibilityTestService中重写的onAccessibilityEvent方法中和event绑定。

AccessibilityOperator.getInstance().updateEvent(this, event);

updateEvent方法

public void updateEvent(AccessibilityService service, AccessibilityEvent event) {    if (service != null && mAccessibilityService == null) {        mAccessibilityService = service;    }    if (event != null) {        mAccessibilityEvent = event;    }}

 
根据text搜索所有符合的节点

public List<AccessibilityNodeInfo> findNodesByText(String text) {    AccessibilityNodeInfo nodeInfo = getRootNodeInfo();    if (nodeInfo != null) {        Log.i("accessibility", "getClassName:" + nodeInfo.getClassName());        Log.i("accessibility", "getText:" + nodeInfo.getText());        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {            //需要在xml文件中声明权限android:accessibilityFlags="flagReportViewIds"            // 并且版本大于4.3 才能获取到view 的 ID            Log.i("accessibility", "getClassName:" + nodeInfo.getViewIdResourceName());        }        return nodeInfo.findAccessibilityNodeInfosByText(text);    }    return null;}

根据ID搜索所有符合的节点

public List<AccessibilityNodeInfo> findNodesById(String viewId) {    AccessibilityNodeInfo nodeInfo = getRootNodeInfo();    if (nodeInfo != null) {        if (Build.VERSION.SDK_INT >= 18) {            return nodeInfo.findAccessibilityNodeInfosByViewId(viewId);        }    }    return null;}

获取跟节点

private AccessibilityNodeInfo getRootNodeInfo() {    AccessibilityEvent curEvent = mAccessibilityEvent;    AccessibilityNodeInfo nodeInfo = null;    if (Build.VERSION.SDK_INT >= 16) {        if (mAccessibilityService != null) {            nodeInfo = mAccessibilityService.getRootInActiveWindow();        }    } else {        nodeInfo = curEvent.getSource();    }    return nodeInfo;}

 
模拟点击

private boolean performClick(List<AccessibilityNodeInfo> nodeInfos) {    if (nodeInfos != null && !nodeInfos.isEmpty()) {        AccessibilityNodeInfo node;        for (int i = 0; i < nodeInfos.size(); i++) {            node = nodeInfos.get(i);            // 获得点击View的类型            Log.i("accessibility", "getClassName:" + node.getClassName());            // 进行模拟点击            if (node.isEnabled()) {                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {                    return node.performAction(AccessibilityNodeInfo.ACTION_CLICK);                }            }        }    }    return false;}

模拟退出键

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)public boolean clickBackKey() {    return mAccessibilityService.performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);}

需要调用的地方直接获取AccessibilityOperator实例,获取到需要操作的节点后调用模拟方法。

6.通过Android Device Monitor来直接获取界面中View的ID

如图所示,打开Android Device Monitor
Android辅助功能(无障碍)使用---AccessibilityService_第2张图片
可能会有报错信息,因为AndroidStudio自动默认选的是64位的环境,如果出错的话,可直接在该运行程序的目录手动开启
Android辅助功能(无障碍)使用---AccessibilityService_第3张图片
默认打开的是64位的,可手动打开第一个。
然后选中连接的模拟器,点击方框的按钮后,可以将手机当前页面的布局框架加载进来。
Android辅助功能(无障碍)使用---AccessibilityService_第4张图片

点击布局上需要获取resourceid的view 后,会出现该view的id,但是有些view没有id,代码里获取的也是null,例如GooglePlay里某个应用的安装按钮就没有resourceid但是他的父布局是有resourceid的,可以利用获取父布局的节点,再获取该父布局的某个子节点。

Android辅助功能(无障碍)使用---AccessibilityService_第5张图片

获取某个节点的某个子节点代码:

private AccessibilityNodeInfo getChildNodeInfos(String id, int childIndex) {    List<AccessibilityNodeInfo> listChatRecord = findNodesById(id);    if (listChatRecord == null || listChatRecord.size() == 0) {        return null;    }    AccessibilityNodeInfo parentNode = listChatRecord.get(0);//该节点    int count = parentNode.getChildCount();    Log.i("accessibility", "子节点个数 " + count);    return childIndex < count ? parentNode.getChild(childIndex) : null;}

更多相关文章

  1. 在本地UI使用webview,在html页面用js与android通信方法。
  2. Android 源代码在线查看(转)
  3. Android中获取网页表单中的数据实现思路及代码
  4. android安装包apk文件反编译代码
  5. GDB在线调试Android Framework Native C/C++代码
  6. android 发送短信,彩信,邮件代码
  7. 关机重启代码
  8. Android 重启应用代码
  9. 蓝牙原理Android代码实现

随机推荐

  1. Android清理后台所有历史App任务
  2. Android拍照、录像、录音代码范例
  3. Android倒计时实现
  4. 2013.11.19 ——— android 获取本地ip地
  5. Android中的onTouch事件
  6. Android 强制停止RecyclerView滑动方法
  7. NDK各版本下载
  8. Android RecyclerView 去掉滑动边缘阴影
  9. Android应用自启动
  10. Android 框架类图