UiAutomator中执行一个UI相关操作,比如click、swipe、drag等,都需要借助两个类,即GestureController、Gestures、PointerGesture。
Gestures翻译成中文就是手势。所谓手势,其实就是指用户手指或触摸笔在触摸屏上连续触碰行为,比如在屏幕上从左至右划出一个动作,就是手势。
PointerGesture就是代表,当操作一个手势时,其包含的一个点的动作。简单点理解就是,手势,比如滑动,它是连续的,可以看成是很多点连续组成的结果。而PointerGesture就代表手势执行过程中的一个点上的动作。即Gestures包含了很多PointerGesture。
一个Gestures的动作包括很多PointerGesture。所以,Gestures提供了构建PointerGesture的工厂方法。
我的理解就是android把UI操作抽象成了手势Gestures。所以Gestures中封装了点击、滑动等动作。
一个手指手势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));
- }
-
- @Override
- public 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);
- }
-
- @Override
- public Point interpolate(float fraction) {
- return new Point(start);
- }
- }
手指手势类PointerGesture类表示的是一个手指在屏幕上做出的手势。 PointerGesture内部维护了一个双向列表,保存了手指运动过程中所有的手指动作PointerAction。
1、PointerGesture对象中使用双端队列Deque
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
mActions = new ArrayDeque(); - 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仅仅是在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 inbetween
- return 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);
- }
执行给定的PointerGesture中双端队列Deque
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 pointers
- int count = 0;
- Map
pointers = new HashMap(); - for (PointerGesture g : gestures) {
- pointers.put(g, new Pointer(count++, g.start()));
- }
-
- // Initialize MotionEvent arrays
- List
properties = new ArrayList(); - List
coordinates = new ArrayList(); -
- // Track active and pending gestures
- PriorityQueue
active = new PriorityQueue(gestures.length, - END_TIME_COMPARATOR);
- PriorityQueue
pending = new PriorityQueue(gestures.length, - START_TIME_COMPARATOR);
- pending.addAll(Arrays.asList(gestures));
-
- // Record the start time
- long startTime = SystemClock.uptimeMillis();
-
- // Loop
- MotionEvent event;
- for (long elapsedTime = 0; !pending.isEmpty() || !active.isEmpty();
- elapsedTime = SystemClock.uptimeMillis() - startTime) {
-
- // Touchdown any new pointers
- while (!pending.isEmpty() && elapsedTime > pending.peek().delay()) {
- PointerGesture gesture = pending.remove();
- Pointer pointer = pointers.get(gesture);
-
- // Add the pointer to the MotionEvent arrays
- properties.add(pointer.prop);
- coordinates.add(pointer.coords);
-
- // Touch down
- int 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 list
- active.add(gesture);
- }
-
- // Touch up any completed pointers
- while (!active.isEmpty()
- && elapsedTime > active.peek().delay() + active.peek().duration()) {
-
- PointerGesture gesture = active.remove();
- Pointer pointer = pointers.get(gesture);
-
- // Update pointer positions
- pointer.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 pointers
- for (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真正执行动作的类
网上有篇文章写的也不错,值得参考下: