【Auto.JS】Autojs官方提取文档使用说明函数 (2)

接上一篇文章:Autojs官方提取文档使用说明函数 (1)

 

 

Images

Stability: 2 - Stable

images模块提供了一些手机设备中常见的图片处理函数,包括截图、读写图片、图片剪裁、旋转、二值化、找色找图等。

该模块分为两个部分,找图找色部分和图片处理部分。

需要注意的是,image对象创建后尽量在不使用时进行回收,同时避免循环创建大量图片。因为图片是一种占用内存比较大的资源,尽管Auto.js通过各种方式(比如图片缓存机制、垃圾回收时回收图片、脚本结束时回收所有图片)尽量降低图片资源的泄漏和内存占用,但是糟糕的代码仍然可以占用大量内存。

Image对象通过调用recycle()函数来回收。例如:

// 读取图片

var img = images.read("./1.png");

//对图片进行操作

...

// 回收图片

img.recycle();

例外的是,caputerScreen()返回的图片不需要回收。

图片处理

images.read(path)

  • path {string} 图片路径

读取在路径path的图片文件并返回一个Image对象。如果文件不存在或者文件无法解码则返回null。

images.load(url)

  • url {string} 图片URL地址

加载在地址URL的网络图片并返回一个Image对象。如果地址不存在或者图片无法解码则返回null。

images.copy(img)

  • img {Image} 图片
  • 返回 {Image}

复制一张图片并返回新的副本。该函数会完全复制img对象的数据。

images.save(image, path[, format = "png", quality = 100])

  • image {Image} 图片
  • path {string} 路径
  • format {string} 图片格式,可选的值为:
    • png
    • jpeg/jpg
    • webp
  • quality {number} 图片质量,为0~100的整数值

把图片image以PNG格式保存到path中。如果文件不存在会被创建;文件存在会被覆盖。

//把图片压缩为原来的一半质量并保存

var img = images.read("/sdcard/1.png");

images.save(img, "/sdcard/1.jpg", "jpg", 50);

app.viewFile("/sdcard/1.jpg");

images.fromBase64(base64)

  • base64 {string} 图片的Base64数据
  • 返回 {Image}

解码Base64数据并返回解码后的图片Image对象。如果base64无法解码则返回null。

images.toBase64(img[, format = "png", quality = 100])

  • image {image} 图片
  • format {string} 图片格式,可选的值为:
    • png
    • jpeg/jpg
    • webp
  • quality {number} 图片质量,为0~100的整数值
  • 返回 {string}

把图片编码为base64数据并返回。

images.fromBytes(bytes)

  • bytes {byte[]} 字节数组

解码字节数组bytes并返回解码后的图片Image对象。如果bytes无法解码则返回null。

images.toBytes(img[, format = "png", quality = 100])

  • image {image} 图片
  • format {string} 图片格式,可选的值为:
    • png
    • jpeg/jpg
    • webp
  • quality {number} 图片质量,为0~100的整数值
  • 返回 {byte[]}

把图片编码为字节数组并返回。

images.clip(img, x, y, w, h)

  • img {Image} 图片
  • x {number} 剪切区域的左上角横坐标
  • y {number} 剪切区域的左上角纵坐标
  • w {number} 剪切区域的宽度
  • h {number} 剪切区域的高度
  • 返回 {Image}

从图片img的位置(x, y)处剪切大小为w * h的区域,并返回该剪切区域的新图片。

var src = images.read("/sdcard/1.png");

var clip = images.clip(src, 100, 100, 400, 400);

images.save(clip, "/sdcard/clip.png");

images.resize(img, size[, interpolation])

[v4.1.0新增]

  • img {Image} 图片
  • size {Array} 两个元素的数组[w, h],分别表示宽度和高度;如果只有一个元素,则宽度和高度相等
  • interpolation {string} 插值方法,可选,默认为"LINEAR"(线性插值),可选的值有:
    • NEAREST 最近邻插值
    • LINEAR 线性插值(默认)
    • AREA 区域插值
    • CUBIC 三次样条插值
    • LANCZOS4 Lanczos插值 参见InterpolationFlags
  • 返回 {Image}

调整图片大小,并返回调整后的图片。例如把图片放缩为200*300:images.resize(img, [200, 300])。

参见Imgproc.resize

images.scale(img, fx, fy[, interpolation])

[v4.1.0新增]

  • img {Image} 图片
  • fx {number} 宽度放缩倍数
  • fy {number} 高度放缩倍数
  • interpolation {string} 插值方法,可选,默认为"LINEAR"(线性插值),可选的值有:
    • NEAREST 最近邻插值
    • LINEAR 线性插值(默认)
    • AREA 区域插值
    • CUBIC 三次样条插值
    • LANCZOS4 Lanczos插值 参见InterpolationFlags
  • 返回 {Image}

放缩图片,并返回放缩后的图片。例如把图片变成原来的一半:images.scale(img, 0.5, 0.5)。

参见Imgproc.resize

images.rotate(img, degress[, x, y])

[v4.1.0新增]

  • img {Image} 图片
  • degress {number} 旋转角度。
  • x {number} 旋转中心x坐标,默认为图片中点
  • y {number} 旋转中心y坐标,默认为图片中点
  • 返回 {Image}

将图片逆时针旋转degress度,返回旋转后的图片对象。

例如逆时针旋转90度为images.rotate(img, 90)。

images.concat(img1, image2[, direction])

[v4.1.0新增]

  • img1 {Image} 图片1
  • img2 {Image} 图片2
  • direction {string} 连接方向,默认为"RIGHT",可选的值有:
    • LEFT 将图片2接到图片1左边
    • RIGHT 将图片2接到图片1右边
    • TOP 将图片2接到图片1上边
    • BOTTOM 将图片2接到图片1下边
  • 返回 {Image}

连接两张图片,并返回连接后的图像。如果两张图片大小不一致,小的那张将适当居中。

images.grayscale(img)

[v4.1.0新增]

  • img {Image} 图片
  • 返回 {Image}

灰度化图片,并返回灰度化后的图片。

image.threshold(img, threshold, maxVal[, type])

[v4.1.0新增]

  • img {Image} 图片
  • threshold {number} 阈值
  • maxVal {number} 最大值
  • type {string} 阈值化类型,默认为"BINARY",参见ThresholdTypes, 可选的值:
    • BINARY
    • BINARY_INV
    • TRUNC
    • TOZERO
    • TOZERO_INV
    • OTSU
    • TRIANGLE
  • 返回 {Image}

将图片阈值化,并返回处理后的图像。可以用这个函数进行图片二值化。例如:images.threshold(img, 100, 255, "BINARY"),这个代码将图片中大于100的值全部变成255,其余变成0,从而达到二值化的效果。如果img是一张灰度化图片,这个代码将会得到一张黑白图片。

可以参考有关博客(比如threshold函数的使用)或者OpenCV文档threshold

images.adaptiveThreshold(img, maxValue, adaptiveMethod, thresholdType, blockSize, C)

[v4.1.0新增]

  • img {Image} 图片
  • maxValue {number} 最大值
  • adaptiveMethod {string} 在一个邻域内计算阈值所采用的算法,可选的值有:
    • MEAN_C 计算出领域的平均值再减去参数C的值
    • GAUSSIAN_C 计算出领域的高斯均值再减去参数C的值
  • thresholdType {string} 阈值化类型,可选的值有:
    • BINARY
    • BINARY_INV
  • blockSize {number} 邻域块大小
  • C {number} 偏移值调整量
  • 返回 {Image}

对图片进行自适应阈值化处理,并返回处理后的图像。

可以参考有关博客(比如threshold与adaptiveThreshold)或者OpenCV文档adaptiveThreshold

images.cvtColor(img, code[, dstCn])

[v4.1.0新增]

  • img {Image} 图片
  • code {string} 颜色空间转换的类型,可选的值有一共有205个(参见ColorConversionCodes),这里只列出几个:
    • BGR2GRAY BGR转换为灰度
    • BGR2HSV BGR转换为HSV
    • ``
  • dstCn {number} 目标图像的颜色通道数量,如果不填写则根据其他参数自动决定。
  • 返回 {Image}

对图像进行颜色空间转换,并返回转换后的图像。

可以参考有关博客(比如颜色空间转换)或者OpenCV文档cvtColor

images.inRange(img, lowerBound, upperBound)

[v4.1.0新增]

  • img {Image} 图片
  • lowerBound {string} | {number} 颜色下界
  • upperBound {string} | {number} 颜色下界
  • 返回 {Image}

将图片二值化,在lowerBound~upperBound范围以外的颜色都变成0,在范围以内的颜色都变成255。

例如images.inRange(img, "#000000", "#222222")。

images.interval(img, color, interval)

[v4.1.0新增]

  • img {Image} 图片
  • color {string} | {number} 颜色值
  • interval {number} 每个通道的范围间隔
  • 返回 {Image}

将图片二值化,在color-interval ~ color+interval范围以外的颜色都变成0,在范围以内的颜色都变成255。这里对color的加减是对每个通道而言的。

例如images.interval(img, "#888888", 16),每个通道的颜色值均为0x88,加减16后的范围是[0x78, 0x98],因此这个代码将把#787878~#989898的颜色变成#FFFFFF,而把这个范围以外的变成#000000。

images.blur(img, size[, anchor, type])

[v4.1.0新增]

  • img {Image} 图片
  • size {Array} 定义滤波器的大小,如[3, 3]
  • anchor {Array} 指定锚点位置(被平滑点),默认为图像中心
  • type {string} 推断边缘像素类型,默认为"DEFAULT",可选的值有:
    • CONSTANT iiiiii|abcdefgh|iiiiiii with some specified i
    • REPLICATE aaaaaa|abcdefgh|hhhhhhh
    • REFLECT fedcba|abcdefgh|hgfedcb
    • WRAP cdefgh|abcdefgh|abcdefg
    • REFLECT_101 gfedcb|abcdefgh|gfedcba
    • TRANSPARENT uvwxyz|abcdefgh|ijklmno
    • REFLECT101 same as BORDER_REFLECT_101
    • DEFAULT same as BORDER_REFLECT_101
    • ISOLATED do not look outside of ROI
  • 返回 {Image}

对图像进行模糊(平滑处理),返回处理后的图像。

可以参考有关博客(比如实现图像平滑处理)或者OpenCV文档blur

images.medianBlur(img, size)

[v4.1.0新增]

  • img {Image} 图片
  • size {Array} 定义滤波器的大小,如[3, 3]
  • 返回 {Image}

对图像进行中值滤波,返回处理后的图像。

可以参考有关博客(比如实现图像平滑处理)或者OpenCV文档blur

images.gaussianBlur(img, size[, sigmaX, sigmaY, type])

[v4.1.0新增]

  • img {Image} 图片
  • size {Array} 定义滤波器的大小,如[3, 3]
  • sigmaX {number} x方向的标准方差,不填写则自动计算
  • sigmaY {number} y方向的标准方差,不填写则自动计算
  • type {string} 推断边缘像素类型,默认为"DEFAULT",参见images.blur
  • 返回 {Image}

对图像进行高斯模糊,返回处理后的图像。

可以参考有关博客(比如实现图像平滑处理)或者OpenCV文档GaussianBlur

images.matToImage(mat)

[v4.1.0新增]

  • mat {Mat} OpenCV的Mat对象
  • 返回 {Image}

把Mat对象转换为Image对象。

找图找色

images.requestScreenCapture([landscape])

  • landscape {boolean} 布尔值, 表示将要执行的截屏是否为横屏。如果landscape为false, 则表示竖屏截图; true为横屏截图。

向系统申请屏幕截图权限,返回是否请求成功。

第一次使用该函数会弹出截图权限请求,建议选择“总是允许”。

这个函数只是申请截图权限,并不会真正执行截图,真正的截图函数是captureScreen()。

该函数在截图脚本中只需执行一次,而无需每次调用captureScreen()都调用一次。

如果不指定landscape值,则截图方向由当前设备屏幕方向决定,因此务必注意执行该函数时的屏幕方向。

建议在本软件界面运行该函数,在其他软件界面运行时容易出现一闪而过的黑屏现象。

示例:

//请求截图

if(!requestScreenCapture()){

    toast("请求截图失败");

    exit();

}

//连续截图10张图片(间隔1秒)并保存到存储卡目录

for(var i = 0; i < 10; i++){

    captureScreen("/sdcard/screencapture" + i + ".png");

    sleep(1000);

}

该函数也可以作为全局函数使用。

images.captureScreen()

截取当前屏幕并返回一个Image对象。

没有截图权限时执行该函数会抛出SecurityException。

该函数不会返回null,两次调用可能返回相同的Image对象。这是因为设备截图的更新需要一定的时间,短时间内(一般来说是16ms)连续调用则会返回同一张截图。

截图需要转换为Bitmap格式,从而该函数执行需要一定的时间(0~20ms)。

另外在requestScreenCapture()执行成功后需要一定时间后才有截图可用,因此如果立即调用captureScreen(),会等待一定时间后(一般为几百ms)才返回截图。

例子:

//请求横屏截图

requestScreenCapture(true);

//截图

var img = captureScreen();

//获取在点(100, 100)的颜色值

var color = images.pixel(img, 100, 100);

//显示该颜色值

toast(colors.toString(color));

该函数也可以作为全局函数使用。

images.captureScreen(path)

  • path {string} 截图保存路径

截取当前屏幕并以PNG格式保存到path中。如果文件不存在会被创建;文件存在会被覆盖。

该函数不会返回任何值。该函数也可以作为全局函数使用。

images.pixel(image, x, y)

  • image {Image} 图片
  • x {number} 要获取的像素的横坐标。
  • y {number} 要获取的像素的纵坐标。

返回图片image在点(x, y)处的像素的ARGB值。

该值的格式为0xAARRGGBB,是一个"32位整数"(虽然JavaScript中并不区分整数类型和其他数值类型)。

坐标系以图片左上角为原点。以图片左侧边为y轴,上侧边为x轴。

images.findColor(image, color, options)

  • image {Image} 图片
  • color {number} | {string} 要寻找的颜色的RGB值。如果是一个整数,则以0xRRGGBB的形式代表RGB值(A通道会被忽略);如果是字符串,则以"#RRGGBB"代表其RGB值。
  • options {Object} 选项

在图片中寻找颜色color。找到时返回找到的点Point,找不到时返回null。

选项包括:

  • region {Array} 找色区域。是一个两个或四个元素的数组。(region[0], region[1])表示找色区域的左上角;region[2]*region[3]表示找色区域的宽高。如果只有region只有两个元素,则找色区域为(region[0], region[1])到屏幕右下角。如果不指定region选项,则找色区域为整张图片。
  • threshold {number} 找色时颜色相似度的临界值,范围为0~255(越小越相似,0为颜色相等,255为任何颜色都能匹配)。默认为4。threshold和浮点数相似度(0.0~1.0)的换算为 similarity = (255 - threshold) / 255.

该函数也可以作为全局函数使用。

一个循环找色的例子如下:

requestScreenCapture();

 

//循环找色,找到红色(#ff0000)时停止并报告坐标

while(true){

    var img = captureScreen();

    var point = findColor(img, "#ff0000");

    if(point){

        toast("找到红色,坐标为(" + point.x + ", " + point.y + ")");

    }

}

一个区域找色的例子如下:

//读取本地图片/sdcard/1.png

var img = images.read("/sdcard/1.png");

//判断图片是否加载成功

if(!img){

    toast("没有该图片");

    exit();

}

//在该图片中找色,指定找色区域为在位置(400, 500)的宽为300长为200的区域,指定找色临界值为4

var point = findColor(img, "#00ff00", {

     region: [400, 500, 300, 200],

     threshold: 4

 });

if(point){

    toast("找到啦:" + point);

}else{

    toast("没找到");

}

images.findColorInRegion(img, color, x, y[, width, height, threshold])

区域找色的简便方法。

相当于

images.findColor(img, color, {

     region: [x, y, width, height],

     threshold: threshold

});

该函数也可以作为全局函数使用。

images.findColorEquals(img, color[, x, y, width, height])

  • img {Image} 图片
  • color {number} | {string} 要寻找的颜色
  • x {number} 找色区域的左上角横坐标
  • y {number} 找色区域的左上角纵坐标
  • width {number} 找色区域的宽度
  • height {number} 找色区域的高度
  • 返回 {Point}

在图片img指定区域中找到颜色和color完全相等的某个点,并返回该点的左边;如果没有找到,则返回null。

找色区域通过x, y, width, height指定,如果不指定找色区域,则在整张图片中寻找。

该函数也可以作为全局函数使用。

示例: (通过找QQ红点的颜色来判断是否有未读消息)

requestScreenCapture();

launchApp("QQ");

sleep(1200);

var p = findColorEquals(captureScreen(), "#f64d30");

if(p){

    toast("有未读消息");

}else{

    toast("没有未读消息");

}

images.findMultiColors(img, firstColor, colors[, options])

  • img {Image} 要找色的图片
  • firstColor {number} | {string} 第一个点的颜色
  • colors {Array} 表示剩下的点相对于第一个点的位置和颜色的数组,数组的每个元素为[x, y, color]
  • options {Object} 选项,包括:
    • region {Array} 找色区域。是一个两个或四个元素的数组。(region[0], region[1])表示找色区域的左上角;region[2]*region[3]表示找色区域的宽高。如果只有region只有两个元素,则找色区域为(region[0], region[1])到屏幕右下角。如果不指定region选项,则找色区域为整张图片。
    • threshold {number} 找色时颜色相似度的临界值,范围为0~255(越小越相似,0为颜色相等,255为任何颜色都能匹配)。默认为4。threshold和浮点数相似度(0.0~1.0)的换算为 similarity = (255 - threshold) / 255.

多点找色,类似于按键精灵的多点找色,其过程如下:

  • 在图片img中找到颜色firstColor的位置(x0, y0)
  • 对于数组colors的每个元素[x, y, color],检查图片img在位置(x + x0, y + y0)上的像素是否是颜色color,是的话返回(x0, y0),否则继续寻找firstColor的位置,重新执行第1步
  • 整张图片都找不到时返回null

例如,对于代码images.findMultiColors(img, "#123456", [[10, 20, "#ffffff"], [30, 40, "#000000"]]),假设图片在(100, 200)的位置的颜色为#123456, 这时如果(110, 220)的位置的颜色为#fffff且(130, 240)的位置的颜色为#000000,则函数返回点(100, 200)。

如果要指定找色区域,则在options中指定,例如:

var p = images.findMultiColors(img, "#123456", [[10, 20, "#ffffff"], [30, 40, "#000000"]], {

    region: [0, 960, 1080, 960]

});

images.detectsColor(image, color, x, y[, threshold = 16, algorithm = "diff"])

  • image {Image} 图片
  • color {number} | {string} 要检测的颜色
  • x {number} 要检测的位置横坐标
  • y {number} 要检测的位置纵坐标
  • threshold {number} 颜色相似度临界值,默认为16。取值范围为0~255。
  • algorithm {string} 颜色匹配算法,包括:
    • "equal": 相等匹配,只有与给定颜色color完全相等时才匹配。
    • "diff": 差值匹配。与给定颜色的R、G、B差的绝对值之和小于threshold时匹配。
    • "rgb": rgb欧拉距离相似度。与给定颜色color的rgb欧拉距离小于等于threshold时匹配。
    • "rgb+": 加权rgb欧拉距离匹配(LAB Delta E)。
    • "hs": hs欧拉距离匹配。hs为HSV空间的色调值。

返回图片image在位置(x, y)处是否匹配到颜色color。用于检测图片中某个位置是否是特定颜色。

一个判断微博客户端的某个微博是否被点赞过的例子:

requestScreenCapture();

//找到点赞控件

var like = id("ly_feed_like_icon").findOne();

//获取该控件中点坐标

var x = like.bounds().centerX();

var y = like.bounds().centerY();

//截图

var img = captureScreen();

//判断在该坐标的颜色是否为橙红色

if(images.detectsColor(img, "#fed9a8", x, y)){

    //是的话则已经是点赞过的了,不做任何动作

}else{

    //否则点击点赞按钮

    like.click();

}

images.findImage(img, template[, options])

  • img {Image} 大图片
  • template {Image} 小图片(模板)
  • options {Object} 找图选项

找图。在大图片img中查找小图片template的位置(模块匹配),找到时返回位置坐标(Point),找不到时返回null。

选项包括:

  • threshold {number} 图片相似度。取值范围为0~1的浮点数。默认值为0.9。
  • region {Array} 找图区域。参见findColor函数关于region的说明。
  • level {number} 一般而言不必修改此参数。不加此参数时该参数会根据图片大小自动调整。找图算法是采用图像金字塔进行的, level参数表示金字塔的层次, level越大可能带来越高的找图效率,但也可能造成找图失败(图片因过度缩小而无法分辨)或返回错误位置。因此,除非您清楚该参数的意义并需要进行性能调优,否则不需要用到该参数。

该函数也可以作为全局函数使用。

一个最简单的找图例子如下:

var img = images.read("/sdcard/大图.png");

var templ = images.read("/sdcard/小图.png");

var p = findImage(img, templ);

if(p){

    toast("找到啦:" + p);

}else{

    toast("没找到");

}

稍微复杂点的区域找图例子如下:

auto();

requestScreenCapture();

var wx = images.read("/sdcard/微信图标.png");

//返回桌面

home();

//截图并找图

var p = findImage(captureScreen(), wx, {

    region: [0, 50],

    threshold: 0.8

});

if(p){

    toast("在桌面找到了微信图标啦: " + p);

}else{

    toast("在桌面没有找到微信图标");

}

images.findImageInRegion(img, template, x, y[, width, height, threshold])

区域找图的简便方法。相当于:

images.findImage(img, template, {

    region: [x, y, width, height],

    threshold: threshold

})

该函数也可以作为全局函数使用。

images.matchTemplate(img, template, options)

[v4.1.0新增]

  • img {Image} 大图片
  • template {Image} 小图片(模板)
  • options {Object} 找图选项:
    • threshold {number} 图片相似度。取值范围为0~1的浮点数。默认值为0.9。
    • region {Array} 找图区域。参见findColor函数关于region的说明。
    • max {number} 找图结果最大数量,默认为5
    • level {number} 一般而言不必修改此参数。不加此参数时该参数会根据图片大小自动调整。找图算法是采用图像金字塔进行的, level参数表示金字塔的层次, level越大可能带来越高的找图效率,但也可能造成找图失败(图片因过度缩小而无法分辨)或返回错误位置。因此,除非您清楚该参数的意义并需要进行性能调优,否则不需要用到该参数。
  • 返回 {MatchingResult}

在大图片中搜索小图片,并返回搜索结果MatchingResult。该函数可以用于找图时找出多个位置,可以通过max参数控制最大的结果数量。也可以对匹配结果进行排序、求最值等操作。

MatchingResult

[v4.1.0新增]

matches

  • {Array} 匹配结果的数组。

数组的元素是一个Match对象:

  • point {Point} 匹配位置
  • similarity {number} 相似度

例如:

var result = images.matchTemplate(img, template, {

    max: 100

});

result.matches.forEach(match => {

    log("point = " + match.point + ", similarity = " + match.similarity);

});

points

  • {Array} 匹配位置的数组。

first()

  • 返回 {Match}

第一个匹配结果。如果没有任何匹配,则返回null。

last()

  • 返回 {Match}

最后一个匹配结果。如果没有任何匹配,则返回null。

leftmost()

  • 返回 {Match}

位于大图片最左边的匹配结果。如果没有任何匹配,则返回null。

topmost()

  • 返回 {Match}

位于大图片最上边的匹配结果。如果没有任何匹配,则返回null。

rightmost()

  • 返回 {Match}

位于大图片最右边的匹配结果。如果没有任何匹配,则返回null。

bottommost()

  • 返回 {Match}

位于大图片最下边的匹配结果。如果没有任何匹配,则返回null。

best()

  • 返回 {Match}

相似度最高的匹配结果。如果没有任何匹配,则返回null。

worst()

  • 返回 {Match}

相似度最低的匹配结果。如果没有任何匹配,则返回null。

sortBy(cmp)

  • cmp {Function}|{string} 比较函数,或者是一个字符串表示排序方向。例如"left"表示将匹配结果按匹配位置从左往右排序、"top"表示将匹配结果按匹配位置从上往下排序,"left-top"表示将匹配结果按匹配位置从左往右、从上往下排序。方向包括left(左), top (上), right (右), bottom(下)。
  • {MatchingResult}

对匹配结果进行排序,并返回排序后的结果。

var result = images.matchTemplate(img, template, {

    max: 100

});

log(result.sortBy("top-right"));

Image

表示一张图片,可以是截图的图片,或者本地读取的图片,或者从网络获取的图片。

Image.getWidth()

返回以像素为单位图片宽度。

Image.getHeight()

返回以像素为单位的图片高度。

Image.saveTo(path)

  • path {string} 路径

把图片保存到路径path。(如果文件存在则覆盖)

Image.pixel(x, y)

  • x {number} 横坐标
  • y {number} 纵坐标

返回图片image在点(x, y)处的像素的ARGB值。

该值的格式为0xAARRGGBB,是一个"32位整数"(虽然JavaScript中并不区分整数类型和其他数值类型)。

坐标系以图片左上角为原点。以图片左侧边为y轴,上侧边为x轴。

##

Point

findColor, findImage返回的对象。表示一个点(坐标)。

Point.x

横坐标。

Point.y

纵坐标。

Canvas

canvas提供了使用画布进行2D画图的支持,可用于简单的小游戏开发或者图片编辑。使用canvas可以轻松地在一张图片或一个界面上绘制各种线与图形。

canvas的坐标系为平面直角坐标系,以屏幕左上角为原点,屏幕上边为x轴正方向,屏幕左边为y轴正方向。例如分辨率为1920*1080的屏幕上,画一条从屏幕左上角到屏幕右下角的线段为:

canvas.drawLine(0, 0, 1080, 1920, paint);

canvas的绘制依赖于画笔Paint, 通过设置画笔的粗细、颜色、填充等可以改变绘制出来的图形。例如绘制一个红色实心正方形为:

var paint = new Paint();

//设置画笔为填充,则绘制出来的图形都是实心的

paint.setStyle(Paint.STYLE.FILL);

//设置画笔颜色为红色

paint.setColor(colors.RED);

//绘制一个从坐标(0, 0)到坐标(100, 100)的正方形

canvas.drawRect(0, 0, 100, 100, paint);

如果要绘制正方形的边框,则通过设置画笔的Style来实现:

var paint = new Paint();

//设置画笔为描边,则绘制出来的图形都是轮廓

paint.setStyle(Paint.STYLE.STROKE);

//设置画笔颜色为红色

paint.setColor(colors.RED);

//绘制一个从坐标(0, 0)到坐标(100, 100)的正方形

canvas.drawRect(0, 0, 100, 100, paint);

结合画笔canvas可以绘制基本图形、图片等。

canvas.drawARGB(a, r, g, b)

canvas.draw

Keys

按键模拟部分提供了一些模拟物理按键的全局函数,包括Home、音量键、照相键等,有的函数依赖于无障碍服务,有的函数依赖于root权限。

一般来说,以大写字母开头的函数都依赖于root权限。执行此类函数时,如果没有root权限,则函数执行后没有效果,并会在控制台输出一个警告。

back()

  • 返回 {boolean}

模拟按下返回键。返回是否执行成功。 此函数依赖于无障碍服务。

home()

  • 返回 {boolean}

模拟按下Home键。返回是否执行成功。 此函数依赖于无障碍服务。

powerDialog()

  • 返回 {boolean}

弹出电源键菜单。返回是否执行成功。 此函数依赖于无障碍服务。

notifications()

  • 返回 {boolean}

拉出通知栏。返回是否执行成功。 此函数依赖于无障碍服务。

quickSettings()

  • 返回 {boolean}

显示快速设置(下拉通知栏到底)。返回是否执行成功。 此函数依赖于无障碍服务。

recents()

  • 返回 {boolean}

显示最近任务。返回是否执行成功。 此函数依赖于无障碍服务。

splitScreen()

  • 返回 {boolean}

分屏。返回是否执行成功。 此函数依赖于无障碍服务, 并且需要系统自身功能的支持。

Home()

模拟按下Home键。 此函数依赖于root权限。

Back()

模拟按下返回键。 此函数依赖于root权限。

Power()

模拟按下电源键。 此函数依赖于root权限。

Menu()

模拟按下菜单键。 此函数依赖于root权限。

VolumeUp()

按下音量上键。 此函数依赖于root权限。

VolumeDown()

按键音量上键。 此函数依赖于root权限。

Camera()

模拟按下照相键。

Up()

模拟按下物理按键上。 此函数依赖于root权限。

Down()

模拟按下物理按键下。 此函数依赖于root权限。

Left()

模拟按下物理按键左。 此函数依赖于root权限。

Right()

模拟按下物理按键右。 此函数依赖于root权限。

OK()

模拟按下物理按键确定。 此函数依赖于root权限。

Text(text)

  • text {string} 要输入的文字,只能为英文或英文符号 输入文字text。例如Text("aaa");

KeyCode(code)

  • code {number} | 要按下的按键的数字代码或名称。参见下表。 模拟物理按键。例如KeyCode(29)和KeyCode("KEYCODE_A")是按下A键。

附录: KeyCode对照表

KeyCode KeyEvent Value

  • KEYCODE_MENU 1
  • KEYCODE_SOFT_RIGHT 2
  • KEYCODE_HOME 3
  • KEYCODE_BACK 4
  • KEYCODE_CALL 5
  • KEYCODE_ENDCALL 6
  • KEYCODE_0 7
  • KEYCODE_1 8
  • KEYCODE_2 9
  • KEYCODE_3 10
  • KEYCODE_4 11
  • KEYCODE_5 12
  • KEYCODE_6 13
  • KEYCODE_7 14
  • KEYCODE_8 15
  • KEYCODE_9 16
  • KEYCODE_STAR 17
  • KEYCODE_POUND 18
  • KEYCODE_DPAD_UP 19
  • KEYCODE_DPAD_DOWN 20
  • KEYCODE_DPAD_LEFT 21
  • KEYCODE_DPAD_RIGHT 22
  • KEYCODE_DPAD_CENTER 23
  • KEYCODE_VOLUME_UP 24
  • KEYCODE_VOLUME_DOWN 25
  • KEYCODE_POWER 26
  • KEYCODE_CAMERA 27
  • KEYCODE_CLEAR 28
  • KEYCODE_A 29
  • KEYCODE_B 30
  • KEYCODE_C 31
  • KEYCODE_D 32
  • KEYCODE_E 33
  • KEYCODE_F 34
  • KEYCODE_G 35
  • KEYCODE_H 36
  • KEYCODE_I 37
  • KEYCODE_J 38
  • KEYCODE_K 39
  • KEYCODE_L 40
  • KEYCODE_M 41
  • KEYCODE_N 42
  • KEYCODE_O 43
  • KEYCODE_P 44
  • KEYCODE_Q 45
  • KEYCODE_R 46
  • KEYCODE_S 47
  • KEYCODE_T 48
  • KEYCODE_U 49
  • KEYCODE_V 50
  • KEYCODE_W 51
  • KEYCODE_X 52
  • KEYCODE_Y 53
  • KEYCODE_Z 54
  • KEYCODE_COMMA 55
  • KEYCODE_PERIOD 56
  • KEYCODE_ALT_LEFT 57
  • KEYCODE_ALT_RIGHT 58
  • KEYCODE_SHIFT_LEFT 59
  • KEYCODE_SHIFT_RIGHT 60
  • KEYCODE_TAB 61
  • KEYCODE_SPACE 62
  • KEYCODE_SYM 63
  • KEYCODE_EXPLORER 64
  • KEYCODE_ENVELOPE 65
  • KEYCODE_ENTER 66
  • KEYCODE_DEL 67
  • KEYCODE_GRAVE 68
  • KEYCODE_MINUS 69
  • KEYCODE_EQUALS 70
  • KEYCODE_LEFT_BRACKET 71
  • KEYCODE_RIGHT_BRACKET 72
  • KEYCODE_BACKSLASH 73
  • KEYCODE_SEMICOLON 74
  • KEYCODE_APOSTROPHE 75
  • KEYCODE_SLASH 76
  • KEYCODE_AT 77
  • KEYCODE_NUM 78
  • KEYCODE_HEADSETHOOK 79
  • KEYCODE_FOCUS 80
  • KEYCODE_PLUS 81
  • KEYCODE_MENU 82
  • KEYCODE_NOTIFICATION 83
  • KEYCODE_SEARCH 84
  • TAGLAST KEYCODE 85

Media

Stability: 2 - Stable

media模块提供多媒体编程的支持。目前仅支持音乐播放和媒体文件扫描。后续会结合UI加入视频播放等功能。

需要注意是,使用该模块播放音乐时是在后台异步播放的,在脚本结束后会自动结束播放,因此可能需要插入诸如sleep()的语句来使脚本保持运行。例如:

//播放音乐

media.playMusic("/sdcard/1.mp3");

//让音乐播放完

sleep(media.getMusicDuration());

media.scanFile(path)

  • path {string} 媒体文件路径

扫描路径path的媒体文件,将它加入媒体库中;或者如果该文件以及被删除,则通知媒体库移除该文件。

媒体库包括相册、音乐库等,因此该函数可以用于把某个图片文件加入相册。

//请求截图

requestScreenCapture(false);

//截图

var im = captureScreen();

var path = "/sdcard/screenshot.png";

//保存图片

im.saveTo(path);

//把图片加入相册

media.scanFile(path);

media.playMusic(path[, volume, looping])

  • path {string} 音乐文件路径
  • volume {number} 播放音量,为0~1的浮点数,默认为1
  • looping {boolean} 是否循环播放,如果looping为true则循环播放,默认为false

播放音乐文件path。该函数不会显示任何音乐播放界面。如果文件不存在或者文件不是受支持的音乐格式,则抛出UncheckedIOException异常。

//播放音乐

media.playMusic("/sdcard/1.mp3");

//让音乐播放完

sleep(media.getMusicDuration());

如果要循环播放音乐,则使用looping参数:

//传递第三个参数为true以循环播放音乐 media.playMusic("/sdcard/1.mp3", 1, true); //等待三次播放的时间 sleep(media.getMusicDuration() * 3);

如果要使用音乐播放器播放音乐,调用app.viewFile(path)函数。

media.musicSeekTo(msec)

  • msec {number} 毫秒数,表示音乐进度

把当前播放进度调整到时间msec的位置。如果当前没有在播放音乐,则调用函数没有任何效果。

例如,要把音乐调到1分钟的位置,为media.musicSeekTo(60 * 1000)。

//播放音乐

media.playMusic("/sdcard/1.mp3");

//调整到30秒的位置

media.musicSeekTo(30 * 1000);

//等待音乐播放完成

sleep(media.getMusicDuration() - 30 * 1000);

media.pauseMusic()

暂停音乐播放。如果当前没有在播放音乐,则调用函数没有任何效果。

media.resumeMusic()

继续音乐播放。如果当前没有播放过音乐,则调用该函数没有任何效果。

media.stopMusic()

停止音乐播放。如果当前没有在播放音乐,则调用函数没有任何效果。

media.isMusicPlaying()

  • 返回 {boolean}

返回当前是否正在播放音乐。

media.getMusicDuration()

  • 返回 {number}

返回当前音乐的时长。单位毫秒。

media.getMusicCurrentPosition()

  • 返回 {number}

返回当前音乐的播放进度(已经播放的时间),单位毫秒。

module (模块)

Stability: 2 - Stable

Auto.js 有一个简单的模块加载系统。 在 Auto.js 中,文件和模块是一一对应的(每个文件被视为一个独立的模块)。

例子,假设有一个名为 foo.js 的文件:

var circle = require('circle.js');

console.log("半径为 4 的圆的面积是 %d", circle.area(4));

在第一行中,foo.js 加载了同一目录下的 circle.js 模块。

circle.js 文件的内容为:

const PI = Math.PI;

 

var circle = {};

 

circle.area = function (r) {

  return PI * r * r;

};

 

circle.circumference = (r) => 2 * PI * r;

 

module.exports = circle;

circle.js 模块导出了 area() 和 circumference() 两个函数。 通过在特殊的 exports 对象上指定额外的属性,函数和对象可以被添加到模块的根部。

模块内的本地变量是私有的。 在这个例子中,变量 PI 是 circle.js 私有的,不会影响到加载他的脚本的变量环境。

module.exports属性可以被赋予一个新的值(例如函数或对象)。

如下,bar.js 会用到 square 模块,square 导出一个构造函数:

const square = require('square.js');

const mySquare = square(2);

console.log("正方形的面积是 %d", mySquare.area());

square 模块定义在 square.js 中:

 

// 赋值给 `exports` 不会修改模块,必须使用 `module.exports`

module.exports = function(width) {

  return {

    area: () => width ** 2

  };

};

基于控件的操作

基于控件的操作指的是选择屏幕上的控件,获取其信息或对其进行操作。对于一般软件而言,基于控件的操作对不同机型有很好的兼容性;但是对于游戏而言,由于游戏界面并不是由控件构成,无法采用本章节的方法,也无法使用本章节的函数。有关游戏脚本的编写,请参考《基于坐标的操作》。

基于控件的操作依赖于无障碍服务,因此最好在脚本开头使用auto()函数来确保无障碍服务已经启用。如果运行到某个需要权限的语句无障碍服务并没启动,则会抛出异常并跳转到无障碍服务界面。这样的用户体验并不好,因为需要重新运行脚本,后续会加入等待无障碍服务启动并让脚本继续运行的函数。

您也可以在脚本开头使用"auto";表示这个脚本需要无障碍服务,但是不推荐这种做法,因为这个标记必须在脚本的最开头(前面不能有注释或其他语句、空格等),我们推荐使用auto()函数来确保无障碍服务已启用。

auto([mode])

  • mode {string} 模式

检查无障碍服务是否已经启用,如果没有启用则抛出异常并跳转到无障碍服务启用界面;同时设置无障碍模式为mode。mode的可选值为:

  • fast 快速模式。该模式下会启用控件缓存,从而选择器获取屏幕控件更快。对于需要快速的控件操作的脚本可以使用该模式,一般脚本则没有必要使用该函数。
  • normal 正常模式,默认。

如果不加mode参数,则为正常模式。

建议使用auto.waitFor()和auto.setMode()代替该函数,因为auto()函数如果无障碍服务未启动会停止脚本;而auto.waitFor()则会在在无障碍服务启动后继续运行。

示例:

auto("fast");

示例2:

auto();

auto.waitFor()

检查无障碍服务是否已经启用,如果没有启用则跳转到无障碍服务启用界面,并等待无障碍服务启动;当无障碍服务启动后脚本会继续运行。

auto.setMode(mode)

  • mode {string} 模式

设置无障碍模式为mode。mode的可选值为:

  • fast 快速模式。该模式下会启用控件缓存,从而选择器获取屏幕控件更快。对于需要快速的控件查看和操作的脚本可以使用该模式,一般脚本则没有必要使用该函数。
  • normal 正常模式,默认。

SimpleActionAutomator

Stability: 2 - Stable

SimpleActionAutomator提供了一些模拟简单操作的函数,例如点击文字、模拟按键等。这些函数可以直接作为全局函数使用。

click(text[, i])

  • text {string} 要点击的文本
  • i {number} 如果相同的文本在屏幕中出现多次,则i表示要点击第几个文本, i从0开始计算

返回是否点击成功。当屏幕中并未包含该文本,或者该文本所在区域不能点击时返回false,否则返回true。

该函数可以点击大部分包含文字的按钮。例如微信主界面下方的"微信", "联系人", "发现", "我"的按钮。
通常与while同时使用以便点击按钮直至成功。例如:

while(!click("扫一扫"));

当不指定参数i时则会尝试点击屏幕上出现的所有文字text并返回是否全部点击成功。

i是从0开始计算的, 也就是, click("啦啦啦", 0)表示点击屏幕上第一个"啦啦啦", click("啦啦啦", 1)表示点击屏幕上第二个"啦啦啦"。

文本所在区域指的是,从文本处向其父视图寻找,直至发现一个可点击的部件为止。

click(left, top, bottom, right)

  • left {number} 要点击的长方形区域左边与屏幕左边的像素距离
  • top {number} 要点击的长方形区域上边与屏幕上边的像素距离
  • bottom {number} 要点击的长方形区域下边与屏幕下边的像素距离
  • right {number} 要点击的长方形区域右边与屏幕右边的像素距离

注意,该函数一般只用于录制的脚本中使用,在自己写的代码中使用该函数一般不要使用该函数。

点击在指定区域的控件。当屏幕中并未包含与该区域严格匹配的区域,或者该区域不能点击时返回false,否则返回true。

有些按钮或者部件是图标而不是文字(例如发送朋友圈的照相机图标以及QQ下方的消息、联系人、动态图标),这时不能通过click(text, i)来点击,可以通过描述图标所在的区域来点击。left, bottom, top, right描述的就是点击的区域。

至于要定位点击的区域,可以在悬浮窗使用布局分析工具查看控件的bounds属性。

通过无障碍服务录制脚本会生成该语句。

longClick(text[, i]))

  • text {string} 要长按的文本
  • i {number} 如果相同的文本在屏幕中出现多次,则i表示要长按第几个文本, i从0开始计算

返回是否点击成功。当屏幕中并未包含该文本,或者该文本所在区域不能点击时返回false,否则返回true。

当不指定参数i时则会尝试点击屏幕上出现的所有文字text并返回是否全部长按成功。

scrollUp([i])

  • i {number} 要滑动的控件序号

找到第i+1个可滑动控件上滑或左滑。返回是否操作成功。屏幕上没有可滑动的控件时返回false。

另外不加参数时scrollUp()会寻找面积最大的可滑动的控件上滑或左滑,例如微信消息列表等。

参数为一个整数i时会找到第i + 1个可滑动控件滑动。例如scrollUp(0)为滑动第一个可滑动控件。

scrollDown([i])

  • i {number} 要滑动的控件序号

找到第i+1个可滑动控件下滑或右滑。返回是否操作成功。屏幕上没有可滑动的控件时返回false。

另外不加参数时scrollUp()会寻找面积最大的可滑动的控件下滑或右滑。

参数为一个整数i时会找到第i + 1个可滑动控件滑动。例如scrollUp(0)为滑动第一个可滑动控件。

setText([i, ]text)

  • i {number} 表示要输入的为第i + 1个输入框
  • text {string} 要输入的文本

返回是否输入成功。当找不到对应的文本框时返回false。

不加参数i则会把所有输入框的文本都置为text。例如setText("测试")。

这里的输入文本的意思是,把输入框的文本置为text,而不是在原来的文本上追加。

input([i, ]text)

  • i {number} 表示要输入的为第i + 1个输入框
  • text {string} 要输入的文本

返回是否输入成功。当找不到对应的文本框时返回false。

不加参数i则会把所有输入框的文本追加内容text。例如input("测试")。

UiSelector

UiSelector即选择器,用于通过各种条件选取屏幕上的控件,再对这些控件进行点击、长按等动作。这里需要先简单介绍一下控件和界面的相关知识。

一般软件的界面是由一个个控件构成的,例如图片部分是一个图片控件(ImageView),文字部分是一个文字控件(TextView);同时,通过各种布局来决定各个控件的位置,例如,线性布局(LinearLayout)里面的控件都是按水平或垂直一次叠放的,列表布局(AbsListView)则是以列表的形式显示控件。

控件有各种属性,包括文本(text), 描述(desc), 类名(className), id等等。我们通常用一个控件的属性来找到这个控件,例如,想要点击QQ聊天窗口的"发送"按钮,我们就可以通过他的文本属性为"发送"来找到这个控件并点击他,具体代码为:

var sendButton = text("发送").findOne();

sendButton.click();

在这个例子中, text("发送")表示一个条件(文本属性为"发送"),findOne()表示基于这个条件找到一个符合条件的控件,从而我们可以得到发送按钮sendButton,再执行sendButton.click()即可点击"发送"按钮。

用文本属性来定位按钮控件、文本控件通常十分有效。但是,如果一个控件是图片控件,比如Auto.js主界面右上角的搜索图标,他没有文本属性,这时需要其他属性来定位他。我们如何查看他有什么属性呢?首先打开悬浮窗和无障碍服务,点击蓝色的图标(布局分析), 可以看到以下界面:

之后我们点击搜索图标,可以看到他有以下属性:

我们注意到这个图标的desc(描述)属性为"搜索",那么我们就可以通过desc属性来定位这个控件,得到点击搜索图标的代码为:

desc("搜索").findOne().click();

可能心细的你可能注意到了,这个控件还有很多其他的属性,例如checked, className, clickable等等,为什么不用这些属性来定位搜索图标呢?答案是,其他控件也有这些值相同的属性、尝试一下你就可以发现很多其他控件的checked属性和搜索控件一样都是false,如果我们用checked(false)作为条件,将会找到很多控件,而无法确定哪一个是搜索图标。因此,要找到我们想要的那个控件,选择器的条件通常需要是可唯一确定控件的。我们通常用一个独一无二的属性来定位一个控件,例如这个例子中就没有其他控件的desc(描述)属性为"搜索"。

另外,对于这个搜索图标而言,id属性也是唯一的,我们也可以用id("action_search").findOne().click()来点击这个控件。如果一个控件有id属性,那么这个属性很可能是唯一的,除了以下几种情况:

  • QQ的控件的id属性很多都是"name",也就是在QQ界面难以通过id来定位一个控件
  • 列表中的控件,比如QQ联系人列表,微信联系人列表等

尽管id属性很方便,但也不总是最方便的,例如对于微信和网易云音乐,每次更新他的控件id都会变化,导致了相同代码对于不同版本的微信、网易云音乐并不兼容。

除了这些属性外,主要还有以下几种属性:

  • className 类名。类名表示一个控件的类型,例如文本控件为"android.widget.TextView", 图片控件为"android.widget.ImageView"等。
  • packageName 包名。包名表示控件所在的应用包名,例如QQ界面的控件的包名为"com.tencent.mobileqq"。
  • bounds 控件在屏幕上的范围。
  • drawingOrder 控件在父控件的绘制顺序。
  • indexInParent 控件在父控件的位置。
  • clickable 控件是否可点击。
  • longClickable 控件是否可长按。
  • checkable 控件是否可勾选。
  • checked 控件是否可已勾选。
  • scrollable 控件是否可滑动。
  • selected 控件是否已选择。
  • editable 控件是否可编辑。
  • visibleToUser 控件是否可见。
  • enabled 控件是否已启用。
  • depth 控件的布局深度。

有时候只靠一个属性并不能唯一确定一个控件,这时需要通过属性的组合来完成定位,例如className("ImageView").depth(10).findOne().click(),通过链式调用来组合条件。

通常用这些技巧便可以解决大部分问题,即使解决不了问题,也可以通过布局分析的"生成代码"功能来尝试生成一些选择器代码。接下来的问题便是对选取的控件进行操作,包括:

  • click() 点击。点击一个控件,前提是这个控件的clickable属性为true
  • longClick() 长按。长按一个控件,前提是这个控件的longClickable属性为true
  • setText() 设置文本,用于编辑框控件设置文本。
  • scrollForward(), scrollBackward() 滑动。滑动一个控件(列表等), 前提是这个控件的scrollable属性为true
  • exits() 判断控件是否存在
  • waitFor() 等待控件出现

这些操作包含了绝大部分控件操作。根据这些我们可以很容易写出一个"刷屏"脚本(代码仅为示例,请不要在别人的群里测试,否则容易被踢):

while(true){

    className("EditText").findOne().setText("刷屏...");

    text("发送").findOne().clicK();

}

上面这段代码也可以写成:

while(true){

    className("EditText").setText("刷屏...");

    text("发送").clicK();

}

如果不加findOne()而直接进行操作,则选择器会找出所有符合条件的控件并操作。

另外一个比较常用的操作的滑动。滑动操作的第一步是找到需要滑动的控件,例如要滑动QQ消息列表则在悬浮窗布局层次分析中找到AbsListView,这个控件就是消息列表控件,如下图:

长按可查看控件信息,注意到其scrollable属性为true,并找出其id为"recent_chat_list",从而下滑QQ消息列表的代码为:

id("recent_chat_list").className("AbsListView").findOne().scrollForward();

scrollForward()为向前滑,包括下滑和右滑。

选择器的入门教程暂且要这里,更多信息可以查看下面的文档和选择器进阶。

selector()

  • 返回 {UiSelector}

创建一个新的选择器。但一般情况不需要使用该函数,因为可以直接用相应条件的语句创建选择器。

由于历史遗留原因,本不应该这样设计(不应该让id(), text()等作为全局函数,而是应该用By.id(), By.text()),但为了后向兼容性只能保留这个设计。

这样的API设计会污染全局变量,后续可能会支持"去掉这些全局函数而使用By.*"的选项。

UiSelector.text(str)

  • str {string} 控件文本
  • 返回 {UiSelector} 返回选择器自身以便链式调用

为当前选择器附加控件"text等于字符串str"的筛选条件。

控件的text(文本)属性是文本控件上的显示的文字,例如微信左上角的"微信"文本。

UiSelector.textContains(str)

  • str {string} 要包含的字符串

为当前选择器附加控件"text需要包含字符串str"的筛选条件。

这是一个比较有用的条件,例如QQ动态页和微博发现页上方的"大家都在搜...."的控件可以用textContains("大家都在搜").findOne()来获取。

UiSelector.textStartsWith(prefix)

  • prefix {string} 前缀

为当前选择器附加控件"text需要以prefix开头"的筛选条件。

这也是一个比较有用的条件,例如要找出Auto.js脚本列表中名称以"QQ"开头的脚本的代码为textStartsWith("QQ").find()。

UiSelector.textEndsWith(suffix)

  • suffix {string} 后缀

为当前选择器附加控件"text需要以suffix结束"的筛选条件。

UiSelector.textMatches(reg)

  • reg {string} | {Regex} 要满足的正则表达式。

为当前选择器附加控件"text需要满足正则表达式reg"的条件。

有关正则表达式,可以查看正则表达式 - 菜鸟教程

需要注意的是,如果正则表达式是字符串,则需要使用\\来表达\(也即Java正则表达式的形式),例如textMatches("\\d+")匹配多位数字;但如果使用JavaScript语法的正则表达式则不需要,例如textMatches(/\d+/)。但如果使用字符串的正则表达式则该字符串不能以"/"同时以"/"结束,也即不能写诸如textMatches("/\\d+/")的表达式,否则会被开头的"/"和结尾的"/"会被忽略。

UiSelector.desc(str)

  • str {string} 控件文本
  • 返回 {UiSelector} 返回选择器自身以便链式调用

为当前选择器附加控件"desc等于字符串str"的筛选条件。

控件的desc(描述,全称为Content-Description)属性是对一个控件的描述,例如网易云音乐右上角的放大镜图标的描述为搜索。要查看一个控件的描述,同样地可以借助悬浮窗查看。

desc属性同样是定位控件的利器。

UiSelector.descContains(str)

  • str {string} 要包含的字符串

为当前选择器附加控件"desc需要包含字符串str"的筛选条件。

UiSelector.descStartsWith(prefix)

  • prefix {string} 前缀

为当前选择器附加控件"desc需要以prefix开头"的筛选条件。

UiSelector.descEndsWith(suffix)

  • suffix {string} 后缀

为当前选择器附加控件"desc需要以suffix结束"的筛选条件。

UiSelector.descMatches(reg)

  • reg {string} | {Regex} 要满足的正则表达式。

为当前选择器附加控件"desc需要满足正则表达式reg"的条件。

有关正则表达式,可以查看正则表达式 - 菜鸟教程

需要注意的是,如果正则表达式是字符串,则需要使用\\来表达\(也即Java正则表达式的形式),例如textMatches("\\d+")匹配多位数字;但如果使用JavaScript语法的正则表达式则不需要,例如textMatches(/\d+/)。但如果使用字符串的正则表达式则该字符串不能以"/"同时以"/"结束,也即不能写诸如textMatches("/\\d+/")的表达式,否则会被开头的"/"和结尾的"/"会被忽略。

UiSelector.id(resId)

  • resId {string} 控件的id,以"包名:id/"开头,例如"com.tencent.mm:id/send_btn"。也可以不指定包名,这时会以当前正在运行的应用的包名来补全id。例如id("send_btn"),在QQ界面想当于id("com.tencent.mobileqq:id/send_btn")。

为当前选择器附加"id等于resId"的筛选条件。

控件的id属性通常是可以用来确定控件的唯一标识,如果一个控件有id,那么使用id来找到他是最好的方法。要查看屏幕上的控件的id,可以开启悬浮窗并使用界面工具,点击相应控件即可查看。若查看到的控件id为null, 表示该控件没有id。另外,在列表中会出现多个控件的id相同的情况。例如微信的联系人列表,每个头像的id都是一样的。此时不能用id来唯一确定控件。

在QQ界面经常会出现多个id为"name"的控件,在微信上则每个版本的id都会变化。对于这些软件而言比较难用id定位控件。

UiSelector.idContains(str)

  • str {string} id要包含的字符串

为当前选择器附加控件"id包含字符串str"的筛选条件。比较少用。

UiSelector.idStartsWith(prefix)

  • prefix {string} id前缀

为当前选择器附加"id需要以prefix开头"的筛选条件。比较少用。

UiSelector.idEndsWith(suffix)

  • suffix {string} id后缀

为当前选择器附加"id需要以suffix结束"的筛选条件。比较少用。

UiSelector.idMatches(reg)

  • reg {Regex} | {string} id要满足的正则表达式

附加id需要满足正则表达式。

需要注意的是,如果正则表达式是字符串,则需要使用\\来表达\(也即Java正则表达式的形式),例如textMatches("\\d+")匹配多位数字;但如果使用JavaScript语法的正则表达式则不需要,例如textMatches(/\d+/)。但如果使用字符串的正则表达式则该字符串不能以"/"同时以"/"结束,也即不能写诸如textMatches("/\\d+/")的表达式,否则会被开头的"/"和结尾的"/"会被忽略。

idMatches("[a-zA-Z]+")

UiSelector.className(str)

  • str {string} 控件文本
  • 返回 {UiSelector} 返回选择器自身以便链式调用

为当前选择器附加控件"className等于字符串str"的筛选条件。

控件的className(类名)表示一个控件的类别,例如文本控件的类名为android.widget.TextView。

如果一个控件的类名以"android.widget."开头,则可以省略这部分,例如文本控件可以直接用className("TextView")的选择器。

常见控件的类名如下:

  • android.widget.TextView 文本控件
  • android.widget.ImageView 图片控件
  • android.widget.Button 按钮控件
  • android.widget.EditText 输入框控件
  • android.widget.AbsListView 列表控件
  • android.widget.LinearLayout 线性布局
  • android.widget.FrameLayout 帧布局
  • android.widget.RelativeLayout 相对布局
  • android.widget.RelativeLayout 相对布局
  • android.support.v7.widget.RecyclerView 通常也是列表控件

UiSelector.classNameContains(str)

  • str {string} 要包含的字符串

为当前选择器附加控件"className需要包含字符串str"的筛选条件。

UiSelector.classNameStartsWith(prefix)

  • prefix {string} 前缀

为当前选择器附加控件"className需要以prefix开头"的筛选条件。

UiSelector.classNameEndsWith(suffix)

  • suffix {string} 后缀

为当前选择器附加控件"className需要以suffix结束"的筛选条件。

UiSelector.classNameMatches(reg)

  • reg {string} | {Regex} 要满足的正则表达式。

为当前选择器附加控件"className需要满足正则表达式reg"的条件。

有关正则表达式,可以查看正则表达式 - 菜鸟教程

需要注意的是,如果正则表达式是字符串,则需要使用\\来表达\(也即Java正则表达式的形式),例如textMatches("\\d+")匹配多位数字;但如果使用JavaScript语法的正则表达式则不需要,例如textMatches(/\d+/)。但如果使用字符串的正则表达式则该字符串不能以"/"同时以"/"结束,也即不能写诸如textMatches("/\\d+/")的表达式,否则会被开头的"/"和结尾的"/"会被忽略。

UiSelector.packageName(str)

  • str {string} 控件文本
  • 返回 {UiSelector} 返回选择器自身以便链式调用

为当前选择器附加控件"packageName等于字符串str"的筛选条件。

控件的packageName表示控件所属界面的应用包名。例如微信的包名为"com.tencent.mm", 那么微信界面的控件的packageName为"com.tencent.mm"。

要查看一个应用的包名,可以用函数app.getPackageName()获取,例如toast(app.getPackageName("微信"))。

UiSelector.packageNameContains(str)

  • str {string} 要包含的字符串

为当前选择器附加控件"packageName需要包含字符串str"的筛选条件。

UiSelector.packageNameStartsWith(prefix)

  • prefix {string} 前缀

为当前选择器附加控件"packageName需要以prefix开头"的筛选条件。

UiSelector.packageNameEndsWith(suffix)

  • suffix {string} 后缀

为当前选择器附加控件"packageName需要以suffix结束"的筛选条件。

UiSelector.packageNameMatches(reg)

  • reg {string} | {Regex} 要满足的正则表达式。

为当前选择器附加控件"packageName需要满足正则表达式reg"的条件。

有关正则表达式,可以查看正则表达式 - 菜鸟教程

UiSelector.bounds(left, top, right, buttom)

  • left {number} 控件左边缘与屏幕左边的距离
  • top {number} 控件上边缘与屏幕上边的距离
  • right {number} 控件右边缘与屏幕左边的距离
  • bottom {number} 控件下边缘与屏幕上边的距离

一个控件的bounds属性为这个控件在屏幕上显示的范围。我们可以用这个范围来定位这个控件。尽管用这个方法定位控件对于静态页面十分准确,却无法兼容不同分辨率的设备;同时对于列表页面等动态页面无法达到效果,因此使用不推荐该选择器。

注意参数的这四个数字不能随意填写,必须精确的填写控件的四个边界才能找到该控件。例如,要点击QQ主界面的右上角加号,我们用布局分析查看该控件的属性,如下图:

可以看到bounds属性为(951, 67, 1080, 196),此时使用代码bounds(951, 67, 1080, 196).clickable().click()即可点击该控件。

UiSelector.boundsInside(left, top, right, buttom)

  • left {number} 范围左边缘与屏幕左边的距离
  • top {number} 范围上边缘与屏幕上边的距离
  • right {number} 范围右边缘与屏幕左边的距离
  • bottom {number} 范围下边缘与屏幕上边的距离

为当前选择器附加控件"bounds需要在left, top, right, buttom构成的范围里面"的条件。

这个条件用于限制选择器在某一个区域选择控件。例如要在屏幕上半部分寻找文本控件TextView,代码为:

var w = className("TextView").boundsInside(0, 0, device.width, device.height / 2).findOne();

log(w.text());

其中我们使用了device.width来获取屏幕宽度,device.height来获取屏幕高度。

UiSelector.boundsContains(left, top, right, buttom)

  • left {number} 范围左边缘与屏幕左边的距离
  • top {number} 范围上边缘与屏幕上边的距离
  • right {number} 范围右边缘与屏幕左边的距离
  • bottom {number} 范围下边缘与屏幕上边的距离

为当前选择器附加控件"bounds需要包含left, top, right, buttom构成的范围"的条件。

这个条件用于限制控件的范围必须包含所给定的范围。例如给定一个点(500, 300), 寻找在这个点上的可点击控件的代码为:

var w = boundsContains(500, 300, device.width - 500, device.height - 300).clickable().findOne();

w.click();

UiSelector.drawingOrder(order)

  • order {number} 控件在父视图中的绘制顺序

为当前选择器附加控件"drawingOrder等于order"的条件。

drawingOrder为一个控件在父控件中的绘制顺序,通常可以用于区分同一层次的控件。

但该属性在Android 7.0以上才能使用。

UiSelector.clickable([b = true])

  • b {Boolean} 表示控件是否可点击

为当前选择器附加控件是否可点击的条件。但并非所有clickable为false的控件都真的不能点击,这取决于控件的实现。对于自定义控件(例如显示类名为android.view.View的控件)很多的clickable属性都为false都却能点击。

需要注意的是,可以省略参数b而表示选择那些可以点击的控件,例如className("ImageView").clickable()表示可以点击的图片控件的条件,className("ImageView").clickable(false)表示不可点击的图片控件的条件。

UiSelector.longClickable([b = true])

  • b {Boolean} 表示控件是否可长按

为当前选择器附加控件是否可长按的条件。

UiSelector.checkable([b = true])

  • b {Boolean} 表示控件是否可勾选

为当前选择器附加控件是否可勾选的条件。勾选通常是对于勾选框而言的,例如图片多选时左上角通常有一个勾选框。

UiSelector.selected([b = true])

  • b {Boolean} 表示控件是否被选

为当前选择器附加控件是否已选中的条件。被选中指的是,例如QQ聊天界面点击下方的"表情按钮"时,会出现自己收藏的表情,这时"表情按钮"便处于选中状态,其selected属性为true。

UiSelector.enabled([b = true])

  • b {Boolean} 表示控件是否已启用

为当前选择器附加控件是否已启用的条件。大多数控件都是启用的状态(enabled为true),处于“禁用”状态通常是灰色并且不可点击。

UiSelector.scrollable([b = true])

  • b {Boolean} 表示控件是否可滑动

为当前选择器附加控件是否可滑动的条件。滑动包括上下滑动和左右滑动。

可以用这个条件来寻找可滑动控件来滑动界面。例如滑动Auto.js的脚本列表的代码为:

className("android.support.v7.widget.RecyclerView").scrollable().findOne().scrollForward();

//或者classNameEndsWith("RecyclerView").scrollable().findOne().scrollForward();

UiSelector.editable([b = true])

  • b {Boolean} 表示控件是否可编辑

为当前选择器附加控件是否可编辑的条件。一般来说可编辑的控件为输入框(EditText),但不是所有的输入框(EditText)都可编辑。

UiSelector.multiLine([b = true])

  • b {Boolean} 表示文本或输入框控件是否是多行显示的

为当前选择器附加控件是否文本或输入框控件是否是多行显示的条件。

UiSelector.findOne()

  • 返回 UiObject

根据当前的选择器所确定的筛选条件,对屏幕上的控件进行搜索,直到屏幕上出现满足条件的一个控件为止,并返回该控件。如果找不到控件,当屏幕内容发生变化时会重新寻找,直至找到。

需要注意的是,如果屏幕上一直没有出现所描述的控件,则该函数会阻塞,直至所描述的控件出现为止。因此此函数不会返回null。

该函数本来应该命名为untilFindOne(),但由于历史遗留原因已经无法修改。如果想要只在屏幕上搜索一次而不是一直搜索,请使用findOnce()。

另外,如果屏幕上有多个满足条件的控件,findOne()采用深度优先搜索(DFS),会返回该搜索算法找到的第一个控件。注意控件找到的顺序有时会起到作用。

UiSelector.findOne(timeout)

  • timeout {number} 搜索的超时时间,单位毫秒
  • 返回 UiObject

根据当前的选择器所确定的筛选条件,对屏幕上的控件进行搜索,直到屏幕上出现满足条件的一个控件为止,并返回该控件;如果在timeout毫秒的时间内没有找到符合条件的控件,则终止搜索并返回null。

该函数类似于不加参数的findOne(),只不过加上了时间限制。

示例:

//启动Auto.js

launchApp("Auto.js");

//在6秒内找出日志图标的控件

var w = id("action_log").findOne(6000);

//如果找到控件则点击

if(w != null){

    w.click();

}else{

    //否则提示没有找到

    toast("没有找到日志图标");

}

UiSelector.findOnce()

  • 返回 UiObject

根据当前的选择器所确定的筛选条件,对屏幕上的控件进行搜索,如果找到符合条件的控件则返回该控件;否则返回null。

UiSelector.findOnce(i)

  • i {number} 索引

根据当前的选择器所确定的筛选条件,对屏幕上的控件进行搜索,并返回第 i + 1 个符合条件的控件;如果没有找到符合条件的控件,或者符合条件的控件个数 < i, 则返回null。

注意这里的控件次序,是搜索算法深度优先搜索(DSF)决定的。

UiSelector.find()

  • 返回 UiCollection

根据当前的选择器所确定的筛选条件,对屏幕上的控件进行搜索,找到所有满足条件的控件集合并返回。这个搜索只进行一次,并不保证一定会找到,因而会出现返回的控件集合为空的情况。

不同于findOne()或者findOnce()只找到一个控件并返回一个控件,find()函数会找出所有满足条件的控件并返回一个控件集合。之后可以对控件集合进行操作。

可以通过empty()函数判断找到的是否为空。例如:

var c = className("AbsListView").find();

if(c.empty()){

    toast("找到啦");

}else{

    toast("没找到╭(╯^╰)╮");

}

UiSelector.untilFind()

  • 返回 UiCollection

根据当前的选择器所确定的筛选条件,对屏幕上的控件进行搜索,直到找到至少一个满足条件的控件为止,并返回所有满足条件的控件集合。

该函数与find()函数的区别在于,该函数永远不会返回空集合;但是,如果屏幕上一直没有出现满足条件的控件,则该函数会保持阻塞。

UiSelector.exists()

  • 返回 {Boolean}

判断屏幕上是否存在控件符合选择器所确定的条件。例如要判断某个文本出现就执行某个动作,可以用:

if(text("某个文本").exists()){

    //要支持的动作

}

UiSelector.waitFor()

等待屏幕上出现符合条件的控件;在满足该条件的控件出现之前,该函数会一直保持阻塞。

例如要等待包含"哈哈哈"的文本控件出现的代码为:

textContains("哈哈哈").waitFor();

UiSelector.filter(f)

  • f {Function} 过滤函数,参数为UiObject,返回值为boolean

为当前选择器附加自定义的过滤条件。

例如,要找出屏幕上所有文本长度为10的文本控件的代码为:

var uc = className("TextView").filter(function(w){

    return w.text().length == 10;

});

UiObject

UiObject表示一个控件,可以通过这个对象获取到控件的属性,也可以对控件进行点击、长按等操作。

获取一个UiObject通常通过选择器的findOne(), findOnce()等函数,也可以通过UiCollection来获取,或者通过UiObject.child(), UiObject.parent()等函数来获取一个控件的子控件或父控件。

UiObject.click()

  • 返回 {Boolean}

点击该控件,并返回是否点击成功。

如果该函数返回false,可能是该控件不可点击(clickable为false),当前界面无法响应该点击等。

UiObject.longClick()

  • 返回 {Boolean}

长按该控件,并返回是否点击成功。

如果该函数返回false,可能是该控件不可点击(longClickable为false),当前界面无法响应该点击等。

UiObject.setText(text)

  • text {string} 文本
  • 返回 {Boolean}

设置输入框控件的文本内容,并返回是否设置成功。

该函数只对可编辑的输入框(editable为true)有效。

UiObject.copy()

  • 返回 {Boolean}

对输入框文本的选中内容进行复制,并返回是否操作成功。

该函数只能用于输入框控件,并且当前输入框控件有选中的文本。可以通过setSelection()函数来设置输入框选中的内容。

var et = className("EditText").findOne();

//选中前两个字

et.setSelection(0, 2);

//对选中内容进行复制

if(et.copy()){

    toast("复制成功");

}else{

    toast("复制失败");

}

UiObject.cut()

对输入框文本的选中内容进行剪切,并返回是否操作成功。

该函数只能用于输入框控件,并且当前输入框控件有选中的文本。可以通过setSelection()函数来设置输入框选中的内容。

UiObject.paste()

  • 返回 {Boolean}

对输入框控件进行粘贴操作,把剪贴板内容粘贴到输入框中,并返回是否操作成功。

//设置剪贴板内容为“你好”

setClip("你好");

var et = className("EditText").findOne();

et.paste();

UiObject.setSelection(start, end)

  • start {number} 选中内容起始位置
  • end {number} 选中内容结束位置(不包括)
  • 返回 {Boolean}

对输入框控件设置选中的文字内容,并返回是否操作成功。

索引是从0开始计算的;并且,选中内容不包含end位置的字符。例如,如果一个输入框内容为"123456789",要选中"4567"的文字的代码为et.setSelection(3, 7)。

该函数也可以用来设置光标位置,只要参数的end等于start,即可把输入框光标设置在start的位置。例如et.setSelection(1, 1)会把光标设置在第一个字符的后面。

UiObject.scrollForward()

  • 返回 {Boolean}

对控件执行向前滑动的操作,并返回是否操作成功。

向前滑动包括了向右和向下滑动。如果一个控件既可以向右滑动和向下滑动,那么执行scrollForward()的行为是未知的(这是因为Android文档没有指出这一点,同时也没有充分的测试可供参考)。

UiObject.scrollBackward()

  • 返回 {Boolean}

对控件执行向后滑动的操作,并返回是否操作成功。

向后滑动包括了向右和向下滑动。如果一个控件既可以向右滑动和向下滑动,那么执行scrollForward()的行为是未知的(这是因为Android文档没有指出这一点,同时也没有充分的测试可供参考)。

UiObject.select()

  • 返回 {Boolean}

对控件执行"选中"操作,并返回是否操作成功。"选中"和isSelected()的属性相关,但该操作十分少用。

UiObject.collapse()

  • 返回 {Boolean}

对控件执行折叠操作,并返回是否操作成功。

UiObject.expand()

  • 返回 {Boolean}

对控件执行操作,并返回是否操作成功。

UiObject.show()

对集合中所有控件执行显示操作,并返回是否全部操作成功。

UiObject.scrollUp()

对集合中所有控件执行向上滑的操作,并返回是否全部操作成功。

UiObject.scrollDown()

对集合中所有控件执行向下滑的操作,并返回是否全部操作成功。

UiObject.scrollLeft()

对集合中所有控件执行向左滑的操作,并返回是否全部操作成功。

UiObject.scrollRight()

children()

  • 返回 UiCollection

返回该控件的所有子控件组成的控件集合。可以用于遍历一个控件的子控件,例如:

className("AbsListView").findOne().children()

    .forEach(function(child){

        log(child.className());

    });

childCount()

  • 返回 {number}

返回子控件数目。

child(i)

  • i {number} 子控件索引
  • 返回 {UiObject}

返回第i+1个子控件。如果i>=控件数目或者小于0,则抛出异常。

需要注意的是,由于布局捕捉的问题,该函数可能返回null,也就是可能获取不到某个子控件。

遍历子控件的示例:

var list = className("AbsListView").findOne();

for(var i = 0; i < list.childCount(); i++){

    var child = list.child(i);

    log(child.className());

}

parent()

  • 返回 {UiObject}

返回该控件的父控件。如果该控件没有父控件,返回null。

bounds()

  • 返回 Rect

返回控件在屏幕上的范围,其值是一个Rect对象。

示例:

var b = text("Auto.js").findOne().bounds();

toast("控件在屏幕上的范围为" + b);

如果一个控件本身无法通过click()点击,那么我们可以利用bounds()函数获取其坐标,再利用坐标点击。例如:

var b = desc("打开侧拉菜单").findOne().bounds();

click(b.centerX(), b.centerY());

//如果使用root权限,则用 Tap(b.centerX(), b.centerY());

boundsInParent()

  • 返回 Rect

返回控件在父控件中的范围,其值是一个Rect对象。

drawingOrder()

  • 返回 {number}

返回控件在父控件中的绘制次序。该函数在安卓7.0及以上才有效,7.0以下版本调用会返回0。

id()

  • 返回 {string}

获取控件的id,如果一个控件没有id,则返回null。

text()

  • 返回 {string}

获取控件的文本,如果控件没有文本,返回""。

findByText(str)

  • str {string} 文本
  • 返回 UiCollection

根据文本text在子控件中递归地寻找并返回文本或描述(desc)包含这段文本str的控件,返回它们组成的集合。

该函数会在当前控件的子控件,孙控件,曾孙控件...中搜索text或desc包含str的控件,并返回它们组合的集合。

findOne(selector)

  • selector UiSelector
  • 返回 UiOobject

根据选择器selector在该控件的子控件、孙控件...中搜索符合该选择器条件的控件,并返回找到的第一个控件;如果没有找到符合条件的控件则返回null。

例如,对于酷安动态列表,我们可以遍历他的子控件(每个动态列表项),并在每个子控件中依次寻找点赞数量和图标,对于点赞数量小于10的点赞:

//找出动态列表

var list = id("recycler_view").findOne();

//遍历动态

list.children().forEach(function(child){

    //找出点赞图标

    var like = child.findOne(id("feed_action_view_like"));

    //找出点赞数量

    var likeCount = child.findOne(id("text_view"));

    //如果这两个控件没有找到就不继续了

    if(like == null || likeCount == null){

        return;

    }

    //判断点赞数量是否小于10

    if(parseInt(likeCount.text()) < 10){

        //点赞

        like.click();

    }

});

find(selector)

  • selector UiSelector
  • 返回 UiCollection

根据选择器selector在该控件的子控件、孙控件...中搜索符合该选择器条件的控件,并返回它们组合的集合。

UiCollection

UiCollection, 控件集合, 通过选择器的find(), untilFind()方法返回的对象。

UiCollection"继承"于数组,实际上是一个UiObject的数组,因此可以使用数组的函数和属性,例如使用length属性获取UiCollection的大小,使用forEach函数来遍历UiCollection。

例如,采用forEach遍历屏幕上所有的文本控件并打印出文本内容的代码为:

console.show();

className("TextView").find().forEach(function(tv){

    if(tv.text() != ""){

        log(tv.text());

    }

});

也可以使用传统的数组遍历方式:

console.show();

var uc = className("TextView").find();

for(var i = 0; i < uc.length; i++){

    var tv = uc[i];

    if(tv.text() != ""){

        log(tv.text());

    }

}

UiCollection的每一个元素都是UiObject,我们可以取出他的元素进行操作,例如取出第一个UiObject并点击的代码为ui[0].click()。如果想要对该集合的所有元素进行操作,可以直接在集合上调用相应的函数,例如uc.click(),该代码会对集合上所有UiObject执行点击操作并返回是否全部点击成功。

因此,UiCollection具有所有UiObject对控件操作的函数,包括click(), longClick(), scrollForward()等等,不再赘述。

UiCollection.size()

  • 返回 {number}

返回集合中的控件数。

历史遗留函数,相当于属性length。

UiCollection.get(i)

  • i {number} 索引
  • 返回 UiObject

返回集合中第i+1个控件(UiObject)。

历史遗留函数,建议直接使用数组下标的方式访问元素。

UiCollection.each(func)

  • func {Function} 遍历函数,参数为UiObject。

遍历集合。

历史遗留函数,相当于forEach。参考forEach

empty()

  • 返回 {Boolean}

返回控件集合是否为空。

nonEmpty()

  • 返回 {Boolean}

返回控件集合是否非空。

UiCollection.find(selector)

  • selector UiSelector
  • 返回 UiCollection

根据selector所确定的条件在该控件集合的控件、子控件、孙控件...中找到所有符合条件的控件并返回找到的控件集合。

注意这会递归地遍历控件集合里所有的控件以及他们的子控件。和数组的filter函数不同。

例如:

var names = id("name").find();

//在集合

var clickableNames = names.find(clickable());

UiCollection.findOne(selector)

  • selector UiSelector
  • 返回 UiOobject

根据选择器selector在该控件集合的控件的子控件、孙控件...中搜索符合该选择器条件的控件,并返回找到的第一个控件;如果没有找到符合条件的控件则返回null。

Rect

UiObject.bounds(), UiObject.boundsInParent()返回的对象。表示一个长方形(范围)。

Rect.left

  • {number}

长方形左边界的x坐标、

Rect.right

  • {number}

长方形右边界的x坐标、

Rect.top

  • {number}

长方形上边界的y坐标、

Rect.bottom

  • {number}

长方形下边界的y坐标、

Rect.centerX()

  • 返回 {number}

长方形中点x坐标。

Rect.centerY()

  • 返回 {number}

长方形中点y坐标。

Rect.width()

  • 返回 {number}

长方形宽度。通常可以作为控件宽度。

Rect.height()

  • 返回 {number}

长方形高度。通常可以作为控件高度。

Rect.contains(r)

  • Rect

返回是否包含另一个长方形r。包含指的是,长方形r在该长方形的里面(包含边界重叠的情况)。

Rect.intersect(r)

  • Rect

返回是否和另一个长方形相交。

UiSelector进阶

未完待续。

Sensors

Stability: 2 - Stable

sensors模块提供了获取手机上的传感器的信息的支持,这些传感器包括距离传感器、光线光感器、重力传感器、方向传感器等。需要指出的是,脚本只能获取传感器的数据,不能模拟或伪造传感器的数据和事件,因此诸如模拟摇一摇的功能是无法实现的。

要监听一个传感器时,需要使用sensors.register()注册监听器,之后才能开始监听;不需要监听时则调用sensors.unregister()注销监听器。在脚本结束时会自动注销所有的监听器。同时,这种监听会使脚本保持运行状态,如果不注销监听器,脚本会一直保持运行状态。

例如,监听光线传感器的代码为:

//光线传感器监听

sensors.register("light").on("change", (event, light)=>{

    log("当前光强度为", light);

});

要注意的是,每个传感器的数据并不相同,所以对他们调用on()监听事件时的回调函数参数也不是相同,例如光线传感器参数为(event, light),加速度传感器参数为(event, ax, ay, az)。甚至在某些设备上的传感器参数有所增加,例如华为手机的距离传感器为三个参数,一般手机只有一个参数。

常用的传感器及其事件参数如下表:

  • accelerometer 加速度传感器,参数(event, ax, ay, az):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • ax {number} x轴上的加速度,单位m/s^2
    • ay {number} y轴上的加速度,单位m/s^2
    • az {number} z轴上的加速度,单位m/s^2 这里的x轴,y轴,z轴所属的坐标系统如下图(其中z轴垂直于设备屏幕表面):

!

  • orientation 方向传感器,参数(event, azimuth, pitch, roll):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • azimuth {number} 方位角,从地磁指北方向线起,依顺时针方向到y轴之间的水平夹角,单位角度,范围0~359
    • pitch {number} 绕x轴旋转的角度,当设备水平放置时该值为0,当设备顶部翘起时该值为正数,当设备尾部翘起时该值为负数,单位角度,范围-180~180
    • roll {number} 绕y轴顺时针旋转的角度,单位角度,范围-90~90
  • gyroscope 陀螺仪传感器,参数(event, wx, wy, wz):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • wx {number} 绕x轴的角速度,单位弧度/s
    • wy {number} 绕y轴的角速度,单位弧度/s
    • wz {number} 绕z轴的角速度,单位弧度/s
  • magnetic_field 磁场传感器,参数(event, bx, by, bz):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • bx {number} x轴上的磁场强度,单位uT
    • by {number} y轴上的磁场强度,单位uT
    • bz {number} z轴上的磁场强度,单位uT
  • gravity 重力传感器,参数(event, gx, gy, gz):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • gx {number} x轴上的重力加速度,单位m/s^2
    • gy {number} y轴上的重力加速度,单位m/s^2
    • gz {number} z轴上的重力加速度,单位m/s^2
  • linear_acceleration 线性加速度传感器,参数(event, ax, ay, az):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • ax {number} x轴上的线性加速度,单位m/s^2
    • ay {number} y轴上的线性加速度,单位m/s^2
    • az {number} z轴上的线性加速度,单位m/s^2
  • ambient_temperature 环境温度传感器,大部分设备并不支持,参数(event, t):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • t {number} 环境温度,单位摄氏度。
  • light 光线传感器,参数(event, light):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • light {number} 环境光强度,单位lux
  • pressure 压力传感器,参数(event, p):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • p {number} 大气压,单位hPa
  • proximity 距离传感器,参数(event, distance):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • distance {number} 一般指设备前置摄像头旁边的距离传感器到前方障碍物的距离,并且很多设备上这个值只有两种情况:当障碍物较近时该值为0,当障碍物较远或在范围内没有障碍物时该值为5
  • relative_humidity 湿度传感器,大部分设备并不支持,参数(event, rh):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • rh {number} 相对湿度,范围为0~100(百分比)

sensors.register(sensorName[, delay])

  • sensorName {string} 传感器名称,常用的传感器名称如上面所述
  • delay {number} 传感器数据更新频率,可选,默认为sensors.delay.normal。可用的值如下:
    • sensors.delay.normal 正常频率
    • sensors.delay.ui 适合于用户界面的更新频率
    • sensors.delay.game 适合于游戏的更新频率
    • sensors.delay.fastest 最快的更新频率】
  • 返回 SensorEventEmiiter

注册一个传感器监听并返回SensorEventEmitter

例如:

console.show();

//注册传感器监听

var sensor = sensors.register("gravity");

if(sensor == null){

    toast("不支持重力传感器");

    exit();

}

//监听数据

sensor.on("change", (gx, gy, gz)=>{

    log("重力加速度: %d, %d, %d", gx, gy, gz);

});

可以通过delay参数来指定传感器数据的更新频率,例如:

var sensor = sensors.register("gravity", sensors.delay.game);

另外,如果不支持sensorName所指定的传感器,那么该函数将返回null;但如果sensors.ignoresUnsupportedSensor的值被设置为true, 则该函数会返回一个不会分发任何传感器事件的SensorEventEmitter

例如:

sensors.ignoresUnsupportedSensor = true;

//无需null判断

sensors.register("gravity").on("change", (gx, gy, gz)=>{

    log("重力加速度: %d, %d, %d", gx, gy, gz);

});

更多信息,参见SensorEventEmittersensors.ignoresUnsupportedSensor

sensors.unregister(emitter)

  • emiiter SensorEventEmitter

注销该传感器监听器。被注销的监听器将不再能监听传感器数据。

//注册一个传感器监听器

var sensor = sensors.register("gravity");

if(sensor == null){

    exit();

}

//2秒后注销该监听器

setTimeout(()=> {

    sensors.unregister(sensor);

}, 2000);

sensors.unregisterAll()

注销所有传感器监听器。

sensors.ignoresUnsupportedSensor

  • {boolean}

表示是否忽略不支持的传感器。如果该值被设置为true,则函数sensors.register()即使对不支持的传感器也会返回一个无任何数据的虚拟传感器监听,也就是sensors.register()不会返回null从而避免非空判断,并且此时会触发sensors的"unsupported_sensor"事件。

//忽略不支持的传感器

sensors.ignoresUnsupportedSensor = true;

//监听有不支持的传感器时的事件

sensors.on("unsupported_sensor", function(sensorName){

    toastLog("不支持的传感器: " + sensorName);

});

//随便注册一个不存在的传感器。

log(sensors.register("aaabbb"));

事件: 'unsupported_sensor'

  • sensorName {string} 不支持的传感器名称

当sensors.ignoresUnsupportedSensor被设置为true并且有不支持的传感器被注册时触发该事件。事件参数的传感器名称。

SensorEventEmitter

注册传感器返回的对象,其本身是一个EventEmmiter,用于监听传感器事件。

事件: 'change'

  • ..args {Any} 传感器参数

当传感器数据改变时触发该事件;该事件触发的最高频繁由sensors.register()指定的delay参数决定。

事件参数根据传感器类型不同而不同,具体参见本章最前面的列表。

一个监听光线传感器和加速度传感器并且每0.5秒获取一个数据并最终写入一个csv表格文件的例子如下:

//csv文件路径

cosnt csvPath = "/sdcard/sensors_data.csv";

//记录光线传感器的数据

var light = 0;

//记录加速度传感器的数据

var ax = 0;

var ay = 0;

var az = 0;

//监听光线传感器

sensors.register("light", sensors.delay.fastest)

    .on("change", l => {

        light = l;

    });

//监听加速度传感器

sensors.register("accelerometer", sensors.delay.fastest)

    .on("change", (ax0, ay0, az0) => {

        ax = ax0;

        ay = ay0;

        az = az0;

    });

 

var file = open(csvPath, "w");

//写csv表格头

file.writeline("light,ax,ay,az")

//每0.5秒获取一次数据并写入文件

setInterval(()=>{

    file.writeline(util.format("%d,%d,%d,%d", light, ax, ay, az));

}, 500);

//10秒后退出并打开文件

setTimeout(()=>{

    file.close();

    sensors.unregsiterAll();

    app.viewFile(csvPath);

}, 10 * 1000);

事件: 'accuracy_change'

  • accuracy {number} 表示传感器精度。为以下值之一:
    • -1 传感器未连接
    • 0 传感器不可读
    • 1 低精度
    • 2 中精度
    • 3 高精度

当传感器精度改变时会触发的事件。比较少用。

shell即Unix Shell,在类Unix系统提供与操作系统交互的一系列命令。

很多程序可以用来执行shell命令,例如终端模拟器。

在Auto.js大致等同于用adb执行命令"adb shell"。其实现包括两种方式:

  • 通过java.lang.Runtime.exec执行(shell, Tap, Home等函数)
  • 通过内嵌终端模拟器执行(RootAutomator, Shell等对象)

shell函数

Stability: 2 - Stable

shell(cmd[, root])

  • cmd {string} 要执行的命令
  • root {Boolean} 是否以root权限运行,默认为false。

一次性执行命令cmd, 并返回命令的执行结果。返回对象的其属性如下:

  • code {number} 返回码。执行成功时为0,失败时为非0的数字。
  • result {string} 运行结果(stdout输出结果)
  • error {string} 运行的错误信息(stderr输出结果)。例如执行需要root权限的命令但没有授予root权限会返回错误信息"Permission denied"。

示例(强制停止微信) :

var result = shell("am force-stop com.tencent.mm", true);

log(result);

console.show();

if(result.code == 0){

  toast("执行成功");

}else{

  toast("执行失败!请到控制台查看错误信息");

}

Shell

Stability: 2 - Stable

shell函数通过用来一次性执行单条命令并获取结果。如果有多条命令需要执行,用Shell对象的效率更高。这是因为,每次运行shell函数都会打开一个单独的shell进程并在运行结束后关闭他,这个过程需要一定的时间;而Shell对象自始至终使用同一个shell进程。

new Shell(root)

  • root {Boolean} 是否以root权限运行一个shell进程,默认为false。这将会影响其后使用该Shell对象执行的命令的权限

Shell对象的"构造函数"。

var sh = new Shell(true);

//强制停止微信

sh.exec("am force-stop com.tencent.mm");

sh.exit();

Shell.exec(cmd)

  • cmd {string} 要执行的命令

执行命令cmd。该函数不会返回任何值。

注意,命令执行是"异步"的、非阻塞的。也就是不会等待命令完成后才继续向下执行。

尽管这样的设计使用起来有很多不便之处,但受限于终端模拟器,暂时没有解决方式;如果后续能找到解决方案,则将提供Shell.execAndWaitFor函数。

Shell.exit()

直接退出shell。正在执行的命令会被强制退出。

Shell.exitAndWaitFor()

执行"exit"命令并等待执行命令执行完成、退出shell。

此函数会执行exit命令来正常退出shell。

Shell.setCallback(callback)

  • callback {Object} 回调函数

设置该Shell的回调函数,以便监听Shell的输出。可以包括以下属性:

  • onOutput {Function} 每当shell有新的输出时便会调用该函数。其参数是一个字符串。
  • onNewLine {Function} 每当shell有新的一行输出时便会调用该函数。其参数是一个字符串(不包括最后的换行符)。

例如:

var sh = new Shell();

sh.setCallback({

    onNewLine: function(line){

        //有新的一行输出时打印到控制台

        log(line);

    }

})

while(true){

    //循环输入命令

    var cmd = dialogs.rawInput("请输入要执行的命令,输入exit退出");

    if(cmd == "exit"){

        break;

    }

    //执行命令

    sh.exec(cmd);

}

sh.exit();

附录: shell命令简介

以下关于shell命令的资料来自AndroidStudio用户指南:Shell命令

am命令

am命令即Activity Manager命令,用于管理应用程序活动、服务等。

以下命令均以"am "开头,例如shell('am start -p com.tencent.mm');(启动微信)

start [options] intent

启动 intent 指定的 Activity(应用程序活动)。
请参阅 intent 参数的规范

选项包括:

  • -D:启用调试。
  • -W:等待启动完成。
  • --start-profiler file:启动分析器并将结果发送到 file。
  • -P file:类似于 --start-profiler,但当应用进入空闲状态时分析停止。
  • -R count:重复 Activity 启动 count 次数。在每次重复前,将完成顶部 Activity。
  • -S:启动 Activity 前强行停止目标应用。
  • --opengl-trace:启用 OpenGL 函数的跟踪。
  • --user user_id | current:指定要作为哪个用户运行;如果未指定,则作为当前用户运行。

startservice [options] intent

启动 intent 指定的 Service(服务)。
请参阅 intent 参数的规范
选项包括:

  • --user user_id | current:指定要作为哪个用户运行;如果未指定,则作为当前用户运行。

force-stop package

强行停止与 package(应用包名)关联的所有应用。

kill [options] package

终止与 package(应用包名)关联的所有进程。此命令仅终止可安全终止且不会影响用户体验的进程。
选项包括:

  • --user user_id | all | current:指定将终止其进程的用户;如果未指定,则终止所有用户的进程。

kill-all

终止所有后台进程。

broadcast [options] intent

发出广播 intent。 请参阅 intent 参数的规范

选项包括:

  • [--user user_id | all | current]:指定要发送到的用户;如果未指定,则发送到所有用户。

instrument [options] component

使用 Instrumentation 实例启动监控。通常,目标 component 是表单 test_package/runner_class。
选项包括:

  • -r:输出原始结果(否则对 report_key_streamresult 进行解码)。与 [-e perf true] 结合使用以生成性能测量的原始输出。
  • -e name value:将参数 name 设为 value。对于测试运行器,通用表单为 -e testrunner_flag value[,value...]。
  • -p file:将分析数据写入 file。
  • -w:先等待仪器完成,然后再返回。测试运行器需要使用此选项。
  • --no-window-animation:运行时关闭窗口动画。
  • --user user_id | current:指定仪器在哪个用户中运行;如果未指定,则在当前用户中运行。
  • profile start process file 启动 process 的分析器,将结果写入 file。
  • profile stop process 停止 process 的分析器。

dumpheap [options] process file

转储 process 的堆,写入 file。

选项包括:

  • --user [user_id|current]:提供进程名称时,指定要转储的进程用户;如果未指定,则使用当前用户。
  • -n:转储原生堆,而非托管堆。
  • set-debug-app [options] package 将应用 package 设为调试。

选项包括:

  • -w:应用启动时等待调试程序。
  • --persistent:保留此值。
  • clear-debug-app 使用 set-debug-app 清除以前针对调试用途设置的软件包。

monitor [options] 启动对崩溃或 ANR 的监控。

选项包括:

  • --gdb:在崩溃/ANR 时在给定端口上启动 gdbserv。

screen-compat {on|off} package

控制 package 的屏幕兼容性模式。

display-size [reset|widthxheight]

替换模拟器/设备显示尺寸。此命令对于在不同尺寸的屏幕上测试您的应用非常有用,它支持使用大屏设备模仿小屏幕分辨率(反之亦然)。
示例:

shell("am display-size 1280x800", true);

display-density dpi

替换模拟器/设备显示密度。此命令对于在不同密度的屏幕上测试您的应用非常有用,它支持使用低密度屏幕在高密度环境环境上进行测试(反之亦然)。
示例:

shell("am display-density 480", true);

to-uri intent

将给定的 intent 规范以 URI 的形式输出。 请参阅 intent 参数的规范

to-intent-uri intent

将给定的 intent 规范以 intent:URI 的形式输出。 请参阅 intent 参数的规范。

intent参数的规范

对于采用 intent 参数的 am 命令,您可以使用以下选项指定 intent:

  • -a action
    指定 intent 操作,如“android.intent.action.VIEW”。此指定只能声明一次。
  • -d data_uri
    指定 intent 数据 URI,如“content://contacts/people/1”。此指定只能声明一次。
  • -t mime_type
    指定 intent MIME 类型,如“image/png”。此指定只能声明一次。
  • -c category
    指定 intent 类别,如“android.intent.category.APP_CONTACTS”。
  • -n component
    指定带有软件包名称前缀的组件名称以创建显式 intent,如“com.example.app/.ExampleActivity”。
  • -f flags
    将标志添加到 setFlags() 支持的 intent。
  • --esn extra_key
    添加一个 null extra。URI intent 不支持此选项。
  • -e|--es extra_key extra_string_value
    添加字符串数据作为键值对。
  • --ez extra_key extra_boolean_value
    添加布尔型数据作为键值对。
  • --ei extra_key extra_int_value
    添加整数型数据作为键值对。
  • --el extra_key extra_long_value
    添加长整型数据作为键值对。
  • --ef extra_key extra_float_value
    添加浮点型数据作为键值对。
  • --eu extra_key extra_uri_value
    添加 URI 数据作为键值对。
  • --ecn extra_key extra_component_name_value
    添加组件名称,将其作为 ComponentName 对象进行转换和传递。
  • --eia extra_key extra_int_value[,extra_int_value...]
    添加整数数组。
  • --ela extra_key extra_long_value[,extra_long_value...]
    添加长整型数组。
  • --efa extra_key extra_float_value[,extra_float_value...]
    添加浮点型数组。
  • --grant-read-uri-permission
    包含标志 FLAG_GRANT_READ_URI_PERMISSION。
  • --grant-write-uri-permission
    包含标志 FLAG_GRANT_WRITE_URI_PERMISSION。
  • --debug-log-resolution
    包含标志 FLAG_DEBUG_LOG_RESOLUTION。
  • --exclude-stopped-packages
    包含标志 FLAG_EXCLUDE_STOPPED_PACKAGES。
  • --include-stopped-packages
    包含标志 FLAG_INCLUDE_STOPPED_PACKAGES。
  • --activity-brought-to-front
    包含标志 FLAG_ACTIVITY_BROUGHT_TO_FRONT。
  • --activity-clear-top
    包含标志 FLAG_ACTIVITY_CLEAR_TOP。
  • --activity-clear-when-task-reset
    包含标志 FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET。
  • --activity-exclude-from-recents
    包含标志 FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS。
  • --activity-launched-from-history
    包含标志 FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY。
  • --activity-multiple-task
    包含标志 FLAG_ACTIVITY_MULTIPLE_TASK。
  • --activity-no-animation
    包含标志 FLAG_ACTIVITY_NO_ANIMATION。
  • --activity-no-history
    包含标志 FLAG_ACTIVITY_NO_HISTORY。
  • --activity-no-user-action
    包含标志 FLAG_ACTIVITY_NO_USER_ACTION。
  • --activity-previous-is-top
    包含标志 FLAG_ACTIVITY_PREVIOUS_IS_TOP。
  • --activity-reorder-to-front
    包含标志 FLAG_ACTIVITY_REORDER_TO_FRONT。
  • --activity-reset-task-if-needed
    包含标志 FLAG_ACTIVITY_RESET_TASK_IF_NEEDED。
  • --activity-single-top
    包含标志 FLAG_ACTIVITY_SINGLE_TOP。
  • --activity-clear-task
    包含标志 FLAG_ACTIVITY_CLEAR_TASK。
  • --activity-task-on-home
    包含标志 FLAG_ACTIVITY_TASK_ON_HOME。
  • --receiver-registered-only
    包含标志 FLAG_RECEIVER_REGISTERED_ONLY。
  • --receiver-replace-pending
    包含标志 FLAG_RECEIVER_REPLACE_PENDING。
  • --selector
    需要使用 -d 和 -t 选项以设置 intent 数据和类型。

URI component package

如果不受上述某一选项的限制,您可以直接指定 URI、软件包名称和组件名称。当参数不受限制时,如果参数包含一个“:”(冒号),则此工具假定参数是一个 URI;如果参数包含一个“/”(正斜杠),则此工具假定参数是一个组件名称;否则,此工具假定参数是一个软件包名称。

应用包名

所谓应用包名,是唯一确定应用的标识。例如微信的包名是"com.tencent.mm", QQ的包名是"com.tencent.mobileqq"。
要获取一个应用的包名,可以通过函数getPackageName(appName)获取。参见帮助->其他一般函数。

pm命令

pm命令用于管理应用程序,例如卸载应用、冻结应用等。
以下命令均以"pm "开头,例如"shell(\"pm disable com.tencent.mm\");"(冻结微信)

list packages [options] filter

输出所有软件包,或者,仅输出包名称包含 filter 中的文本的软件包。
选项:

  • -f:查看它们的关联文件。
  • -d:进行过滤以仅显示已停用的软件包。
  • -e:进行过滤以仅显示已启用的软件包。
  • -s:进行过滤以仅显示系统软件包。
  • -3:进行过滤以仅显示第三方软件包。
  • -i:查看软件包的安装程序。
  • -u:也包括卸载的软件包。
  • --user user_id:要查询的用户空间。

list permission-groups

输出所有已知的权限组。

list permissions [options] group

输出所有已知权限,或者,仅输出 group 中的权限。
选项:

  • -g:按组加以组织。
  • -f:输出所有信息。
  • -s:简短摘要。
  • -d:仅列出危险权限。
  • -u:仅列出用户将看到的权限。

list instrumentation [options]

列出所有测试软件包。
选项:

  • -f:列出用于测试软件包的 APK 文件。
  • target_package:列出仅用于此应用的测试软件包。

list features

输出系统的所有功能。

list libraries

输出当前设备支持的所有库。

list users

输出系统上的所有用户。

path package

输出给定 package 的 APK 的路径。

install [options] path

将软件包(通过 path 指定)安装到系统。
选项:

  • -l:安装具有转发锁定功能的软件包。
  • -r:重新安装现有应用,保留其数据。
  • -t:允许安装测试 APK。
  • -i installer_package_name:指定安装程序软件包名称。
  • -s:在共享的大容量存储(如 sdcard)上安装软件包。
  • -f:在内部系统内存上安装软件包。
  • -d:允许版本代码降级。
  • -g:授予应用清单文件中列出的所有权限。

uninstall [options] package

从系统中卸载软件包。
选项:

  • -k:移除软件包后保留数据和缓存目录。

clear package

删除与软件包关联的所有数据。

enable package_or_component

启用给定软件包或组件(作为“package/class”写入)。

disable package_or_component

停用给定软件包或组件(作为“package/class”写入)。

disable-user [options] package_or_component

选项:

  • --user user_id:要停用的用户。

grant package_name permission

向应用授予权限。在运行 Android 6.0(API 级别 23)及更高版本的设备上,可以是应用清单中声明的任何权限。在运行 Android 5.1(API 级别 22)和更低版本的设备上,必须是应用定义的可选权限。

revoke package_name permission

从应用中撤销权限。在运行 Android 6.0(API 级别 23)及更高版本的设备上,可以是应用清单中声明的任何权限。在运行 Android 5.1(API 级别 22)和更低版本的设备上,必须是应用定义的可选权限。

set-install-location location

更改默认安装位置。位置值:

  • 0:自动—让系统决定最佳位置。
  • 1:内部—安装在内部设备存储上。
  • 2:外部—安装在外部介质上。

注:此命令仅用于调试目的;使用此命令会导致应用中断和其他意外行为。

get-install-location

返回当前安装位置。返回值:

  • 0 [auto]:让系统决定最佳位置。
  • 1 [internal]:安装在内部设备存储上
  • 2 [external]:安装在外部介质上

set-permission-enforced permission [true|false]

指定是否应强制执行给定的权限。

trim-caches desired_free_space

减少缓存文件以达到给定的可用空间。

create-user user_name

使用给定的 user_name 创建新用户,输出新用户的标识符。

remove-user user_id

移除具有给定的 user_id 的用户,删除与该用户关联的所有数据。

get-max-users

输出设备支持的最大用户数。

其他命令

进行屏幕截图

screencap 命令是一个用于对设备显示屏进行屏幕截图的 shell 实用程序。在 shell 中,此语法为:

screencap filename

例如:

$ shell("screencap /sdcard/screen.png");

列表文件

ls filepath

例如:

log(shell("ls /system/bin").result);

Storages

Stability: 2 - Stable

 

storages模块提供了保存简单数据、用户配置等的支持。保存的数据除非应用被卸载或者被主动删除,否则会一直保留。

 

storages支持number, boolean, string等数据类型以及把Object, Array用JSON.stringify序列化存取。

 

storages保存的数据在脚本之间是共享的,任何脚本只要知道storage名称便可以获取到相应的数据,因此它不能用于敏感数据的储存。 storages无法像Web开发中LocalStorage一样提供根据域名独立的存储,因为脚本的路径随时可能改变。

 

storages.create(name)

name {string} 本地存储名称

创建一个本地存储并返回一个Storage对象。不同名称的本地存储的数据是隔开的,而相同名称的本地存储的数据是共享的。

 

例如在一个脚本中,创建名称为ABC的存储并存入a=123:

 

var storage = storages.create("ABC");

storage.put("a", 123);

而在另一个脚本中是可以获取到ABC以及a的值的:

 

var storage = storages.create("ABC");

log("a = " + storage.get("a"));

因此,本地存储的名称比较重要,尽量使用含有域名、作者邮箱等唯一信息的名称来避免冲突,例如:

 

var storage = storages.create("2732014414@qq.com:ABC");

storages.remove(name)

name {string} 本地存储名称

删除一个本地存储以及他的全部数据。如果该存储不存在,返回false;否则返回true。

 

Storages

Storage.get(key[, defaultValue])

key {string} 键值

defaultValue {any} 可选,默认值

从本地存储中取出键值为key的数据并返回。

 

如果该存储中不包含该数据,这时若指定了默认值参数则返回默认值,否则返回undefined。

 

返回的数据可能是任意数据类型,这取决于使用Storage.put保存该键值的数据时的数据类型。

 

Storage.put(key, value)

key {string} 键值

value {any} 值

把值value保存到本地存储中。value可以是undefined以外的任意数据类型。如果value为undefined则抛出TypeError。

 

存储的过程实际上是使用JSON.stringify把value转换为字符串再保存,因此value必须是可JSON化的才能被接受。

 

Storage.remove(key)

key {string} 键值

移除键值为key的数据。不返回任何值。

 

Storage.contains(key)

key {string} 键值

返回该本地存储是否包含键值为key的数据。是则返回true,否则返回false。

 

Storage.clear()

移除该本地存储的所有数据。不返回任何值。

Threads

Stability: 1 - Experiment

threads模块提供了多线程支持,可以启动新线程来运行脚本。

脚本主线程会等待所有子线程执行完成后才停止执行,因此如果子线程中有死循环,请在必要的时候调用exit()来直接停止脚本或threads.shutDownAll()来停止所有子线程。

通过threads.start()启动的所有线程会在脚本被强制停止时自动停止。

由于JavaScript自身没有多线程的支持,因此您可能会遇到意料之外的问题。

threads.start(action)

  • action {Function} 要在新线程执行的函数
  • 返回 Thread

启动一个新线程并执行action。

例如:

threads.start(function(){

    //在新线程执行的代码

    while(true){

        log("子线程");

    }

});

while(true){

    log("脚本主线程");

}

通过该函数返回的Thread对象可以获取该线程的状态,控制该线程的运行中。例如:

var thread = threads.start(function(){

    while(true){

        log("子线程");

    }

});

//停止线程执行

thread.interrupt();

更多信息参见Thread

threads.shutDownAll()

停止所有通过threads.start()启动的子线程。

threads.currentThread()

  • 返回 Thread

返回当前线程。

threads.disposable()

  • 返回 Disposable

新建一个Disposable对象,用于等待另一个线程的某个一次性结果。更多信息参见线程通信以及Disposable

threads.atomic([initialValue])

  • initialValue {number} 初始整数值,默认为0
  • 返回AtomicLong

新建一个整数原子变量。更多信息参见线程安全以及AtomicLong

threads.lock()

  • 返回ReentrantLock

新建一个可重入锁。更多信息参见线程安全以及ReentrantLock

Thread

线程对象,threads.start()返回的对象,用于获取和控制线程的状态,与其他线程交互等。

Thread对象提供了和timers模块一样的API,例如setTimeout(), setInterval()等,用于在该线程执行相应的定时回调,从而使线程之间可以直接交互。例如:

var thread = threads.start(function(){

    //在子线程执行的定时器

    setInterval(function(){

        log("子线程:" + threads.currentThread());

    }, 1000);

});

 

log("当前线程为主线程:" + threads.currentThread());

 

//等待子线程启动

thread.waitFor();

//在子线程执行的定时器

thread.setTimeout(function(){

    //这段代码会在子线程执行

    log("当前线程为子线程:" + threads.currentThread());

}, 2000);

 

sleep(30 * 1000);

thread.interrupt();

Thread.interrupt()

中断线程运行。

Thread.join([timeout])

  • timeout {number} 等待时间,单位毫秒

等待线程执行完成。如果timeout为0,则会一直等待直至该线程执行完成;否则最多等待timeout毫秒的时间。

例如:

var sum = 0;

//启动子线程计算1加到10000

var thread = threads.start(function(){

    for(var i = 0; i < 10000; i++){

        sum += i;

    }

});

//等待该线程完成

thread.join();

toast("sum = " + sum);

isAlive()

  • 返回 {boolean}

返回线程是否存活。如果线程仍未开始或已经结束,返回false; 如果线程已经开始或者正在运行中,返回true。

waitFor()

等待线程开始执行。调用threads.start()以后线程仍然需要一定时间才能开始执行,因此调用此函数会等待线程开始执行;如果线程已经处于执行状态则立即返回。

var thread = threads.start(function(){

    //do something

});

thread.waitFor();

thread.setTimeout(function(){

    //do something

}, 1000);

Thread.setTimeout(callback, delay[, ...args])

参见timers.setTimeout()

区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出IllegalStateException。

log("当前线程(主线程):" + threads.currentThread());

 

var thread = threads.start(function(){

    //设置一个空的定时来保持线程的运行状态

    setInterval(function(){}, 1000);

});

 

sleep(1000);

thread.setTimeout(function(){

    log("当前线程(子线程):" + threads.currentThread());

    exit();

}, 1000);

Thread.setInterval(callback, delay[, ...args])

参见timers.setInterval()

区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出IllegalStateException。

Thread.setImmediate(callback[, ...args])

参见timers.setImmediate()

区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出IllegalStateException。

Thread.clearInterval(id)

参见timers.clearInterval()

区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出IllegalStateException。

Thread.clearTimeout(id)

参见timers.clearTimeout()

区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出IllegalStateException。

Thread.clearImmediate(id)

参见timers.clearImmediate()

区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出IllegalStateException。

线程安全

线程安全问题是一个相对专业的编程问题,本章节只提供给有需要的用户。

引用维基百科的解释:

线程安全是编程中的术语,指某个函数、函数库在多线程环境中被调用时,能够正确地处理多个线程之间的共享变量,使程序功能正确完成。

在Auto.js中,线程间变量在符合JavaScript变量作用域规则的前提下是共享的,例如全局变量在所有线程都能访问,并且保证他们在所有线程的可见性。但是,不保证任何操作的原子性。例如经典的自增"i++"将不是原子性操作。

Rhino和Auto.js提供了一些简单的设施来解决简单的线程安全问题,如锁threads.lock(), 函数同步锁sync(), 整数原子变量threads.atomic()等。

例如,对于多线程共享下的整数的自增操作(自增操作会导致问题,是因为自增操作实际上为i = i + 1,也就是先读取i的值, 把他加1, 再赋值给i, 如果两个线程同时进行自增操作,可能出现i的值只增加了1的情况),应该使用threads.atomic()函数来新建一个整数原子变量,或者使用锁threads.lock()来保证操作的原子性,或者用sync()来增加同步锁。

线程不安全的代码如下:

var i = 0;

threads.start(function(){

    while(true){

        log(i++);

    }

});

while(true){

    log(i++);

}

此段代码运行后打开日志,可以看到日志中有重复的值出现。

使用threads.atomic()的线程安全的代码如下:

//atomic返回的对象保证了自增的原子性

var i = threads.atomic();

threads.start(function(){

    while(true){

        log(i.getAndIncrement());

    }

});

while(true){

    log(i.getAndIncrement());

}

或者:

//锁保证了操作的原子性

var lock = threads.lock();

var i = 0;

threads.start(function(){

    while(true){

        lock.lock();

        log(i++);

        lock.unlock();

    }

});

while(true){

    lock.lock();

    log(i++);

    lock.unlock();

}

或者:

//sync函数会把里面的函数加上同步锁,使得在同一时刻最多只能有一个线程执行这个函数

var i = 0;

var getAndIncrement = sync(function(){

    return i++;

});

threads.start(function(){

    while(true){

        log(getAndIncrement());

    }

});

while(true){

    log(getAndIncrement());

}

另外,数组Array不是线程安全的,如果有这种复杂的需求,请用Android和Java相关API来实现。例如CopyOnWriteList, Vector等都是代替数组的线程安全的类,用于不同的场景。例如:

var nums = new java.util.Vector();

nums.add(123);

nums.add(456);

toast("长度为" + nums.size());

toast("第一个元素为" + nums.get(0));

但很明显的是,这些类不像数组那样简便易用,也不能使用诸如slice()之类的方便的函数。在未来可能会加入线程安全的数组来解决这个问题。当然您也可以为每个数组的操作加锁来解决线程安全问题:

var nums = [];

var numsLock = threads.lock();

threads.start(function(){

    //向数组添加元素123

    numsLock.lock();

    nums.push(123);

    log("线程: %s, 数组: %s", threads.currentThread(), nums);

    numsLock.unlock();

});

 

threads.start(function(){

    //向数组添加元素456

    numsLock.lock();

    nums.push(456);

    log("线程: %s, 数组: %s", threads.currentThread(), nums);

    numsLock.unlock();

});

 

//删除数组最后一个元素

numsLock.lock();

nums.pop();

log("线程: %s, 数组: %s", threads.currentThread(), nums);

numsLock.unlock();

sync(func)

  • func {Function} 函数
  • 返回 {Function}

给函数func加上同步锁并作为一个新函数返回。

var i = 0;

function add(x){

    i += x;

}

 

var syncAdd = sync(add);

syncAdd(10);

toast(i);

线程通信

Auto.js提供了一些简单的设施来支持简单的线程通信。threads.disposable()用于一个线程等待另一个线程的(一次性)结果,同时Lock.newCondition()提供了Condition对象用于一般的线程通信(await, signal)。另外,events模块也可以用于线程通信,通过指定EventEmiiter的回调执行的线程来实现。

使用threads.disposable()可以简单地等待和获取某个线程的执行结果。例如要等待某个线程计算"1+.....+10000":

var sum = threads.disposable();

//启动子线程计算

threads.start(function(){

    var s = 0;

    //从1加到10000

    for(var i = 1; i <= 10000; i++){

        s += i;

    }

    //通知主线程接收结果

    sum.setAndNotify(s);

});

//blockedGet()用于等待结果

toast("sum = " + sum.blockedGet());

如果上述代码用Condition实现:

//新建一个锁

var lock = threads.lock();

//新建一个条件,即"计算完成"

var complete = lock.newCondition();

var sum = 0;

threads.start(function(){

    //从1加到10000

    for(var i = 1; i <= 10000; i++){

        sum += i;

    }

    //通知主线程接收结果

    lock.lock();

    complete.signal();

    lock.unlock();

});

//等待计算完成

lock.lock();

complete.await();

lock.unlock();

//打印结果

toast("sum = " + sum);

如果上诉代码用events模块实现:

//新建一个emitter, 并指定回调执行的线程为当前线程

var sum = events.emitter(threads.currentThread());

threads.start(function(){

    var s = 0;

    //从1加到10000

    for(var i = 1; i <= 10000; i++){

        s += i;

    }

    //发送事件result通知主线程接收结果

    sum.emit('result', s);

});

sum.on('result', function(s){

    toastLog("sum = " + s + ", 当前线程: " + threads.currentThread());

});

有关线程的其他问题,例如生产者消费者等问题,请用Java相关方法解决,例如java.util.concurrent.BlockingQueue。

Timers

Stability: 2 - Stable

timers 模块暴露了一个全局的 API,用于在某个未来时间段调用调度函数。 因为定时器函数是全局的,所以使用该 API 无需调用 timers.*

Auto.js 中的计时器函数实现了与 Web 浏览器提供的定时器类似的 API,除了它使用了一个不同的内部实现,它是基于 Android Looper-Handler消息循环机制构建的。其实现机制与Node.js比较相似。

例如,要在5秒后发出消息"hello":

setTimeout(function(){

    toast("hello")

}, 5000);

需要注意的是,这些定时器仍然是单线程的。如果脚本主体有耗时操作或死循环,则设定的定时器不能被及时执行,例如:

setTimeout(function(){

    //这里的语句会在15秒后执行而不是5秒后

    toast("hello")

}, 5000);

//暂停10秒

sleep(10000);

再如:

setTimeout(function(){

    //这里的语句永远不会被执行

    toast("hello")

}, 5000);

//死循环

while(true);

setInterval(callback, delay[, ...args])

  • callback {Function} 当定时器到点时要调用的函数。
  • delay {number} 调用 callback 之前要等待的毫秒数。
  • ...args {any} 当调用 callback 时要传入的可选参数。

预定每隔 delay 毫秒重复执行的 callback。 返回一个用于 clearInterval() 的 id。

当 delay 小于 0 时,delay 会被设为 0。

setTimeout(callback, delay[, ...args])

  • callback {Function} 当定时器到点时要调用的函数。
  • delay {number} 调用 callback 之前要等待的毫秒数。
  • ...args {any} 当调用 callback 时要传入的可选参数。

预定在 delay 毫秒之后执行的单次 callback。 返回一个用于 clearTimeout() 的 id。

callback 可能不会精确地在 delay 毫秒被调用。 Auto.js 不能保证回调被触发的确切时间,也不能保证它们的顺序。 回调会在尽可能接近所指定的时间上调用。

当 delay 小于 0 时,delay 会被设为 0。

setImmediate(callback[, ...args])

  • callback {Function} 在Looper循环的当前回合结束时要调用的函数。
  • ...args {any} 当调用 callback 时要传入的可选参数。

预定立即执行的 callback,它是在 I/O 事件的回调之后被触发。 返回一个用于 clearImmediate() 的 id。

当多次调用 setImmediate() 时,callback 函数会按照它们被创建的顺序依次执行。 每次事件循环迭代都会处理整个回调队列。 如果一个立即定时器是被一个正在执行的回调排入队列的,则该定时器直到下一次事件循环迭代才会被触发。

setImmediate()、setInterval() 和 setTimeout() 方法每次都会返回表示预定的计时器的id。 它们可用于取消定时器并防止触发。

clearInterval(id)

  • id {number} 一个 setInterval() 返回的 id。

取消一个由 setInterval() 创建的循环定时任务。

例如:

//每5秒就发出一次hello

var id = setInterval(function(){

    toast("hello");

}, 5000);

//1分钟后取消循环

setTimeout(function(){

    clearInterval(id);

}, 60 * 1000);

clearTimeout(id)

  • id {number} 一个 setTimeout() 返回的 id。

取消一个由 setTimeout() 创建的定时任务。

clearImmediate(id)

  • id {number} 一个 setImmediate() 返回的 id。

取消一个由 setImmediate() 创建的 Immediate 对象。

用户界面: UI

ui模块提供了编写用户界面的支持。

带有ui的脚本的的最前面必须使用"ui";指定ui模式,否则脚本将不会以ui模式运行。正确示范:

"ui";

 

//脚本的其他代码

字符串"ui"的前面可以有注释、空行和空格[v4.1.0新增],但是不能有其他代码。

界面是由视图(View)组成的。View分成两种,控件(Widget)和布局(Layout)。控件(Widget)用来具体显示文字、图片、网页等,比如文本控件(text)用来显示文字,按钮控件(button)则可以显示一个按钮并提供点击效果,图片控件(img)则用来显示来自网络或者文件的图片,除此之外还有输入框控件(input)、进度条控件(progressbar)、单选复选框控件(checkbox)等;布局(Layout)则是装着一个或多个控件的"容器",用于控制在他里面的控件的位置,比如垂直布局(vertical)会把他里面的控件从上往下依次显示(即纵向排列),水平布局(horizontal)则会把他里面的控件从左往右依次显示(即横向排列),以及帧布局(frame),他会把他里面的控件直接在左上角显示,如果有多个控件,后面的控件会重叠在前面的控件上。

我们使用xml来编写界面,并通过ui.layout()函数指定界面的布局xml。举个例子:

"ui";

ui.layout(

    <vertical>

        <button text="第一个按钮"/>

        <button text="第二个按钮"/>

    </vertical>

);

在这个例子中,第3~6行的部分就是xml,指定了界面的具体内容。代码的第3行的标签<vertical> ... </vertical>表示垂直布局,布局的标签通常以<...>开始,以</...>结束,两个标签之间的内容就是布局里面的内容,例如<frame> ... </frame>。在这个例子中第4, 5行的内容就是垂直布局(vertical)里面的内容。代码的第4行是一个按钮控件(button),控件的标签通常以<...开始,以/>结束,他们之间是控件的具体属性,例如<text ... />。在这个例子中text="第一个按钮"的部分就是按钮控件(button)的属性,这个属性指定了这个按钮控件的文本内容(text)为"第一个按钮"。

代码的第5行和第4行一样,也是一个按钮控件,只不过他的文本内容为"第二个按钮"。这两个控件在垂直布局中,因此会纵向排列,效果如图:

 

如果我们把这个例子的垂直布局(vertical)改成水平布局(horizontal),也即:

"ui";

ui.layout(

    <horizontal>

        <button text="第一个按钮"/>

        <button text="第二个按钮"/>

    </horizontal>

);

则这两个按钮会横向排列,效果如图:

 

一个控件可以指定多个属性(甚至可以不指定任何属性),用空格隔开即可;布局同样也可以指定属性,例如:

"ui";

ui.layout(

    <vertical bg="#ff0000">

        <button text="第一个按钮" textSize="20sp"/>

        <button text="第二个按钮"/>

    </vertical>

);

第三行bg="#ff0000"指定了垂直布局的背景色(bg)为"#ff0000",这是一个RGB颜色,表示红色(有关RGB的相关知识参见RGB颜色对照表)。第四行的textSize="20sp"则指定了按钮控件的字体大小(textSize)为"20sp",sp是一个字体单位,暂时不用深入理会。上述代码的效果如图:

 

一个界面便由一些布局和控件组成。为了便于文档阅读,我们再说明一下以下术语:

  • 子视图, 子控件: 布局里面的控件是这个布局的子控件/子视图。实际上布局里面不仅仅只能有控件,还可以是嵌套的布局。因此用子视图(Child View)更准确一些。在上面的例子中,按钮便是垂直布局的子控件。
  • 父视图,父布局:直接包含一个控件的布局是这个控件的父布局/父视图(Parent View)。在上面的例子中,垂直布局便是按钮的父布局。

视图: View

控件和布局都属于视图(View)。在这个章节中将介绍所有控件和布局的共有的属性和函数。例如属性背景,宽高等(所有控件和布局都能设置背景和宽高),函数click()设置视图(View)被点击时执行的动作。

w

View的宽度,是属性width的缩写形式。可以设置的值为*, auto和具体数值。其中*表示宽度尽量填满父布局,而auto表示宽度将根据View的内容自动调整(自适应宽度)。例如:

"ui";

ui.layout(

    <horizontal>

        <button w="auto" text="自适应宽度"/>

        <button w="*" text="填满父布局"/>

    </horizontal>

);

在这个例子中,第一个按钮为自适应宽度,第二个按钮为填满父布局,显示效果为:

 

如果不设置该属性,则不同的控件和布局有不同的默认宽度,大多数为auto。

宽度属性也可以指定一个具体数值。例如w="20",w="20px"等。不加单位的情况下默认单位为dp,其他单位包括px(像素), mm(毫米), in(英寸)。有关尺寸单位的更多内容,参见尺寸的单位: Dimension

"ui";

ui.layout(

    <horizontal>

        <button w="200" text="宽度200dp"/>

        <button w="100" text="宽度100dp"/>

    </horizontal>

);

h

View的高度,是属性height的缩写形式。可以设置的值为*, auto和具体数值。其中*表示宽度尽量填满父布局,而auto表示宽度将根据View的内容自动调整(自适应宽度)。

如果不设置该属性,则不同的控件和布局有不同的默认高度,大多数为auto。

宽度属性也可以指定一个具体数值。例如h="20",h="20px"等。不加单位的情况下默认单位为dp,其他单位包括px(像素), mm(毫米), in(英寸)。有关尺寸单位的更多内容,参见尺寸的单位: Dimension

id

View的id,用来区分一个界面下的不同控件和布局,一个界面的id在同一个界面下通常是唯一的,也就是一般不存在两个View有相同的id。id属性也是连接xml布局和JavaScript代码的桥梁,在代码中可以通过一个View的id来获取到这个View,并对他进行操作(设置点击动作、设置属性、获取属性等)。例如:

"ui";

ui.layout(

    <frame>

        <button id="ok" text="确定"/>

    </frame>

);

//通过ui.ok获取到按钮控件

toast(ui.ok.getText());

这个例子中有一个按钮控件"确定",id属性为"ok",那么我们可以在代码中使用ui.ok来获取他,再通过getText()函数获取到这个按钮控件的文本内容。 另外这个例子中使用帧布局(frame)是因为,我们只有一个控件,因此用于最简单的布局帧布局。

gravity

View的"重力"。用于决定View的内容相对于View的位置,可以设置的值为:

  • left 靠左
  • right 靠右
  • top 靠顶部
  • bottom 靠底部
  • center 居中
  • center_vertical 垂直居中
  • center_horizontal 水平居中

例如对于一个按钮控件,gravity="right"会使其中的文本内容靠右显示。例如:

"ui";

ui.layout(

    <frame>

        <button gravity="right" w="*" h="auto" text="靠右的文字"/>

    </frame>

);

显示效果为:

 

这些属性是可以组合的,例如gravity="right|bottom"的View他的内容会在右下角。

layout_gravity

View在布局中的"重力",用于决定View本身在他的父布局的位置,可以设置的值和gravity属性相同。注意把这个属性和gravity属性区分开来。

"ui";

ui.layout(

    <frame w="*" h="*">

        <button layout_gravity="center" w="auto" h="auto" text="居中的按钮"/>

        <button layout_gravity="right|bottom" w="auto" h="auto" text="右下角的按钮"/>

    </frame>

);

在这个例子中,我们让帧布局(frame)的大小占满整个屏幕,通过给第一个按钮设置属性layout_gravity="center"来使得按钮在帧布局中居中,通过给第二个按钮设置属性layout_gravity="right|bottom"使得他在帧布局中位于右下角。效果如图:

 

要注意的是,layout_gravity的属性不一定总是生效的,具体取决于布局的类别。例如不能让水平布局中的第一个子控件靠底部显示(否则和水平布局本身相违背)。

margin

margin为View和其他View的间距,即外边距。margin属性包括四个值:

  • marginLeft 左外边距
  • marginRight 右外边距
  • marginTop 上外边距
  • marginBottom 下外边距

而margin属性本身的值可以有三种格式:

  • margin="marginAll" 指定各个外边距都是该值。例如margin="10"表示左右上下边距都是10dp。
  • margin="marginLeft marginTop marginRight marginBottom" 分别指定各个外边距。例如margin="10 20 30 40"表示左边距为10dp, 上边距为20dp, 右边距为30dp, 下边距为40dp
  • margin="marginHorizontal marginVertical" 指定水平外边距和垂直外边距。例如margin="10 20"表示左右边距为10dp, 上下边距为20dp。

用一个例子来具体理解外边距的含义:

"ui";

ui.layout(

    <horizontal>

        <button margin="30" text="距离四周30"/>

        <button text="普通的按钮"/>

    </horizontal>

);

第一个按钮的margin属性指定了他的边距为30dp, 也就是他与水平布局以及第二个按钮的间距都是30dp, 其显示效果如图:

 

如果把margin="30"改成margin="10 40"那么第一个按钮的左右间距为10dp, 上下间距为40dp, 效果如图:

 

有关margin属性的单位,参见尺寸的单位: Dimension

marginLeft

View的左外边距。如果该属性和margin属性指定的值冲突,则在后面的属性生效,前面的属性无效,例如margin="20" marginLeft="10"的左外边距为10dp,其他外边距为20dp。

"ui";

ui.layout(

    <horizontal>

        <button marginLeft="50" text="距离左边50"/>

        <button text="普通的按钮"/>

    </horizontal>

);

第一个按钮指定了左外边距为50dp,则他和他的父布局水平布局(horizontal)的左边的间距为50dp, 效果如图:

 

marginRight

View的右外边距。如果该属性和margin属性指定的值冲突,则在后面的属性生效,前面的属性无效。

marginTop

View的上外边距。如果该属性和margin属性指定的值冲突,则在后面的属性生效,前面的属性无效。

marginBottom

View的下外边距。如果该属性和margin属性指定的值冲突,则在后面的属性生效,前面的属性无效。

padding

View和他的自身内容的间距,也就是内边距。注意和margin属性区分开来,margin属性是View之间的间距,而padding是View和他自身内容的间距。举个例子,一个文本控件的padding也即文本控件的边缘和他的文本内容的间距,paddingLeft即文本控件的左边和他的文本内容的间距。

paddding属性的值同样有三种格式:

  • padding="paddingAll" 指定各个内边距都是该值。例如padding="10"表示左右上下内边距都是10dp。
  • padding="paddingLeft paddingTop paddingRight paddingBottom" 分别指定各个内边距。例如padding="10 20 30 40"表示左内边距为10dp, 上内边距为20dp, 右内边距为30dp, 下内边距为40dp
  • padding="paddingHorizontal paddingVertical" 指定水平内边距和垂直内边距。例如padding="10 20"表示左右内边距为10dp, 上下内边距为20dp。

用一个例子来具体理解内边距的含义:

"ui";

ui.layout(

    <frame w="*" h="*" gravity="center">

        <text padding="10 20 30 40" bg="#ff0000" w="auto" h="auto" text="HelloWorld"/>

    </frame>

);

这个例子是一个居中的按钮(通过父布局的gravity="center"属性设置),背景色为红色(bg="#ff0000"),文本内容为"HelloWorld",左边距为10dp,上边距为20dp,下边距为30dp,右边距为40dp,其显示效果如图:

 

paddingLeft

View的左内边距。如果该属性和padding属性指定的值冲突,则在后面的属性生效,前面的属性无效。

paddingRight

View的右内边距。如果该属性和padding属性指定的值冲突,则在后面的属性生效,前面的属性无效。

paddingTop

View的上内边距。如果该属性和padding属性指定的值冲突,则在后面的属性生效,前面的属性无效。

paddingBottom

View的下内边距。如果该属性和padding属性指定的值冲突,则在后面的属性生效,前面的属性无效。

bg

View的背景。其值可以是一个链接或路径指向的图片,或者RGB格式的颜色,或者其他背景。具体参见Drawables

例如,bg="#00ff00"设置背景为绿色,bg="file:///sdcard/1.png"设置背景为图片"1.png",bg="?attr/selectableItemBackground"设置背景为点击时出现的波纹效果(可能需要同时设置clickable="true"才生效)。

alpha

View的透明度,其值是一个0~1之间的小数,0表示完全透明,1表示完全不透明。例如alpha="0.5"表示半透明。

foreground

View的前景。前景即在一个View的内容上显示的内容,可能会覆盖掉View本身的内容。其值和属性bg的值类似。

minHeight

View的最小高度。该值不总是生效的,取决于其父布局是否有足够的空间容纳。

例:<text height="auto" minHeight="50"/>

有关该属性的单位,参见尺寸的单位: Dimension

minWidth

View的最小宽度。该值不总是生效的,取决于其父布局是否有足够的空间容纳。

例:<input width="auto" minWidth="50"/>

有关该属性的单位,参见尺寸的单位: Dimension

visbility

View的可见性,该属性可以决定View是否显示出来。其值可以为:

  • gone 不可见。
  • visible 可见。默认情况下View都是可见的。
  • invisible 不可见,但仍然占用位置。

rotation

View的旋转角度。通过该属性可以让这个View顺时针旋转一定的角度。例如rotation="90"可以让他顺时针旋转90度。

如果要设置旋转中心,可以通过transformPivotX, transformPivotY属性设置。默认的旋转中心为View的中心。

transformPivotX

View的变换中心坐标x。用于View的旋转、放缩等变换的中心坐标。例如transformPivotX="10"。

该坐标的坐标系以View的左上角为原点。也就是x值为变换中心到View的左边的距离。

有关该属性的单位,参见尺寸的单位: Dimension

transformPivotY

View的变换中心坐标y。用于View的旋转、放缩等变换的中心坐标。例如transformPivotY="10"。

该坐标的坐标系以View的左上角为原点。也就是y值为变换中心到View的上边的距离。

有关该属性的单位,参见尺寸的单位: Dimension

style

设置View的样式。不同控件有不同的可选的内置样式。具体参见各个控件的说明。

需要注意的是,style属性只支持安卓5.1及其以上。

文本控件: text

文本控件用于显示文本,可以控制文本的字体大小,字体颜色,字体等。

以下介绍该控件的主要属性和方法,如果要查看他的所有属性和方法,请阅读TextView

text

设置文本的内容。例如text="一段文本"。

textColor

设置字体的颜色,可以是RGB格式的颜色(例如#ff00ff),或者颜色名称(例如red, green等),具体参见颜色

示例, 红色字体:<text text="红色字体" textColor="red"/>

textSize

设置字体的大小,单位一般是sp。按照Material Design的规范,正文字体大小为14sp,标题字体大小为18sp,次标题为16sp。

示例,超大字体: <text text="超大字体" textSize="40sp"/>

textStyle

设置字体的样式,比如斜体、粗体等。可选的值为:

  • bold 加粗字体
  • italic 斜体
  • normal 正常字体

可以用或("|")把他们组合起来,比如粗斜体为"bold|italic"。

例如,粗体:`

lines

设置文本控件的行数。即使文本内容没有达到设置的行数,控件也会留出相应的宽度来显示空白行;如果文本内容超出了设置的行数,则超出的部分不会显示。

另外在xml中是不能设置多行文本的,要在代码中设置。例如:

"ui";

ui.layout(

    <vertical>

        <text id="myText" line="3">

    </vertical>

)

//通过\n换行

ui.myText.setText("第一行\n第二行\n第三行\n第四行");

maxLines

设置文本控件的最大行数。

typeface

设置字体。可选的值为:

  • normal 正常字体
  • sans 衬线字体
  • serif 非衬线字体
  • monospace 等宽字体

示例,等宽字体: <text text="等宽字体" typeface="monospace"/>

ellipsize

设置文本的省略号位置。文本的省略号会在文本内容超出文本控件时显示。可选的值为:

  • end 在文本末尾显示省略号
  • marquee 跑马灯效果,文本将滚动显示
  • middle 在文本中间显示省略号
  • none 不显示省略号
  • start 在文本开头显示省略号

ems

当设置该属性后,TextView显示的字符长度(单位是em),超出的部分将不显示,或者根据ellipsize属性的设置显示省略号。

例如,限制文本最长为5em: `

autoLink

控制是否自动找到url和电子邮件地址等链接,并转换为可点击的链接。默认值为“none”。

设置该值可以让文本中的链接、电话等变成可点击状态。

可选的值为以下的值以其通过或("|")的组合:

  • all 匹配所有连接、邮件、地址、电话
  • email 匹配电子邮件地址
  • map 匹配地图地址
  • none 不匹配 (默认)
  • phone 匹配电话号码
  • web 匹配URL地址

示例:<text autoLink="web|phone" text="百度: http://www.baidu.com 电信电话: 10000"/>

按钮控件: button

按钮控件是一个特殊的文本控件,因此所有文本控件的函数的属性都适用于按钮控件。

除此之外,按钮控件有一些内置的样式,通过style属性设置,包括:

  • Widget.AppCompat.Button.Colored 带颜色的按钮
  • Widget.AppCompat.Button.Borderless 无边框按钮
  • Widget.AppCompat.Button.Borderless.Colored 带颜色的无边框按钮

这些样式的具体效果参见"示例/界面控件/按钮控件.js"。

例如:<button style="Widget.AppCompat.Button.Colored" text="漂亮的按钮"/>

输入框控件: input

输入框控件也是一个特殊的文本控件,因此所有文本控件的函数的属性和函数都适用于按钮控件。输入框控件有自己的属性和函数,要查看所有这些内容,阅读EditText

对于一个输入框控件,我们可以通过text属性设置他的内容,通过lines属性指定输入框的行数;在代码中通过getText()函数获取输入的内容。例如:

"ui";

ui.layout(

    <vertical padding="16">

        <text textSize="16sp" textColor="black" text="请输入姓名"/>

        <input id="name" text="小明"/>

        <button id="ok" text="确定"/>

    </vertical>

);

//指定确定按钮点击时要执行的动作

ui.ok.click(function(){

    //通过getText()获取输入的内容

    var name = ui.name.getText();

    toast(name + "您好!");

});

效果如图:

 

除此之外,输入框控件有另外一些主要属性(虽然这些属性对于文本控件也是可用的但一般只用于输入框控件):

hint

输入提示。这个提示会在输入框为空的时候显示出来。如图所示:

 

上面图片效果的代码为:

"ui";

ui.layout(

    <vertical>

        <input hint="请输入姓名"/>

    </vertical>

)

textColorHint

指定输入提示的字体颜色。

textSizeHint

指定输入提示的字体大小。

inputType

指定输入框可以输入的文本类型。可选的值为以下值及其用"|"的组合:

  • date 用于输入日期。
  • datetime 用于输入日期和时间。
  • none 没有内容类型。此输入框不可编辑。
  • number 仅可输入数字。
  • numberDecimal 可以与number和它的其他选项组合,以允许输入十进制数(包括小数)。
  • numberPassword 仅可输入数字密码。
  • numberSigned 可以与number和它的其他选项组合,以允许输入有符号的数。
  • phone 用于输入一个电话号码。
  • text 只是普通文本。
  • textAutoComplete 可以与text和它的其他选项结合, 以指定此字段将做自己的自动完成, 并适当地与输入法交互。
  • textAutoCorrect 可以与text和它的其他选项结合, 以请求自动文本输入纠错。
  • textCapCharacters 可以与text和它的其他选项结合, 以请求大写所有字符。
  • textCapSentences 可以与text和它的其他选项结合, 以请求大写每个句子里面的第一个字符。
  • textCapWords 可以与text和它的其他选项结合, 以请求大写每个单词里面的第一个字符。
  • textEmailAddress 用于输入一个电子邮件地址。
  • textEmailSubject 用于输入电子邮件的主题。
  • textImeMultiLine 可以与text和它的其他选项结合,以指示虽然常规文本视图不应为多行, 但如果可以, 则IME应提供多行支持。
  • textLongMessage 用于输入长消息的内容。
  • textMultiLine 可以与text和它的其他选项结合, 以便在该字段中允许多行文本。如果未设置此标志, 则文本字段将被限制为单行。
  • textNoSuggestions 可以与text及它的其他选项结合, 以指示输入法不应显示任何基于字典的单词建议。
  • textPassword 用于输入密码。
  • textPersonName 用于输入人名。
  • textPhonetic 用于输入拼音发音的文本, 如联系人条目中的拼音名称字段。
  • textPostalAddress 用于输入邮寄地址。
  • textShortMessage 用于输入短的消息内容。
  • textUri 用于输入一个URI。
  • textVisiblePassword 用于输入可见的密码。
  • textWebEditText 用于输入在web表单中的文本。
  • textWebEmailAddress 用于在web表单里输入一个电子邮件地址。
  • textWebPassword 用于在web表单里输入一个密码。
  • time 用于输入时间。

例如,想指定一个输入框的输入类型为小数数字,为: <input inputType="number|numberDecimal"/>

password

指定输入框输入框是否为密码输入框。默认为false。

例如:<input password="true"/>

numeric

指定输入框输入框是否为数字输入框。默认为false。

例如:<input numeric="true"/>

phoneNumber

指定输入框输入框是否为电话号码输入框。默认为false。

例如:<input phoneNumber="true"/>

digits

指定输入框可以输入的字符。例如,要指定输入框只能输入"1234567890+-",为<input digits="1234567890+-"/>。

singleLine

指定输入框是否为单行输入框。默认为false。您也可以通过lines="1"来指定单行输入框。

例如:<input singleLine="true"/>

图片控件: img

图片控件用于显示来自网络、本地或者内嵌数据的图片,并可以指定图片以圆角矩形、圆形等显示。但是不能用于显示gif动态图。

这里只介绍他的主要方法和属性,如果要查看他的所有方法和属性,阅读ImageView

src

使用一个Uri指定图片的来源。可以是图片的地址(http://....),本地路径(file://....)或者base64数据("https://img-blog.csdnimg.cn/2022010619420118312.png")。

如果使用图片地址或本地路径,Auto.js会自动使用适当的缓存来储存这些图片,减少下次加载的时间。

例如,显示百度的logo:

"ui";

ui.layout(

    <frame>

        <img src="https://www.baidu.com/img/bd_logo1.png"/>

    </frame>

);

再例如,显示文件/sdcard/1.png的图片为 <img src="file:///sdcard/1.png"/>。 再例如,使base64显示一张钱包小图片为:

"ui";

ui.layout(

    <frame>

        <img w="40" h="40" src="https://img-blog.csdnimg.cn/2022010619420036485.png"/>

    </frame>

);

tint

图片着色,其值是一个颜色名称或RGB颜色值。使用该属性会将图片中的非透明区域都涂上同一颜色。可以用于改变图片的颜色。

例如,对于上面的base64的图片: <img w="40" h="40" tint="red" src="https://img-blog.csdnimg.cn/2022010619420118312.png"/>,则钱包图标颜色会变成红色。

scaleType

控制图片根据图片控件的宽高放缩时的模式。可选的值为:

  • center 在控件中居中显示图像, 但不执行缩放。
  • centerCrop 保持图像的长宽比缩放图片, 使图像的尺寸 (宽度和高度) 等于或大于控件的相应尺寸 (不包括内边距padding)并且使图像在控件中居中显示。
  • centerInside 保持图像的长宽比缩放图片, 使图像的尺寸 (宽度和高度) 小于视图的相应尺寸 (不包括内边距padding)并且图像在控件中居中显示。
  • fitCenter 保持图像的长宽比缩放图片, 使图片的宽高和控件的宽高相同并使图片在控件中居中显示
  • fitEnd 保持图像的长宽比缩放图片, 使图片的宽高和控件的宽高相同并使图片在控件中靠右下角显示
  • fitStart 保持图像的长宽比缩放图片, 使图片的宽高和控件的宽高相同并使图片在控件靠左上角显示
  • fitXY 使图片和宽高和控件的宽高完全匹配,但图片的长宽比可能不能保持一致
  • matrix 绘制时使用图像矩阵进行缩放。需要在代码中使用setImageMatrix(Matrix)函数才能生效。

默认的scaleType为fitCenter;除此之外最常用的是fitXY, 他能使图片放缩到控件一样的大小,但图片可能会变形。

radius

图片控件的半径。如果设置为控件宽高的一半并且控件的宽高相同则图片将剪切为圆形显示;否则图片为圆角矩形显示,半径即为四个圆角的半径,也可以通过radiusTopLeft, radiusTopRight, radiusBottomLeft, radiusBottomRight等属性分别设置四个圆角的半径。

例如,圆角矩形的Auto.js图标:<img w="100" h="100" radius="20" bg="white" src="http://www.autojs.org/assets/uploads/profile/3-profileavatar.png" />

有关该属性的单位,参见尺寸的单位: Dimension

radiusTopLeft

图片控件的左上角圆角的半径。有关该属性的单位,参见尺寸的单位: Dimension

radiusTopRight

图片控件的右上角圆角的半径。有关该属性的单位,参见尺寸的单位: Dimension

radiusBottomLeft

图片控件的左下角圆角的半径。有关该属性的单位,参见尺寸的单位: Dimension

radiusBottomRight

图片控件的右下角圆角的半径。有关该属性的单位,参见尺寸的单位: Dimension

borderWidth

图片控件的边框宽度。用于在图片外面显示一个边框,边框会随着图片控件的外形(圆角等)改变而相应变化。 例如, 圆角矩形带灰色边框的Auto.js图标:<img w="100" h="100" radius="20" borderWidth="5" borderColor="gray" bg="white" src="http://www.autojs.org/assets/uploads/profile/3-profileavatar.png" />

borderColor

图片控件的边框颜色。

circle

指定该图片控件的图片是否剪切为圆形显示。如果为true,则图片控件会使其宽高保持一致(如果宽高不一致,则保持高度等于宽度)并使圆形的半径为宽度的一半。

例如,圆形的Auto.js图标:<img w="100" h="100" circle="true" bg="white" src="http://www.autojs.org/assets/uploads/profile/3-profileavatar.png" />

垂直布局: vertical

垂直布局是一种比较简单的布局,会把在它里面的控件按照垂直方向依次摆放,如下图所示:

垂直布局:

—————

| 控件1 |

| 控件2 |

| 控件3 |

| ............ |

——————

layout_weight

垂直布局中的控件可以通过layout_weight属性来控制控件高度占垂直布局高度的比例。如果为一个控件指定layout_weight, 则这个控件的高度=垂直布局剩余高度 * layout_weight / weightSum;如果不指定weightSum, 则weightSum为所有子控件的layout_weight之和。所谓"剩余高度",指的是垂直布局中减去没有指定layout_weight的控件的剩余高度。 例如:

"ui";

ui.layout(

    <vertical h="100dp">

        <text layout_weight="1" text="控件1" bg="#ff0000"/>

        <text layout_weight="1" text="控件2" bg="#00ff00"/>

        <text layout_weight="1" text="控件3" bg="#0000ff"/>

    </vertical>

);

在这个布局中,三个控件的layout_weight都是1,也就是他们的高度都会占垂直布局高度的1/3,都是33.3dp. 再例如:

"ui";

ui.layout(

    <vertical h="100dp">

        <text layout_weight="1" text="控件1" bg="#ff0000"/>

        <text layout_weight="2" text="控件2" bg="#00ff00"/>

        <text layout_weight="1" text="控件3" bg="#0000ff"/>

    </vertical>

);

在这个布局中,第一个控件高度为1/4, 第二个控件为2/4, 第三个控件为1/4. 再例如:

"ui";

ui.layout(

    <vertical h="100dp" weightSum="5">

        <text layout_weight="1" text="控件1" bg="#ff0000"/>

        <text layout_weight="2" text="控件2" bg="#00ff00"/>

        <text layout_weight="1" text="控件3" bg="#0000ff"/>

    </vertical>

);

在这个布局中,因为指定了weightSum为5, 因此第一个控件高度为1/5, 第二个控件为2/5, 第三个控件为1/5. 再例如:

"ui";

ui.layout(

    <vertical h="100dp">

        <text h="40dp" text="控件1" bg="#ff0000"/>

        <text layout_weight="2" text="控件2" bg="#00ff00"/>

        <text layout_weight="1" text="控件3" bg="#0000ff"/>

    </vertical>

);

在这个布局中,第一个控件并没有指定layout_weight, 而是指定高度为40dp, 因此不加入比例计算,此时布局剩余高度为60dp。第二个控件高度为剩余高度的2/3,也就是40dp,第三个控件高度为剩余高度的1/3,也就是20dp。

垂直布局的layout_weight属性还可以用于控制他的子控件高度占满剩余空间,例如:

"ui";

ui.layout(

    <vertical h="100dp">

        <text h="40dp" text="控件1" bg="#ff0000"/>

        <text h="40dp" text="控件2" bg="#00ff00"/>

        <text layout_weight="1" text="控件3" bg="#0000ff"/>

    </vertical>

);

在这个布局中,第三个控件的高度会占满除去控件1和控件2的剩余空间。

水平布局: horizontal

水平布局是一种比较简单的布局,会把在它里面的控件按照水平方向依次摆放,如下图所示: 水平布局: ————————————————————————————

| 控件1 | 控件2 | 控件3 | ... |

————————————————————————————

layout_weight

水平布局中也可以使用layout_weight属性来控制子控件的宽度占父布局的比例。和垂直布局中类似,不再赘述。

线性布局: linear

实际上,垂直布局和水平布局都属于线性布局。线性布局有一个orientation的属性,用于指定布局的方向,可选的值为vertical和horizontal。

例如<linear orientation="vertical"></linear>相当于<vertical></vertical>。

线性布局的默认方向是横向的,因此,一个没有指定orientation属性的线性布局就是横向布局。

帧布局: frame

帧布局

相对布局: relative

勾选框控件: checkbox

选择框控件: radio

选择框布局: radiogroup

开关控件: switch

进度条控件: progressbar

拖动条控件: seekbar

下来菜单控件: spinner

时间选择控件: timepicker

日期选择控件: datepicker

浮动按钮控件: fab

标题栏控件: toolbar

卡片: card

抽屉布局: drawer

列表: list

Tab: tab

ui

ui.layout(xml)

ui.inflate(xml[, parent])

ui.findView(id)

ui.finish()

ui.setContentView(view)

ui.run(callback)

ui.post(callback[, daley])

ui.statusBarColor(color)

ui.showPopupMenu(view, menu)

尺寸的单位: Dimension

Drawables

颜色

(完善中...)

语言   编辑   高级

这篇文章描述了如何在rhino中使用java。使用脚本调用Java有很多用途,它使得我们可以利用Java中现有的库,来帮助我们构建强大的脚本。我们可以通过编写脚本,来对Java程序进行测试。可以通过脚本来进行探索式编程,辅助Java的开发,所谓探索式编程,就是通过快速地编程调用库或API来探索这些库或API可以做什么,显而易见,脚本语言很适合探索式编程。

 

这里注意,ECMA标准并没有包含和Java(或者其他任何对象系统)交互的标准。本文所描述的所有内容,应该被认为是一个扩展。

 

 

 

访问 Java Packages 和 Classes节

Java的每段代码都是类的一部分,每一个JAVA类都是包的一部分。在Javascript中,脚本不属于任何package。我们可以访问Java包中的类么?

 

Rhino定义了一个顶层的变量Packages。Packages的所有属性都是Java中顶层的包,比如java和com。比如我们可以访问java包:

 

js> Packages.java

[JavaPackage java]

还有一种更方便的方式,Rhino定义了一个顶层的变量java,等价于Packages.java。所以上面的例子可以更简介地写成:

 

js> java

[JavaPackage java]

我们可以通过访问包的下层,来直接访问java类:

 

js> java.io.File

[JavaClass java.io.File]

如果你的脚本需要访问很多的Java类,每次都附带完整的包名会使得编程很麻烦。Rhino提供了一个顶层的方法importPackage,它的功能和Java的import一样。比如,我们可以导入java.io包中的所有类,然后直接通过类名File来访问java.io.File:

 

js> importPackage(java.io)

js> File

[JavaClass java.io.File]

这里importPackage(java.io)使得java.io包中的所有类(例如File)可以在顶层被访问。这和Java中的java.io.*;等价。

 

要注意Java会暗中导入java.lang.*,但是Rhino不会。因为JavaScript的顶层对象Boolean、Math、Number、Object和String和java.lang包中同名的类并不相同。因为这种冲突,建议不要用importPackage来导入java.lang包。

 

有一点要注意的,就是Rhino对于指定包名或类名时是如何处理错误的。如果java.Myclass是可访问的,Rhino会试图加载名为java.MyClass的类,如果加载失败,它会假设java.MyClass是一个包名,不会报错:

 

js> java.MyClass

[JavaPackage java.MyClass]

只有在你试图将这个对象当作类使用时,才会报错。

 

额外的包和类

额外的包和类也可以在Rhino中使用。确认你的.jar或.class文件在你的classpath里,你就可以在你的JavaScript应用中导入它们。这些包基本不会在java包中,所以你在使用时,需要在包前加上前缀"Packages"。 比如你想导入 org.mozilla.javascript 包,你应该像下面这样去使用importPackage():

 

$ java org.mozilla.javascript.tools.shell.Main

js> importPackage(Packages.org.mozilla.javascript);

js> Context.currentContext;

org.mozilla.javascript.Context@bb6ab6

偶尔,我们也会见到在一些例子中使用包的完整名称,而没有使用importPackage()。这也是可以的,只是会让你多打一些字。如果使用完整的名称,上面的例子就会变成下面这样:

 

$ java org.mozilla.javascript.tools.shell.Main

js> jsPackage = Packages.org.mozilla.javascript;

[JavaPackage org.mozilla.javascript]

js> jsPackage.Context.currentContext;

org.mozilla.javascript.Context@bb6ab6

同样,你可以通过importClass()来导入一个类,上面的例子也可以像这样写:

 

$ java org.mozilla.javascript.tools.shell.Main

js> importClass(Packages.org.mozilla.javascript.Context);

js>  Context.currentContext;

org.mozilla.javascript.Context@bb6ab6

和Java一起工作节

现在我们可以访问Java类,下一步就是要创建一个对象。方法就和在Java中一样, 用new来创建对象:

 

js> new java.util.Date()

Thu Jan 24 16:18:17 EST 2002

如果我们将创建的对象存放在JavaScript变量中,我们可以调用它的方法:

 

js> f = new java.io.File("test.txt")

test.txt

js> f.exists()

true

js> f.getName()

test.txt

静态方法和属性可以直接通过类对象来访问:

 

js> java.lang.Math.PI

3.141592653589793

js> java.lang.Math.cos(0)

1

不像Java,在JavaScript里,方法就是一个对象。它可以被评估,也可以被调用。如果我们去查看这个方法,我们可以看到这个方法所有重载的形式:

 

js> f.listFiles

function listFiles() {/*

java.io.File[] listFiles()

java.io.File[] listFiles(java.io.FilenameFilter)

java.io.File[] listFiles(java.io.FileFilter)

*/}

输出告诉我们,File类有listFiles方法的三种重载:一种不包含参数的,另一种包含一个FilenameFilter类型的参数,第三个包含一个FileFilter类型的参数。所有的方法都返回一个File对象数组。可以观察到Java方法的参数和返回类型在探索式编程中是非常有用的,尤其是在对一个方法的参数和返回对象不确定的时候。

 

另一个有助于探索式编程的特性,是可以看到对象中定义的所有方法和属性。用JavaScript的for..in , 我们可以打印这些值:

 

js> for (i in f) { print(i) }

exists

parentFile

mkdir

toString

wait

[44 others]

注意这里不仅列出了File类中的所有方法,也列出了从基类java.lang.Object中继承的方法,例如wait。这使得我们可以更好地处理那些有复杂继承关系的对象,因为我们可以看到对象中所有可用的方法。

 

Rhino可以通过属性名来方便地访问JavaBean的属性。一个JavaBean的属性foo被方法getFoo和setFoo定义,另外,一个也叫foo的boolean类型的属性,可以被isFoo来定义。比如, 下面的代码实际上调用了File对象的getName和isDirectory方法。

 

js> f.name

test.txt

js> f.directory

false

调用重载方法节

 根据参数类型选择调用方法的过程称为重载决议。在 Java 中, 重载决议在编译时执行, 而在rhino中则在运行时发生。这种差异是不可避免的, 因为 JavaScript 使用动态类型, 在2章中: 由于变量的类型直到运行时才知道, 才会发生重载决议。

 

 

例如, 请查看下面的 Java 类, 它定义了许多重载方法并调用它们。

 

 

public class Overload {

 

    public String f(Object o) { return "f(Object)"; }

    public String f(String s) { return "f(String)"; }

    public String f(int i)    { return "f(int)"; }

 

    public String g(String s, int i) { return "g(String,int)"; }

    public String g(int i, String s) { return "g(int,String)"; }

 

    public static void main(String[] args) {

        Overload o = new Overload();

        Object[] a = new Object[] { new Integer(3), "hi", Overload.class };

        for (int i = 0; i != a.length; ++i)

            System.out.println(o.f(a[i]));

    }

}

当我们编译和执行程序, 它产生输出

 

f(Object)

f(Object)

f(Object)

但是, 如果我们编写一个类似的脚本

 

var o = new Packages.Overload();

var a = [ 3, "hi", Packages.Overload ];

for (var i = 0; i != a.length; ++i)

    print(o.f(a[i]));

并且运行它,将会输出

 

f(int)

f(String)

f(Object)

因为Rhino在运行时选择重载方法, 所以它会调用与该参数匹配的更具体的类型。 同时, 在编译时, Java 只在参数的类型上选择重载方法。

 

尽管这有利于选择一种方法,这种方法可能是每个调用的更好匹配,但它确实对性能有影响,因为每次调用时都要做更多的工作。事实上,这种性能代价在实际应用中并不明显。

 

因为重载决议发生在运行时,它可能在运行时失败。例如,如果我们用两个整数调用重载方法g,我们就会得到一个错误,因为方法的两个形式都比另一个更接近参数类型:

 

js> o.g(3,4)

js:"<stdin>", line 2: The choice of Java method Overload.g

matching JavaScript argument types (number,number) is ambiguous;

candidate methods are:

class java.lang.String g(java.lang.String,int)

class java.lang.String g(int,java.lang.String)

http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html 提供了一个更精确的重载语义定义。

 

  实现Java接口节

 

 

现在我们可以访问Java类,创建Java对象,并访问这些对象的字段、方法和属性,我们就可以轻松掌握大量的功能。但是,在少数情况下是不够用的:Java中的很多API通过提供客户端必须实现的接口来工作。其中一个例子就是Thread类:其构造函数Runnable包含一个run方法,这个方法在新线程启动时被调用。

 

为了满足这种需求,Rhino提供了创建新的Java对象实现的接口的能力。首先,我们必须定义一个JavaScript对象,其中的函数属性的名称与Java接口所需的方法名称相匹配。要实现一个Runnable ,我们只需要定义一个不带参数的run单方法。如果你还记得第3章,可以用{ propertyName: value}符号定义一个JavaScript对象。我们可以在这里结合函数表达式使用这个语法来用一个 run方法定义一个JavaScript对象:

 

js> obj = { run: function () { print("\nrunning"); } }

[object Object]

js> obj.run()

 

running

 

 

现在我们可以通过构建一个 Runnable 来实现 Runnable 接口的对象:

 

js> r = new java.lang.Runnable(obj);

 

 

js> r = new java.lang.Runnable(obj);

[object JavaObject]

在Java中,不可能在接口上使用new运算符,因为没有可用的实现。Rhino从JavaScript对象中获取实现obj。现在我们有一个对象实现Runnable,我们可以创建Thread并运行它。我们定义的函数run 将在新线程上调用。

 

js> t = new java.lang.Thread(r)

Thread[Thread-2,5,main]

js> t.start()

js>

 

running

 

 

最终js提示和新线程的输出可能以任意顺序显示,具体取决于线程调度。

 

在后台,Rhino为一个新的Java类生成字节码,该类实现 Runnable 并转发对其 run方法的所有调用,并转发给关联的JavaScript对象。实现此类的对象称为Java适配器。因为转发到JavaScript是在运行时发生的,所以可能会延迟定义实现接口的方法直到它们被调用。虽然省略必要的方法对大编程来说是一种糟糕的做法,但它对小脚本和探索性编程很有用。

 

 

 

JavaAdapter构造函数节

在前面的章节中,我们使用 new 运算符与Java接口创建Java适配器。这种方法有其局限性:不可能实现多个接口,也不能扩展非抽象类。因为这些原因,有一个 JavaAdapter 构造函数。

 

JavaAdapter构造函数的语法是:

 

new JavaAdapter(javaIntfOrClass, [javaIntf, ..., javaIntf,] javascriptObject)

这里javaIntfOrClass是一个实现的接口或一个扩展的类,并且javaIntf是实现接口的接口。而javascriptObject 则包含从Java适配器调用的方法的JavaScript对象。

 

在实践中,几乎不需要JavaAdapter 直接调用构造函数。大多数情况下,使用new运算符之前的语法就足够了。

 

作为Java接口的JavaScript函数节

通常我们只需要使用一种方法实现一个接口,就像前面的 Runnable 例子或者提供各种事件监听器实现一样。为了方便这个,Rhino允许在这种接口传递JavaScript函数。该函数被称为接口方法的实现。

 

这里是简化的 Runnable 实例:

 

js> t = java.lang.Thread(function () { print("\nrunning"); });

Thread[Thread-0,5,main]

js> t.start()

js>

running

如果所有的方法都具有相同的签名,Rhino还允许使用JavaScript函数作为Java接口的实现方法。当调用函数时,Rhino将方法的名称作为附加参数传递。函数可以使用它来代表被调用的方法:

 

js> var frame = new Packages.javax.swing.JFrame();

js> frame.addWindowListener(function(event, methodName) {

if (methodName == "windowClosing") {     

            print("Calling System.exit()..."); java.lang.System.exit(0);

}

    });

js> frame.setSize(100, 100);

js> frame.visible = true;

true

js> Calling System.exit()...

创建Java数组节

Rhino不提供创建Java数组的特殊语法。你必须使用这个 java.lang.reflect.Array 类来达到这个目的。要创建一个由五个Java字符串组成的数组,可以进行以下调用:

 

js> a = java.lang.reflect.Array.newInstance(java.lang.String, 5);

[Ljava.lang.String;@7ffe01

要创建一个基本类型数组,我们必须使用 java.lang 包中相关对象类中定义的特殊TYPE字段。例如,要创建一个字节数组,我们必须使用特殊字段 java.lang.Byte.TYPE:

 

js> a = java.lang.reflect.Array.newInstance(java.lang.Character.TYPE, 2);

[C@7a84e4

而且结果值是允许被使用在该类型的Java数组的任何地方。

 

js> a[0] = 104

104

js> a[1] = 105

105

js> new java.lang.String(a)

hi

Java字符串和JavaScript字符串节

请记住,Java字符串和JavaScript字符串是不一样的。Java字符串类型的实例,java.lang.String ,并具有由该类定义的所有方法。JavaScript字符串具有由...定义的方法,String.prototype. 最常见的绊脚石是 length, 这是Java字符串方法和JavaScript字符串的动态属性:

 

js> javaString = new java.lang.String("Java")

Java

js> jsString = "JavaScript"

JavaScript

js> javaString.length()

4

js> jsString.length

10

Rhino 在减少这两种类型之间的差异方面提供了一些帮助。首先,您可以将JavaScript字符串传递给需要Java字符串的Java方法,Rhino将执行转换。实际上,我们在前面java.lang.String 例子中的构造函数调用中看到了这个特性。

 

如果java.lang.String 类尚未定义它们,Rhino还会使JavaScript方法可用于Java字符串。例如:

 

js> javaString.match(/a.*/)

ava

JavaImporter 构造函数节

JavaImporter是一个新的全局构造函数,它允许在脚本化Java时省略显式的包名称:

 

var SwingGui = JavaImporter(Packages.javax.swing,

                            Packages.javax.swing.event,

                            Packages.javax.swing.border,

                            java.awt.event,

                            java.awt.Point,

                            java.awt.Rectangle,

                            java.awt.Dimension);

...

 

with (SwingGui) {

    var mybutton = new JButton(test);

    var mypoint = new Point(10, 10);

    var myframe = new JFrame();

...

}

以前,这样的功能仅适用于将 org.mozilla.javascript.ImporterTopLevel 用作顶级作用域的嵌入。这个类提供额外的 importPackage() 和importClass() 全局函数的脚本,但其广泛的使用有污染Java类名的全局命名空间的趋势,还有防止垃圾收集加载类。

 

详情请参阅 Bugzilla 245882.

 

Java 异常节

 

 

JavaScript代码使用 try ... catch 语句可以捕获Java方法抛出的异常。Rhino将Java异常封装到具有以下属性的错误对象中:

 

javaException:Java方法抛出的原始异常

rhinoException:由Rhino运行时包装的异常

instanceof运算符可用于查询异常的类型:

 

 

 

try {

    java.lang.Class.forName("NonExistingClass");

} catch (e) {

    if (e.javaException instanceof java.lang.ClassNotFoundException) {

       print("Class not found");

    }

}

Rhino 还支持对 try... catch 语句的扩展,允许定义条件捕获异常:

 

function classForName(name) {

    try {

        return java.lang.Class.forName(name);

    } catch (e if e.javaException instanceof java.lang.ClassNotFoundException) {

        print("Class " + name + " not found");

    } catch (e if e.javaException instanceof java.lang.NullPointerException) {

        print("Class name is null");

    }

}

 

classForName("NonExistingClass");

classForName(null);

  • 8
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值