自动化 - 坐标操作

2022年10月22日

自动化 - 坐标操作

Stability: 2 - Stable

本章节介绍了一些使用坐标进行点击、滑动的函数。这些函数有的需要安卓7.0以上,有的需要root权限。

要获取要点击的位置的坐标,可以在开发者选项中开启"指针位置"。

基于坐标的脚本通常会有分辨率的问题,这时可以通过setScreenMetrics()函数来进行自动坐标放缩。这个函数会影响本章节的所有点击、长按、滑动等函数。通过设定脚本设计时的分辨率,使得脚本在其他分辨率下自动放缩坐标。

控件和坐标也可以相互结合。一些控件是无法点击的(clickable为false), 无法通过.click()函数来点击,这时如果安卓版本在7.0以上或者有root权限,就可以通过以下方式来点击:

//获取这个控件
var widget = id("xxx").findOne();
//获取其中心位置并点击
click(widget.bounds().centerX(), widget.bounds().centerY());
//如果用root权限则用Tap

setScreenMetrics(width, height)

  • width {number} 屏幕宽度,单位像素
  • height {number} 屏幕高度,单位像素

设置脚本坐标点击所适合的屏幕宽高。如果脚本运行时,屏幕宽度不一致会自动放缩坐标。

例如在1080*1920的设备中,某个操作的代码为

setScreenMetrics(1080, 1920);
click(800, 200);
longClick(300, 500);

那么在其他设备上云控会自动放缩坐标以便脚本仍然有效。例如在540 * 960的屏幕中click(800, 200)实际上会点击位置(400, 100)。

安卓7.0以上的触摸和手势模拟

Stability: 2 - Stable

注意以下命令只有Android7.0及以上才有效

click(x, y)

  • x {number} 要点击的坐标的x值
  • y {number} 要点击的坐标的y值

模拟点击坐标(x, y),并返回是否点击成功。只有在点击执行完成后脚本才继续执行。

一般而言,只有点击过程(大约150毫秒)中被其他事件中断(例如用户自行点击)才会点击失败。

使用该函数模拟连续点击时可能有点击速度过慢的问题,这时可以用press()函数代替。

longClick(x, y)

  • x {number} 要长按的坐标的x值
  • y {number} 要长按的坐标的y值

模拟长按坐标(x, y), 并返回是否成功。只有在长按执行完成(大约600毫秒)时脚本才会继续执行。

一般而言,只有长按过程中被其他事件中断(例如用户自行点击)才会长按失败。

press(x, y, duration)

  • x {number} 要按住的坐标的x值
  • y {number} 要按住的坐标的y值
  • duration {number} 按住时长,单位毫秒

模拟按住坐标(x, y), 并返回是否成功。只有按住操作执行完成时脚本才会继续执行。

如果按住时间过短,那么会被系统认为是点击;如果时长超过500毫秒,则认为是长按。

一般而言,只有按住过程中被其他事件中断才会操作失败。

一个连点器的例子如下:

//循环100次
for(var i = 0; i < 100; i++){
  //点击位置(500, 1000), 每次用时1毫秒
  press(500, 1000, 1);
}

swipe(x1, y1, x2, y2, duration)

  • x1 {number} 滑动的起始坐标的x值
  • y1 {number} 滑动的起始坐标的y值
  • x2 {number} 滑动的结束坐标的x值
  • y2 {number} 滑动的结束坐标的y值
  • duration {number} 滑动时长,单位毫秒

模拟从坐标(x1, y1)滑动到坐标(x2, y2),并返回是否成功。只有滑动操作执行完成时脚本才会继续执行。

一般而言,只有滑动过程中被其他事件中断才会滑动失败。

gesture(duration, [x1, y1], [x2, y2], ...)

  • duration {number} 手势的时长
  • [x, y] {...} 手势滑动路径的一系列坐标

模拟手势操作。例如gesture(1000, [0, 0], [500, 500], [500, 1000])为模拟一个从(0, 0)到(500, 500)到(500, 100)的手势操作,时长为2秒。

gestures([delay1, duration1, [x1, y1], [x2, y2], ...], [delay2, duration2, [x3, y3], [x4, y4], ...], ...)

同时模拟多个手势。每个手势的参数为[delay, duration, 坐标], delay为延迟多久(毫秒)才执行该手势;duration为手势执行时长;坐标为手势经过的点的坐标。其中delay参数可以省略,默认为0。

例如手指捏合:

gestures([0, 500, [800, 300], [500, 1000]],
         [0, 500, [300, 1500], [500, 1000]]);

使用root权限点击和滑动的简单命令

Stability: 1 - Experimental

注意:本章节的函数在后续版本很可能有改动!请勿过分依赖本章节函数的副作用。推荐使用RootAutomator代替本章节的触摸函数。

以下函数均需要root权限,可以实现任意位置的点击、滑动等。

  • 这些函数通常首字母大写以表示其特殊的权限。
  • 这些函数均不返回任何值。
  • 并且,这些函数的执行是异步的、非阻塞的,在不同机型上所用的时间不同。脚本不会等待动作执行完成才继续执行。因此最好在每个函数之后加上适当的sleep来达到期望的效果。

例如:

Tap(100, 100);
sleep(500);

注意,动作的执行可能无法被停止,例如:

for(var i = 0; i < 100; i++){
  Tap(100, 100);
}

这段代码执行后可能会出现在任务管理中停止脚本后点击仍然继续的情况。 因此,强烈建议在每个动作后加上延时:

for(var i = 0; i < 100; i++){
  Tap(100, 100);
  sleep(500);
}

Tap(x, y)

  • x {number} 要点击的x坐标
  • y {number} 要点击的y坐标

点击位置(x, y), 您可以通过"开发者选项"开启指针位置来确定点击坐标。

Swipe(x1, y1, x2, y2, [duration])

  • x1 {number} 滑动起点的x坐标
  • y1 {number} 滑动起点的y坐标
  • x2 {number} 滑动终点的x坐标
  • y2 {number} 滑动终点的y坐标
  • duration {number} 滑动动作所用的时间

滑动。从(x1, y1)位置滑动到(x2, y2)位置。

RootAutomator

Stability: 2 - Stable

RootAutomator是一个使用root权限来模拟触摸的对象,用它可以完成触摸与多点触摸,并且这些动作的执行没有延迟。

一个脚本中最好只存在一个RootAutomator,并且保证脚本结束退出他。可以在exit事件中退出RootAutomator,例如:

var ra = new RootAutomator();
events.on('exit', function(){
  ra.exit();
});
//执行一些点击操作
...

注意

RootAutomator需要root权限或adb权限才能执行,要使用adb权限可以用shell.setDefaultOptions({adb: true})默认使用adb权限,需要shizuku授权。

另外RootAutomoat兼容性不佳,从9.3版本开始,推荐使用RootAutomator2代替。

new RootAutomator([options])

  • options {object} 可选参数,包括:
    • adb {boolean} 是否使用adb权限,默认为false。若为true时,需要用shizuku授权才能使用。
    • inputDevice {string} 指定RootAutomator操作的设备,比如/dev/input/event4。不指定则自动检测。

构造一个RootAutomator。

RootAutomator.tap(x, y[, id])

  • x {number} 横坐标
  • y {number} 纵坐标
  • id {number} 多点触摸id,可选,默认为1,可以通过setDefaultId指定。

点击位置(x, y)。其中id是一个整数值,用于区分多点触摸,不同的id表示不同的"手指",例如:

var ra = new RootAutomator();
//让"手指1"点击位置(100, 100)
ra.tap(100, 100, 1);
//让"手指2"点击位置(200, 200);
ra.tap(200, 200, 2);
ra.exit();

如果不需要多点触摸,则不需要id这个参数。 多点触摸通常用于手势或游戏操作,例如模拟双指捏合、双指上滑等。

某些情况下可能存在tap点击无反应的情况,这时可以用RootAutomator.press()函数代替。

RootAutomator.swipe(x1, x2, y1, y2[, duration, id])

  • x1 {number} 滑动起点横坐标
  • y1 {number} 滑动起点纵坐标
  • x2 {number} 滑动终点横坐标
  • y2 {number} 滑动终点纵坐标
  • duration {number} 滑动时长,单位毫秒,默认值为300
  • id {number} 多点触摸id,可选,默认为1

模拟一次从(x1, y1)到(x2, y2)的时间为duration毫秒的滑动。

RootAutomator.press(x, y, duration[, id])

  • x {number} 横坐标
  • y {number} 纵坐标
  • duration {number} 按下时长
  • id {number} 多点触摸id,可选,默认为1

模拟按下位置(x, y),时长为duration毫秒。

RootAutomator.longPress(x, y[, id])

  • x {number} 横坐标
  • y {number} 纵坐标
  • duration {number} 按下时长
  • id {number} 多点触摸id,可选,默认为1

模拟长按位置(x, y)。

以上为简单模拟触摸操作的函数。如果要模拟一些复杂的手势,需要更底层的函数。

RootAutomator.touchDown(x, y[, id])

  • x {number} 横坐标
  • y {number} 纵坐标
  • id {number} 多点触摸id,可选,默认为1

模拟手指按下位置(x, y)。

RootAutomator.touchMove(x, y[, id])

  • x {number} 横坐标
  • y {number} 纵坐标
  • id {number} 多点触摸id,可选,默认为1

模拟移动手指到位置(x, y)。

RootAutomator.touchUp([id])

  • id {number} 多点触摸id,可选,默认为1

模拟手指弹起。

RootAutomator2

RootAutomator2用于基于root或者adb权限,模拟点击、手势、长按等操作。相比起基于无障碍的操作,RootAutomator支持多点触控和动态改变手势;相比RootAutomator,RootAutomator2的兼容性更佳。

let screenWidth = $device.width;
let screenHeight = $device.height;

// 使用root权限执行。也可以指定为{adb: true}使用adb权限,需要shizuku授权
const ra = new RootAutomator2({ root: true });

// 点击(200, 200)的位置
ra.tap(200, 200);
sleep(1000);

// 按住屏幕中点持续500毫秒
ra.press(screenWidth / 2, screenHeight / 2, 500);
sleep(1000);

// 从(500, 200)滑动到(500, 1000),滑动时长300毫秒
ra.swipe(500, 200, 500, 1000, 300);
sleep(1000);


// 双指捏合
// 左上角位置
let p0 = {
    x: screenWidth / 6,
    y: screenHeight / 6,
};
// 右下角位置
let p1 = {
    x: screenWidth - p0.x,
    y: screenHeight - p0.y,
}
// 同时按下左上角和右下角,手指id为0和1
ra.touchDown([
    { x: p0.x, y: p0.y, id: 0 },
    { x: p1.x, y: p1.y, id: 1 },
]);

// 移动步数
const steps = 20;
// 计算每一步移动的偏移量
const stepX = Math.round((p1.x - p0.x) / steps) / 2;
const stepY = Math.round((p1.y - p0.y) / steps) / 2;
for (let i = 0; i < steps; i++) {
    // 手指0向右下移动,手指1向左上移动
    ra.touchMove([
        { x: p0.x + stepX * i, y: p0.y + stepY * i, id: 0 },
        { x: p1.x - stepX * i, y: p1.y - stepY * i, id: 1 }
    ]);
}
// 弹起所有手指
ra.touchUp();
// 等待前面的操作全部完成
ra.flush();

// 退出RootAutomator,如果没有正确退出,可能导致"手指"残留在屏幕上
ra.exit();

new RootAutomator2([options])

  • options 创建RootAutomator2的选项,可选。
    • adb {boolean} 是否使用adb权限,默认为`false
    • root {boolean} 是否使用root权限,当不指定adb权限时,默认为true

根据选项创建一个新的RootAutomator2实例。RootAutomator2相比RootAutomator有更好的兼容性。

可以指定是否使用root权限、adb权限等,参见ShellOptions。如果不指定root或adb权限,则默认使用root权限。

RootAutomator2.tap(x, y)

点击位置(x, y),时长为5毫秒。此函数会等待操作同步完成,可能函数执行时间大于实际操作时间,若对执行时间有要求,可以用touchDown, touchUp等异步方法代替。

参数

名称 类型
x number
y number

RootAutomator2.longPress(x, y)

长按(x, y)位置。长按的时长为ViewConfiguration.getLongPressTimeout()open in new window+100毫秒。此函数会等待操作同步完成,可能函数执行时间大于实际操作时间,若对执行时间有要求,可以用touchDown, touchUp等异步方法代替。

参数

名称 类型
x number
y number

RootAutomator2.press(x, y, duration)

按下(x, y)位置持续duration时长,然后抬起手指。此函数会等待操作同步完成,可能函数执行时间大于实际操作时间,若对执行时间有要求,可以用touchDown, touchUp等异步方法代替。

参数

名称 类型 描述
x number -
y number -
duration number 按下时长,单位毫秒

RootAutomator2.swipe(x1, y1, x2, y2, duration)

在给定的duration时长从(x1, y1)位置滑动到(x2, y2)位置。此函数会等待操作同步完成,可能函数执行时间大于实际操作时间,若对执行时间有要求,可以用touchDown, touchUp等异步方法代替。

参数

名称 类型 描述
x1 number -
y1 number -
x2 number -
y2 number -
duration number 滑动时长,单位毫秒

RootAutomator2.touchDown(x, y, [id])

按下(x, y)位置。若对应id的手指之前已经是按下状态,则会模拟手指移动(touchMove)事件。此操作是异步进行的,若要等待操作完成,可以使用flush方法。

参数

名称 类型 描述
x number -
y number -
id number 手指ID,默认为0

RootAutomator2.touchDown(pointers)

模拟一个手指按下事件,使用数组描述触摸的位置和相应的手指id。例如ra.touchDown([{x: 100, y: 100, id: 0}, {x: 200, y: 200, id: 1}])会使用手指0按下位置(100, 100),使用手指1按下位置(200, 200)。此操作是异步进行的,若要等待操作完成,可以使用flush方法。

参数

名称 类型 描述
pointers Array 描述每个手指位置的数组,数组的每个元素带有x, yid三个字段

RootAutomator2.touchMove(x, y, [id])

将手指移动到(x, y)位置。若对应id的手指之前并非按下状态,则会模拟手指按下(touchDown)事件。此操作是异步进行的,若要等待操作完成,可以使用flush方法。

参数

名称 类型 描述
x number -
y number -
id number 手指ID,默认为0

RootAutomator2.touchMove(pointers)

模拟一个手指移动事件,使用数组描述触摸的位置和相应的手指id。例如ra.touchMove([{x: 100, y: 100, id: 0}, {x: 200, y: 200, id: 1}])会将手指0移动到位置(100, 100),将手指1移动到位置(200, 200)。此操作是异步进行的,若要等待操作完成,可以使用flush方法。

参数

名称 类型 描述
pointers Array 描述每个手指位置的数组,数组的每个元素带有x, yid三个字段

RootAutomator2.touchUp([id])

抬起手指。

参数

名称 类型 描述
id number 手指ID,若不指定则抬起所有手指

RootAutomator2.touchUp(pointers)

模拟一个手指抬起事件,使用数组描述触摸的位置和相应的手指id。例如ra.touchUp([{x: 100, y: 100, id: 0}, {x: 200, y: 200, id: 1}])会使用手指0和手指1抬起,其中的坐标位置为手指抬起时的位置。此操作是异步进行的,若要等待操作完成,可以使用flush方法。

参数

名称 类型 描述
pointers Array 描述每个手指位置的数组,数组的每个元素带有x, yid三个字段

RootAutomator2.flush()

等待所有操作完成。例如我们使用tocuhDown, touchMove, touchUp完成了一系列手势,需要等待这些手势完成后继续下一步时,使用ra.flush()来等待这些操作完成。

RootAutomator2.exit([forced])

退出RootAutomator2。

参数

名称 类型 描述
forced boolean 可选。如果为true,将不等待未完成的操作,而是尽可能快地退出;如果为false,在会所有未完成的操作结束后退出进程。
上次编辑于: 2022/11/20 17:30:01
贡献者: Bruce