UiAutomator中执行一个UI相关操作,比如click、swipe、drag等,都需要借助两个类,即GestureController、Gestures、PointerGesture。

Gestures翻译成中文就是手势。所谓手势,其实就是指用户手指或触摸笔在触摸屏上连续触碰行为,比如在屏幕上从左至右划出一个动作,就是手势。

PointerGesture就是代表,当操作一个手势时,其包含的一个点的动作。简单点理解就是,手势,比如滑动,它是连续的,可以看成是很多点连续组成的结果。而PointerGesture就代表手势执行过程中的一个点上的动作。即Gestures包含了很多PointerGesture。

一个Gestures的动作包括很多PointerGesture。所以,Gestures提供了构建PointerGesture的工厂方法。

我的理解就是android把UI操作抽象成了手势Gestures。所以Gestures中封装了点击、滑动等动作。

一、PointerAction  表示手指手势中的某一个动作,是一个抽象接口

一个手指手势PointerGesture包括多个PointerAction动作,比如一个手指手势包括:点->滑动等。PointerAction就是指的这其中一个动作。这些动作组合起来就变成手指手势PointerGesture了。

包括手势起始点start、手势结束点end和持续时间duaratioin。并且还有一个插值接口函数interpolate()作用是:在持续时间duaration内,在起始点到结束点之间插入多个点Point。

/** A {@link PointerAction} represents part of a {@link PointerGesture}. */
private static abstract class PointerAction {final Point start;final Point end;final long duration;public PointerAction(Point startPoint, Point endPoint, long time) {start = startPoint;end = endPoint;duration = time;}public abstract Point interpolate(float fraction);
}

PointerAction共有两个实现类PointerLinearMoveAction和PointerPauseAction。

1、PointerLinearMoveAction会利用interpolate()来插入start~end区间内多个点Point,来模拟手势的move滑动行为

/*** A {@link PointerLinearMotionAction} moves the pointer between two points at a constant* speed.*/
private static class PointerLinearMoveAction extends PointerAction {public PointerLinearMoveAction(Point startPoint, Point endPoint, int speed) {super(startPoint, endPoint, (long)(1000 * calcDistance(startPoint, endPoint) / speed));}@Overridepublic Point interpolate(float fraction) {Point ret = new Point(start);ret.offset((int)(fraction * (end.x - start.x)), (int)(fraction * (end.y - start.y)));return ret;}private static double calcDistance(final Point a, final Point b) {return Math.sqrt((b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y));}
}

2、PointerPauseAction中的interpolate()插入的点和起始点start一样,从而可以模拟长按行为;

/** A {@link PointerPauseAction} holds the pointer steady for the given amount of time. */
private static class PointerPauseAction extends PointerAction {public PointerPauseAction(Point startPoint, long time) {super(startPoint, startPoint, time);}@Overridepublic Point interpolate(float fraction) {return new Point(start);}
}

二、PointerGesture  手指的手势  关键类

手指手势类PointerGesture类表示的是一个手指在屏幕上做出的手势。 PointerGesture内部维护了一个双向列表,保存了手指运动过程中所有的手指动作PointerAction

1、PointerGesture对象中使用双端队列Deque<PointerAction> mActions来存储组成手势的所有actions动作;

2、使用long类型的mDelay表示延迟;

3、使用long类型的mDuration表示持续时间

1、构造器

当只是click操作,则使用第一个构造器即可;当手势为滑动等包含多个点操作时,需要使用第二个构造器,mDelay延迟会影响滑动手势的PointerAction的插入点的数量。滑动距离相同的情况下,延迟越大,插值器插入的点越少。

可以看到PointerGesture的构造器其实是创建了一个PointerAction并保存到双向队列中第一个位置。代表手势的起始动作。

class PointerGesture {// The list of actions that make up this gesture.private Deque<PointerAction> mActions = new ArrayDeque<PointerAction>();private long mDelay;private long mDuration;/** Constructs a PointerGesture which touches down at the given start point. */public PointerGesture(Point startPoint) {this(startPoint, 0);}/*** Constructs a PointerGesture which touches down at the given start point after a given delay.* Used in multi-point gestures when the pointers do not all touch down at the same time.*/public PointerGesture(Point startPoint, long initialDelay) {if (initialDelay < 0) {throw new IllegalArgumentException("initialDelay cannot be negative");}mActions.addFirst(new PointerPauseAction(startPoint, 0));mDelay = initialDelay;}}

2、pause()  长按,代表的是持续双向队列中最后一个action一段时间

/** Adds an action which pauses for the specified amount of {@code time} in milliseconds. */
public PointerGesture pause(long time) {if (time < 0) {throw new IllegalArgumentException("time cannot be negative");}mActions.addLast(new PointerPauseAction(mActions.peekLast().end, time));mDuration += (mActions.peekLast().duration);return this;
}

3、move() 移动

/** Adds an action that moves the pointer to {@code dest} at {@code speed} pixels per second. */
public PointerGesture move(Point dest, int speed) {mActions.addLast(new PointerLinearMoveAction(mActions.peekLast().end, dest, speed));mDuration += (mActions.peekLast().duration);return this;
}

4、pointAt()获取手势中某个时间节点的点Point

这里会取出双向队列保存的所有action,然后计算出该action的point。不同的PointAction在这里调用了其插值器函数interpolate()来计算当前时间滑动到什么位置。

/** Returns the pointer location at {@code time} milliseconds into this gesture. */
public Point pointAt(long time) {if (time < 0) {throw new IllegalArgumentException("Time cannot be negative");}time -= mDelay;for (PointerAction action : mActions) {if (time < action.duration) {return action.interpolate((float)time / action.duration);}time -= action.duration;}return mActions.peekLast().end;
}

三、Gestures 手势

Gestures仅仅是在PointerGesture上层再封装一层。某些手势动作比如click,Gestures仅仅是返回一个PointerGesture, 有些动作比如多个手指同时操作捏合,则Gestures返回该动作的多个PointerGesture。

1、构造器

Gestures的构造器是私有的,无法直接创建。所以Gestures其实是单例。需要借助UiDevice对象来创建Gestures对象。构建Gestures需要ViewConfiguration,这个需要用到context,而context又需要借助UiDeivce。

// Keep a handle to the ViewConfiguration
private ViewConfiguration mViewConfig;// Private constructor.
private Gestures(ViewConfiguration config) {mViewConfig = config;
}/** Returns the {@link Gestures} instance for the given {@link Context}. */
public static Gestures getInstance(UiDevice device) {if (sInstance == null) {Context context = device.getInstrumentation().getContext();sInstance = new Gestures(ViewConfiguration.get(context));}return sInstance;
}

2、点击click

返回的是单个PointerGesture,持续时间不知道则为0。

/** Returns a {@link PointerGesture} representing a click at the given {@code point}. */
public PointerGesture click(Point point) {// A basic click is a touch down and touch up over the same point with no delay.return click(point, 0);
}/*** Returns a {@link PointerGesture} representing a click at the given {@code point} that lasts* for {@code duration} milliseconds.** @param point The point to click.* @param duration The duration of the click in milliseconds.* @return The {@link PointerGesture} representing this click.*/
public PointerGesture click(Point point, long duration) {// A click is a touch down and touch up over the same point with an optional delay inbetweenreturn new PointerGesture(point).pause(duration);
}

3、滑动

/*** Returns a {@link PointerGesture} representing a swipe.** @param start The touch down point for the swipe.* @param end The touch up point for the swipe.* @param speed The speed at which to move in pixels per second.* @return The {@link PointerGesture} representing this swipe.*/
public PointerGesture swipe(Point start, Point end, int speed) {// A swipe is a click that moves before releasing the pointer.return click(start).move(end, speed);
}

四、GestureController 手势控制

执行给定的PointerGesture中双端队列Deque<PointerAction> mActions保存的所有的手势动作行为。为了执行手势,该方法会记录手势中经过的每个点的位置,并向系统中插入MotionEvent事件,从而达到UI的效果。

public void performGesture(PointerGesture ... gestures){}

如果是单指操作,则gestures就是有当前手指的手势对象;

如果是多指操作,则gestures是多个手指的手势对象

函数内部逻辑大致为

1、使用一个map来存储每个手指的手势和手势的起始点Pointer对象;

2、初始化一个优先队列active,用来在循环中存储所有手指的PointerGesture,其排序按照谁先开始谁放前面的规则;

3、使用一个优先队列pending来存储存储所有手指的PointerGesture,其排序按照哪个手指的PointerGesture先结束谁放前面的规则;

4、使用elapsedTime来统计手势动作已经执行多次时间;

5、用一个while循环依次取出pending中所有的PointerGesture,封装成MotionEvent.ACTION_DOWN时间并插入到系统中,从而触发了每个手指的down点击事件;

6、再用一个while循环遍历优先队列active中每个PointerGesture,判断当前时间是否已经超过了PointerGesture的手势持续时间,如果超过了则封装一个MotionEvent.ACTION_UP事件,并插入到系统中,代表某个手指离开屏幕;

7、当某个手势还没有结束,则计算出滑动需要经过的所有点,封装出MotionEvent.ACTION_MOVE事件,开始滑动。

public void performGesture(PointerGesture ... gestures) {// Initialize pointersint count = 0;Map<PointerGesture, Pointer> pointers = new HashMap<PointerGesture, Pointer>();for (PointerGesture g : gestures) {pointers.put(g, new Pointer(count++, g.start()));}// Initialize MotionEvent arraysList<PointerProperties> properties = new ArrayList<PointerProperties>();List<PointerCoords>     coordinates = new ArrayList<PointerCoords>();// Track active and pending gesturesPriorityQueue<PointerGesture> active = new PriorityQueue<PointerGesture>(gestures.length,END_TIME_COMPARATOR);PriorityQueue<PointerGesture> pending = new PriorityQueue<PointerGesture>(gestures.length,START_TIME_COMPARATOR);pending.addAll(Arrays.asList(gestures));// Record the start timelong startTime = SystemClock.uptimeMillis();// LoopMotionEvent event;for (long elapsedTime = 0; !pending.isEmpty() || !active.isEmpty();elapsedTime = SystemClock.uptimeMillis() - startTime) {// Touchdown any new pointerswhile (!pending.isEmpty() && elapsedTime > pending.peek().delay()) {PointerGesture gesture = pending.remove();Pointer pointer = pointers.get(gesture);// Add the pointer to the MotionEvent arraysproperties.add(pointer.prop);coordinates.add(pointer.coords);// Touch downint action = MotionEvent.ACTION_DOWN;if (!active.isEmpty()) {// Use ACTION_POINTER_DOWN for secondary pointers. The index is stored at// ACTION_POINTER_INDEX_SHIFT.action = MotionEvent.ACTION_POINTER_DOWN+ ((properties.size() - 1) << MotionEvent.ACTION_POINTER_INDEX_SHIFT);}event = getMotionEvent(startTime, startTime + elapsedTime, action, properties,coordinates);getDevice().getUiAutomation().injectInputEvent(event, true);// Move the PointerGesture to the active listactive.add(gesture);}// Touch up any completed pointerswhile (!active.isEmpty()&& elapsedTime > active.peek().delay() + active.peek().duration()) {PointerGesture gesture = active.remove();Pointer pointer = pointers.get(gesture);// Update pointer positionspointer.updatePosition(gesture.end());for (PointerGesture current : active) {pointers.get(current).updatePosition(current.pointAt(elapsedTime));}int action = MotionEvent.ACTION_UP;int index = properties.indexOf(pointer.prop);if (!active.isEmpty()) {action = MotionEvent.ACTION_POINTER_UP+ (index << MotionEvent.ACTION_POINTER_INDEX_SHIFT);}event = getMotionEvent(startTime, startTime + elapsedTime, action, properties,coordinates);getDevice().getUiAutomation().injectInputEvent(event, true);properties.remove(index);coordinates.remove(index);}// Move any active pointersfor (PointerGesture gesture : active) {Pointer pointer = pointers.get(gesture);pointer.updatePosition(gesture.pointAt(elapsedTime - gesture.delay()));}if (!active.isEmpty()) {event = getMotionEvent(startTime, startTime + elapsedTime, MotionEvent.ACTION_MOVE,properties, coordinates);getDevice().getUiAutomation().injectInputEvent(event, true);}}
}

五、总结

PointerAction:代表 手势中的某个动作;

PointerGesture:代表 一个手指在屏幕上操作的完整手势;

Gestures: 在PointerGesture基础上封装了一层,可以表示多个手指的PointerGesture;

GestureController:根据Gestures真正执行动作的类

网上有篇文章写的也不错,值得参考下:

Android之UiAutomator测试框架源码分析(第29篇:UiObject2中的点击控件功能深度分析)_叫我王员外就行的博客-CSDN博客_uiautomator2 源码分析前言UI自动化的三个重要组成部分为查找控件、操作控件、预期结果,UiObject2是如何做到操作控件的?通过本篇源码分析,我们将知道插装测试的点击原理https://blog.csdn.net/cadi2011/article/details/106739947

UiAutomator常用类之UI手势动作相关推荐

  1. IOS UI Automation 学习之常用类,方法和模拟手势

    为什么80%的码农都做不了架构师?>>>    IOS UI Automation 学习之常用类,方法和模拟手势 常用类结构图 作者不擅长作画,如果有好的画此类图形的工具,可以留言, ...

  2. Android - xml动画,识别手势动作,代码抽取,获取手机SIM卡串号,获取联系人数据,开机广播,发送/解析短信,报警音乐

    转载请注明出处:https://blog.csdn.net/mythmayor/article/details/72878059 1.Activity的任务栈 1.类似一个木桶,每层只能放一个木块,我 ...

  3. 手机卫士03_手势动作_广播接收者应用

    手机卫士03_手势动作_广播接收者应用 //在控件上面按ctrl+1 可以抽取样式 1,设置向导界面的细节问题 1.1设置向导界面的跳转的时候要记得finsh(),把当前界面从任务栈中移除. 在设置向 ...

  4. 浅谈Java锁,与JUC的常用类,集合安全类,常用辅助类,读写锁,阻塞队列,线程池,ForkJoin,volatile,单例模式不安全,CAS,各种锁

    浅谈JUC的常用类 JUC就是java.util.concurrent-包下的类 回顾多线程 Java默认有几个线程? 2 个 mian.GC Java 真的可以开启线程吗? 开不了,点击源码得知:本 ...

  5. Foundation框架中常用类的介绍

    http://blog.csdn.net/mengtnt/article/details/6087536 Foundation框架的架构 cocoa程序编写主要用到2个框架Foundation和App ...

  6. 第二周:java异常和常用类 容器

    一.java异常类 1.定义 异常就是在运行时产生的问题.通常用Exception描述. 在java中,把异常封装成了一个类,当出现问题时,就会创建异常类对象并抛出异常相关的信息(如详细信息,名称以及 ...

  7. 「JavaSE」- 常用类

    常用类 Object类 Object是所有类的父类,任何类都默认继承Object.理论上Object类是所有类的父类,即直接或间接的继承java.lang.Object类.由于所有的类都继承在Obje ...

  8. JAVA基础03-Object类,常用类,基本的数据结构, Collection常用体系,泛型-泛型通配符

    1.object类 1.概述 java.lang.object类是java语言中的根类,即所有类的超类(基类)他描述的所有的方法子类都可以使用,在对象实例化的时候最终找到的类就是object 如果一个 ...

  9. OpenCV+Mediapipe手势动作捕捉与Unity引擎的结合

    OpenCV+Mediapipe手势动作捕捉与Unity引擎的结合 前言 Demo演示 认识Mediapipe 项目环境 手势动作捕捉部分 实时动作捕捉 核心代码 完整代码 Hands.py py代码 ...

最新文章

  1. sql数据库打包部署安装
  2. Java黑皮书课后题第5章:*5.31(金融应用:计算CD价值)假设你用10 000美元投资一张CD,年利率为5.75%。编写程序,提示由用户输入一个金额数、年获利率、月份数,然后显示一个表格
  3. python时间计算_python datetime库使用和时间加减计算
  4. ios页面间跳转方式总结
  5. java五子棋(可悔棋,人人+人机对弈)
  6. 解决在linux环境下面不显示验证码的问题
  7. ba控制系统的服务器,01-正文
  8. 新的实现上下文对话的方法
  9. 基于JAVA+SpringMVC+Mybatis+MYSQL的网络投票系统
  10. easypoi导出数值型_解决EasyPoi导出Excel金额数值类型
  11. 程序员必看!java开发金融类项目
  12. C++复习笔记3——类与对象(赋值重载、临时对象、const、static)
  13. 未受信任的企业级开发者_“未受信任的企业级开发者”是什么意思?怎么解决?...
  14. SSL P2133 腾讯大战360
  15. oracle调用web severs,PL/SQL调用WebService
  16. Qt -设计嵌入式设备用户界面的利器
  17. 微博短视频百万级高可用、高并发架构如何设计?
  18. ProcessOn -在线绘图
  19. java蚂蚁智力题,智力题大全_附答案
  20. 海康威视系统未连接服务器,ivms-4200客户端登入不了云服务器

热门文章

  1. 使用canvas绘制扇形图
  2. Word取消默认文档只读
  3. 【Android】——LinearLayout布局
  4. Excel更改引用方式在绝对引用与相对引用之间快速切换
  5. 博客园 首页 公告 自定义 标准 时钟/一般时钟 HONE HONE CLOCK时钟
  6. mysql中间件研究( Atlas,cobar,TDDL,mycat,heisenberg,Oceanus,vitess )
  7. 可以悬浮在屏幕的搜题软件_悬浮窗搜题神器app下载-悬浮窗自动识别搜题神器软件下载v1.0_86PS软件园...
  8. 强大的万年历微信小程序源码下载支持多做流量主模式
  9. ue4怎么用虚幻商城场景_ue4商城资源DownTown市中心场景
  10. IRF配合VRRP实战