前言
最近研究无障碍服务AccessibilityService ,操作点击微信文章
AccessibilityService 本身的使用还是很简单的,但是我遇到了 大问题。
无法点击消息里面的链接
原因分析
通常我们获得界面元素AccessibilityNodeInfo
之后,调用performAction()
模拟单击事件。
AccessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
问题出在:
单纯的单击TextView本身并不能触发链接的跳转,需要单击在View的内容之上才可以。所以需要模拟点击位置才行,而不是点击View。
但是AccessibilityService 只能获得View的信息,并且只能对View进行操作。我需要找到一种点击屏幕位置的方法。
思路
对于模拟点击屏幕位置,经过我搜寻大概有两种方案。
1.使用adb
Android自动化之adb模拟操作(可实现按键精灵和手机输入法)
虽然可以满足点击屏幕任意位置的需求,但是需要一台电脑辅助。显然不适用我这种一个手机的需求。
2.使用测试框架
Android的Ui测试框架同样可以点击屏幕位置。当然UI测试框架有好几种,比如谷歌提供的 UiAutomator
,Espresso
等等。都可以点击和控制手机。
但是这样只能当成测试用例跑,并不能直接做成App。当然可以使用暴力的方法,比如使用命令行强行调用测试用例的方法。但这样无法满足设计效果,而且需要装3个App。不可取
解决
在我绞尽脑汁搜索,终于找到一篇使用AccessibilityService 可以点击任何位置的文章
Android7.0 AccessibilityService新增gesturedescription使用详解
没错就是使用AccessibilityService的dispatchGesture方法执行一个手势操作。
首先这个方法是7.0之后加入的所有最小版本改为24
执行的手势类为GestureDescription
,实例化·GestureDescription`需要一段path路径。
学过自定义View的我们对path太熟悉不过了,所以我们可以使用dispatchGesture方法在屏幕上划出一个path,当然若path只是一个点的话,那就是单击事件。
所以整体的思路如下:
1.拿到节点元素
2.获得元素位置信息getBoundsInScreen
3.点击元素中心(默认)
根据ID获得节点
public static AccessibilityNodeInfo getElementById(AccessibilityEvent event, String id, String packagename, int index) {
if (event.getPackageName().equals(packagename) && event.getSource() != null) {
List<AccessibilityNodeInfo> list = event.getSource().findAccessibilityNodeInfosByViewId(id);
if (null != list) {
for (AccessibilityNodeInfo info : list) {
if (info != null && list.indexOf(info) == index) {
return info;
}
}
}
}
return null;
}
使用GestureDescription点击AccessibilityNodeInfo
public static void useGestureClick(AccessibilityNodeInfo info, AccessibilityService accessibilityService) {
if (info == null) {
return;
}
Rect rect = new Rect();
info.getBoundsInScreen(rect);
GestureDescription.Builder builder = new GestureDescription.Builder();
Path path = new Path();
path.moveTo(rect.centerX(), rect.centerY());
GestureDescription gestureDescription = builder
.addStroke(new GestureDescription.StrokeDescription(path, 100, 50))
.build();
accessibilityService.dispatchGesture(gestureDescription, new AccessibilityService.GestureResultCallback() {
@Override
public void onCompleted(GestureDescription gestureDescription) {
super.onCompleted(gestureDescription);
}
}, null);
}
两个方法合在一起使用如下,需要点击任意id元素,直接调用idGesturePlay即可。
public static boolean idGesturePlay(AccessibilityEvent event, AccessibilityService accessibilityService, String id, String packagename, int index) {
AccessibilityNodeInfo info = getElementById(event, id, packagename, index);
if (info != null) {
useGestureClick(info, accessibilityService);
return true;
}
return false;
}
同样这个点击方法可以解决某控件clickable为false的情况,因为这个点击事件和控件无关。
最后也是最重要的,配置xml配置手势支持
加入配置
android:canPerformGestures="true"
总结
本次问题的解决花了一天的功夫,几乎放弃。主要是市面关于AccessibilityService 的文章都很老,没有介绍到这个手势方法。这提醒我们,要密切注意版本的变化。一定的文档和搭梯子能力是必须的。
网友评论