Auto.js使用JavaScript作为脚本语言,目前使用Rhino 1.7.7.2作为脚本引擎,支持ES5与部分ES6特性。
本文档的章节大致上是以模块来分的,总体上可以分成"自动操作"类模块(控件操作、触摸模拟、按键模拟等)和其他类模块(设备、应用、界面等)。
"自动操作"的部分又可以大致分为基于控件和基于坐标的操作。基于坐标的操作是传统按键精灵、触摸精灵等脚本软件采用的方式,通过屏幕坐标来点击、长按指定位置模拟操作,从而到达目的。例如click(100, 200), press(100, 200, 500)等。这种方式在游戏类脚本中比较有可行性,结合找图找色、坐标放缩功能也能达到较好的兼容性。但是,这种方式对一般软件脚本却难以达到想要的效果,而且这种方式需要安卓7.0版本以上或者root权限才能执行。所以对于一般软件脚本(例如批量添加联系人、自动提取短信验证码等等),我们采用基于控件的模拟操作方式,结合通知事情、按键事情等达成更好的工作流。这些部分的文档参见基于控件的操作和基于坐标的操作。
其他部分主要包括:
除此之外,Auto.js内置了对Promise。
点击脚本右边的菜单按钮->更多->定时任务即可定时运行脚本,但是必须保持Auto.js后台运行(自启动白名单、电源管理白名单等)。同时,可以在脚本的开头使用device.wakeUp()来唤醒屏幕;但是,Auto.js没有解锁屏幕的功能,因此难以在有锁屏密码的设备上达到效果。
如果一个脚本是用intent"启动"的,比如定时任务中的特定事件(网络状态变化等)触发而启动的,则可以通过engines.myEngine().execArgv.intent获取启动的intent,从而获取外部参数。
如果除了单脚本以外还有其他脚本、图片、音乐等资源一起打包,则需要使用项目功能。
点击Auto.js的"+"号,选择项目,填写项目名称、包名等信息以后,点击"√"即可新建一个项目。可以在项目中放多个脚本、模块、资源文件,点击项目工具栏的apk打包图标即可打包一个项目,点击工具栏可以重新配置项目。
例如,主脚本要读取同一文件夹下的图片1.png,再执行找图,则可以通过images.read("./1.png")来读取,其中"./1.png"表示同一目录1.png图片;ui中的图片控件要引用同一文件夹的2.png图片则为。Auto.js内置的函数和模块都支持相对路径,但是,其他情况则需要使用files.path()函数来把相对路径转换为绝对路径。
需要使用项目功能。新建项目后,修改项目下的project.json文件,增加以下条目:
"launchConfig": {
"hideLogs": true
}
例如:
{
"name": "项目名称",
"versionName": "1.0.0",
"versionCode": 1,
"packageName": "org.autojs.example",
"main": "main.js",
"launchConfig": {
"hideLogs": true
}
}
"launchConfig"表示启动配置,"hideLogs"表示隐藏日志。
参见项目与项目配置。
由于Auto.js支持直接调用Android的API,对于Auto.js没有内置的函数,可以直接通过修改Android代码为JavaScript代码实现。例如旋转图片的Android代码为:
import android.graphics.Bitmap;
import android.graphics.Matrix;
public static Bitmap rotate(final Bitmap src,
final int degrees,
final float px,
final float py) {
if (degrees == 0) return src;
Matrix matrix = new Matrix();
matrix.setRotate(degrees, px, py);
Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
return ret;
}
转换为JavaScript的代码后为:
importClass(android.graphics.Bitmap);
importClass(android.graphics.Matrix);
function rotate(src, degrees, px, py){
if (degrees == 0) return src;
var matrix = new Matrix();
matrix.setRotate(degrees, px, py);
var ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
return ret;
}
有关调用Android和Java的API的更多信息,参见Work with Java。
本文档为Auto.js的文档,解释了Auto.js各个模块的API的使用方法、作用和例子。
文档借助Node.js的文档构建工具生成,并在github上开源(https://github.com/hyb1996/AutoJs-Docs ),目前由开发者维护。
由于Auto.js处于活跃的更新和开发状态,API可能随时有变动,我们用Stability来标记模块、函数的稳定性。这些标记包括:
Stability: 0 - Deprecated
弃用的函数、模块或特性,在未来的更新中将很快会被移除或更改。应该在脚本中移除对这些函数的使用,以免后续出现意料之外的问题。
Stability: 1 - Experimental
实验性的函数、模块或特性,在未来的更新中可能会更改或移除。应该谨慎使用这些函数或模块,或者仅用作临时或试验用途。
Stability: 2 - Stable
稳定的函数、模块或特性,在未来的更新中这些模块已有的函数一般不会被更改,会保证后向兼容性。
先看一个例子,下面是基于控件的操作模拟的章节中input函数的部分说明。
input([i, ]text)
input表示函数名,括号内的[i, ]text为函数的参数。下面是参数列表,"number"表示参数i的类型为数值,"string"表示参数text的类型为字符串。
例如input(1, "啦啦啦"),执行这个语句会在屏幕上的第2个输入框处输入"啦啦啦"。
方括号[ ]表示参数为可选参数。也就是说,可以省略i直接调用input。例如input("嘿嘿嘿"),按照文档,这个语句会在屏幕上所有输入框输入"嘿嘿嘿"。
调用有可选参数的函数时请不要写上方括号。
我们再看第二个例子。图片和图色处理中detectsColor函数的部分说明。
images.detectsColor(image, color, x, y[, threshold = 16, algorithm = "diff"])
同样地,[, threshold = 16, algorithm = "rgb"]为可选参数,并且,等于号=后面的值为参数的默认值。也就是如果不指定该参数,则该参数将会为这个值。
例如 images.detectsColor(captureScreen(), "#112233", 100, 200) 相当于 images.detectsColor(captureScreen(), "#112233", 100, 200, 16, "rgb"), 而images.detectsColor(captureScreen(), "#112233", 100, 200, 64) 相当于images.detectsColor(captureScreen(), "#112233", 100, 200, 64, "rgb")。
调用有可选参数及默认值的函数时请不要写上方括号和等于号。
app模块提供一系列函数,用于使用其他应用、与其他应用交互。例如发送意图、打开文件、发送邮件等。
同时提供了方便的进阶函数startActivity和sendBroadcast,用他们可完成app模块没有内置的和其他应用的交互。
当前软件版本号,整数值。例如160, 256等。
如果在Auto.js中运行则为Auto.js的版本号;在打包的软件中则为打包软件的版本号。
toastLog(app.versionCode);
当前软件的版本名称,例如"3.0.0 Beta"。
如果在Auto.js中运行则为Auto.js的版本名称;在打包的软件中则为打包软件的版本名称。
toastLog(app.verionName);
Auto.js版本号,整数值。例如160, 256等。
Auto.js版本名称,例如"3.0.0 Beta"。
通过应用名称启动应用。如果该名称对应的应用不存在,则返回false; 否则返回true。如果该名称对应多个应用,则只启动其中某一个。
该函数也可以作为全局函数使用。
launchApp("Auto.js");
通过应用包名启动应用。如果该包名对应的应用不存在,则返回false;否则返回true。
该函数也可以作为全局函数使用。
//启动微信
launch("com.tencent.mm");
相当于app.launch(packageName)。
获取应用名称对应的已安装的应用的包名。如果该找不到该应用,返回null;如果该名称对应多个应用,则只返回其中某一个的包名。
该函数也可以作为全局函数使用。
var name = getPackageName("QQ"); //返回"com.tencent.mobileqq"
获取应用包名对应的已安装的应用的名称。如果该找不到该应用,返回null。
该函数也可以作为全局函数使用。
var name = getAppName("com.tencent.mobileqq"); //返回"QQ"
打开应用的详情页(设置页)。如果找不到该应用,返回false; 否则返回true。
该函数也可以作为全局函数使用。
用其他应用查看文件。文件不存在的情况由查看文件的应用处理。
如果找不出可以查看该文件的应用,则抛出ActivityNotException。
//查看文本文件
app.viewFile("/sdcard/1.txt");
用其他应用编辑文件。文件不存在的情况由编辑文件的应用处理。
如果找不出可以编辑该文件的应用,则抛出ActivityNotException。
//编辑文本文件
app.editFile("/sdcard/1.txt/);
卸载应用。执行后会会弹出卸载应用的提示框。如果该包名的应用未安装,由应用卸载程序处理,可能弹出"未找到应用"的提示。
//卸载QQ
app.uninstall("com.tencent.mobileqq");
用浏览器打开网站url。
如果没有安装浏览器应用,则抛出ActivityNotException。
根据选项options调用邮箱应用发送邮件。这些选项均是可选的。
如果没有安装邮箱应用,则抛出ActivityNotException。
//发送邮件给[email protected]和[email protected]。
app.sendEmail({
email: ["[email protected]", "[email protected]"],
subject: "这是一个邮件标题",
text: "这是邮件正文"
});
启动Auto.js的特定界面。该函数在Auto.js内运行则会打开Auto.js内的界面,在打包应用中运行则会打开打包应用的相应界面。
app.startActivity("console");
Intent(意图) 是一个消息传递对象,您可以使用它从其他应用组件请求操作。尽管 Intent 可以通过多种方式促进组件之间的通信,但其基本用例主要包括以下三个:
本模块提供了构建Intent的函数(app.intent()), 启动Activity的函数app.startActivity(), 发送广播的函数app.sendBroadcast()。
使用这些方法可以用来方便的调用其他应用。例如直接打开某个QQ号的个人卡片页,打开某个QQ号的聊天窗口等。
var qq = "2732014414";
app.startActivity({
action: "android.intent.action.VIEW",
data:"mqq://im/chat?chat_type=wpa&version=1&src_type=web&uin=" + qq,
packageName: "com.tencent.mobileqq",
});
[v4.1.0新增]
[v4.1.0新增]
根据选项,构造一个意图Intent对象。
例如:
//打开应用来查看图片文件
var i = app.intent({
action: "VIEW",
type: "image/png",
data: "file:///sdcard/1.png"
});
context.startActivity(i);
需要注意的是,除非应用专门暴露Activity出来,否则在没有root权限的情况下使用intent是无法跳转到特定Activity、应用的特定界面的。例如我们能通过Intent跳转到QQ的分享界面,是因为QQ对外暴露了分享的Activity;而在没有root权限的情况下,我们无法通过intent跳转到QQ的设置界面,因为QQ并没有暴露这个Activity。
但如果有root权限,则在intent的参数加上"root": true即可。例如使用root权限跳转到Auto.js的设置界面为:
app.startActivity({
packageName: "org.autojs.autojs",
className: "org.autojs.autojs.ui.settings.SettingsActivity_",
root: true
});
另外,关于intent的参数如何获取的问题,一些intent是意外发现并且在网络中传播的(例如跳转QQ聊天窗口是因为QQ给网页提供了跳转到客服QQ的方法),如果要自己获取活动的intent的参数,可以通过例如"intent记录","隐式启动"等应用拦截内部intent或者查询暴露的intent。其中拦截内部intent需要XPosed框架,或者可以通过反编译等手段获取参数。总之,没有简单直接的方法。
更多信息,请百度安卓Intent或参考Android指南: Intent。
根据选项构造一个Intent,并启动该Activity。
app.startActivity({
action: "SEND",
type: "text/plain",
data: "file:///sdcard/1.txt"
});
根据选项构造一个Intent,并发送该广播。
根据选项构造一个Intent,并启动该服务。
[v4.1.0新增]
发送以上特定名称的广播可以触发Auto.js的布局分析,方便脚本调试。这些广播在Auto.js发送才有效,在打包的脚本上运行将没有任何效果。
app.sendBroadcast("inspect_layout_bounds");
[v4.1.0新增]
根据选项构造一个Intent,转换为对应的shell的intent命令的参数。
例如:
shell("am start " + app.intentToShell({
packageName: "org.autojs.autojs",
className: "org.autojs.autojs.ui.settings.SettingsActivity_"
}), true);
参见intent参数的规范。
[v4.1.0新增]
解析uri字符串并返回相应的Uri对象。即使Uri格式错误,该函数也会返回一个Uri对象,但之后如果访问该对象的scheme, path等值可能因解析失败而返回null。
需要注意的是,在高版本Android上,由于系统限制直接在Uri暴露文件的绝对路径,因此如果uri字符串是文件file://...,返回的Uri会是诸如content://...的形式。
[v4.1.0新增]
从一个文件路径创建一个uri对象。需要注意的是,在高版本Android上,由于系统限制直接在Uri暴露文件的绝对路径,因此返回的Uri会是诸如content://...的形式。
全局变量和函数在所有模块中均可使用。 但以下变量的作用域只在模块内,详见 module:
一些模块中的函数为了使用方便也可以直接全局使用,这些函数在此不再赘述。例如timers模块的setInterval, setTimeout等函数。
暂停运行n毫秒的时间。1秒等于1000毫秒。
//暂停5毫秒
sleep(5000);
返回最近一次监测到的正在运行的应用的包名,一般可以认为就是当前正在运行的应用的包名。
此函数依赖于无障碍服务,如果服务未启动,则抛出异常并提示用户启动。
返回最近一次监测到的正在运行的Activity的名称,一般可以认为就是当前正在运行的Activity的名称。
此函数依赖于无障碍服务,如果服务未启动,则抛出异常并提示用户启动。
设置剪贴板内容。此剪贴板即系统剪贴板,在一般应用的输入框中"粘贴"既可使用。
setClip("剪贴板文本");
返回系统剪贴板的内容。
toast("剪贴板内容为:" + getClip());
以气泡显示信息message几秒。(具体时间取决于安卓系统,一般都是2秒)
注意,信息的显示是"异步"执行的,并且,不会等待信息消失程序才继续执行。如果在循环中执行该命令,可能出现脚本停止运行后仍然有不断的气泡信息出现的情况。 例如:
for(var i = 0; i < 100; i++){
toast(i);
}
运行这段程序以后,会很快执行完成,且不断弹出消息,在任务管理中关闭所有脚本也无法停止。 要保证气泡消息才继续执行可以用:
for(var i = 0; i < 100; i++){
toast(i);
sleep(2000);
}
或者修改toast函数:
var _toast_ = toast;
toast = function(message){
_toast_(message);
sleep(2000);
}
for(var i = 0; i < 100; i++){
toast(i);
}
相当于toast(message);log(message)。显示信息message并在控制台中输出。参见console.log。
等待指定的Activity出现,period为检查Activity的间隔。
等待指定的应用出现。例如waitForPackage("com.tencent.mm")为等待当前界面为微信。
立即停止脚本运行。
立即停止是通过抛出ScriptInterrupttedException来实现的,因此如果用try...catch把exit()函数的异常捕捉,则脚本不会立即停止,仍会运行几行后再停止。
返回一个在[min...max]之间的随机数。例如random(0, 2)可能产生0, 1, 2。
返回在[0, 1)的随机浮点数。
表示此脚本需要Android API版本达到指定版本才能运行。例如requiresApi(19)表示脚本需要在Android 4.4以及以上运行。
调用该函数时会判断运行脚本的设备系统的版本号,如果没有达到要求则抛出异常。
可以参考以下Android API和版本的对照表:
平台版本: API级别
Android 7.0: 24
Android 6.0: 23
Android 5.1: 22
Android 5.0: 21
Android 4.4W: 20
Android 4.4: 19
Android 4.3: 18
表示此脚本需要Auto.js版本达到指定版本才能运行。例如requiresAutojsVersion("3.0.0 Beta")表示脚本需要在Auto.js 3.0.0 Beta以及以上运行。
调用该函数时会判断运行脚本的Auto.js的版本号,如果没有达到要求则抛出异常。
version参数可以是整数表示版本号,例如requiresAutojsVersion(250);也可以是字符串格式表示的版本,例如"3.0.0 Beta", "3.1.0 Alpha4", "3.2.0"等。
可以通过app.autojs.versionCode和app.autojs.versionName获取当前的Auto.js版本号和版本。
动态申请安卓的权限。例如:
//请求GPS权限
runtime.requestPermissions(["access_fine_location"]);
尽管安卓有很多权限,但必须写入Manifest才能动态申请,为了防止权限的滥用,目前Auto.js只能额外申请两个权限:
您可以通过APK编辑器来增加Auto.js以及Auto.js打包的应用的权限。
安卓所有的权限列表参见Permissions Overview。(并没有用)
加载目标jar文件,加载成功后将可以使用该Jar文件的类。
// 加载jsoup.jar
runtime.loadJar("./jsoup.jar");
// 使用jsoup解析html
importClass(org.jsoup.Jsoup);
log(Jsoup.parse(files.read("./test.html")));
(jsoup是一个Java实现的解析Html DOM的库,可以在Jsoup下载)
加载目标dex文件,加载成功后将可以使用该dex文件的类。
因为加载jar实际上是把jar转换为dex再加载的,因此加载dex文件会比jar文件快得多。可以使用Android SDK的build tools的dx工具把jar转换为dex。
全局变量。一个android.content.Context对象。
注意该对象为ApplicationContext,因此不能用于界面、对话框等的创建。
Stability: 2 - Stable
控制台模块提供了一个和Web浏览器中相似的用于调试的控制台。用于输出一些调试信息、中间结果等。 console模块中的一些函数也可以直接作为全局函数使用,例如log, print等。
显示控制台。这会显示一个控制台的悬浮窗(需要悬浮窗权限)。
隐藏控制台悬浮窗。
清空控制台。
打印到控制台,并带上换行符。 可以传入多个参数,第一个参数作为主要信息,其他参数作为类似于 printf(3) 中的代替值(参数都会传给 util.format())。
const count = 5;
console.log('count: %d', count);
// 打印: count: 5 到 stdout
console.log('count:', count);
// 打印: count: 5 到 stdout
详见 util.format()。
该函数也可以作为全局函数使用。
与console.log类似,但输出结果以灰色字体显示。输出优先级低于log,用于输出观察性质的信息。
与console.log类似,但输出结果以绿色字体显示。输出优先级高于log, 用于输出重要信息。
与console.log类似,但输出结果以蓝色字体显示。输出优先级高于info, 用于输出警告信息。
与console.log类似,但输出结果以红色字体显示。输出优先级高于warn, 用于输出错误信息。
断言。如果value为false则输出错误信息message并停止脚本运行。
var a = 1 + 1;
console.assert(a == 2, "加法出错啦");
[v4.1.0新增]
启动一个定时器,用以计算一个操作的持续时间。 定时器由一个唯一的 label 标识。 当调用 console.timeEnd() 时,可以使用相同的 label 来停止定时器,并以毫秒为单位将持续时间输出到控制台。 重复启动同一个标签的定时器会覆盖之前启动同一标签的定时器。
[v4.1.0新增]
停止之前通过调用 console.time() 启动的定时器,并打印结果到控制台。 调用 console.timeEnd() 后定时器会被删除。如果不存在标签指定的定时器则会打印 NaNms。
console.time('求和');
var sum = 0;
for(let i = 0; i < 100000; i++){
sum += i;
}
console.timeEnd('求和');
// 打印 求和: xxx ms
[v4.1.0新增]
与console.log类似,同时会打印出调用这个函数所在的调用栈信息(即当前运行的文件、行数等信息)。
console.trace('Show me');
// 打印: (堆栈跟踪会根据被调用的跟踪的位置而变化)
// Show me
// at
与console.log一样输出信息,并在控制台显示输入框等待输入。按控制台的确认按钮后会将输入的字符串用eval计算后返回。
部分机型可能会有控制台不显示输入框的情况,属于bug。
例如:
var n = console.input("请输入一个数字:");
//输入123之后:
toast(n + 1);
//显示124
与console.log一样输出信息,并在控制台显示输入框等待输入。按控制台的确认按钮后会将输入的字符串直接返回。
部分机型可能会有控制台不显示输入框的情况,属于bug。
例如:
var n = console.rawInput("请输入一个数字:");
//输入123之后:
toast(n + 1);
//显示1231
设置控制台的大小,单位像素。
console.show();
//设置控制台大小为屏幕的四分之一
console.setSize(device.width / 2, device.height / 2);
设置控制台的位置,单位像素。
console.show();
console.setPosition(100, 100);
[v4.1.0新增]
设置日志保存的路径和配置。例如把日志保存到"/sdcard/1.txt":
console.setGlobalLogConfig({
"file": "/sdcard/1.txt"
});
注意该函数会影响所有脚本的日志记录。
相当于log(text)。
Stability: 2 - Stable
本章节介绍了一些使用坐标进行点击、滑动的函数。这些函数有的需要安卓7.0以上,有的需要root权限。
要获取要点击的位置的坐标,可以在开发者选项中开启"指针位置"。
基于坐标的脚本通常会有分辨率的问题,这时可以通过setScreenMetrics()函数来进行自动坐标放缩。这个函数会影响本章节的所有点击、长按、滑动等函数。通过设定脚本设计时的分辨率,使得脚本在其他分辨率下自动放缩坐标。
控件和坐标也可以相互结合。一些控件是无法点击的(clickable为false), 无法通过.click()函数来点击,这时如果安卓版本在7.0以上或者有root权限,就可以通过以下方式来点击:
//获取这个控件
var widget = id("xxx").findOne();
//获取其中心位置并点击
click(widget.bounds().centerX(), widget.bounds().centerY());
//如果用root权限则用Tap
设置脚本坐标点击所适合的屏幕宽高。如果脚本运行时,屏幕宽度不一致会自动放缩坐标。
例如在1920*1080的设备中,某个操作的代码为
setScreenMetrics(1080, 1920);
click(800, 200);
longClick(300, 500);
那么在其他设备上AutoJs会自动放缩坐标以便脚本仍然有效。例如在540 * 960的屏幕中click(800, 200)实际上会点击位置(400, 100)。
Stability: 2 - Stable
注意以下命令只有Android7.0及以上才有效
模拟点击坐标(x, y),并返回是否点击成功。只有在点击执行完成后脚本才继续执行。
一般而言,只有点击过程(大约150毫秒)中被其他事件中断(例如用户自行点击)才会点击失败。
使用该函数模拟连续点击时可能有点击速度过慢的问题,这时可以用press()函数代替。
模拟长按坐标(x, y), 并返回是否成功。只有在长按执行完成(大约600毫秒)时脚本才会继续执行。
一般而言,只有长按过程中被其他事件中断(例如用户自行点击)才会长按失败。
模拟按住坐标(x, y), 并返回是否成功。只有按住操作执行完成时脚本才会继续执行。
如果按住时间过短,那么会被系统认为是点击;如果时长超过500毫秒,则认为是长按。
一般而言,只有按住过程中被其他事件中断才会操作失败。
一个连点器的例子如下:
//循环100次
for(var i = 0; i < 100; i++){
//点击位置(500, 1000), 每次用时1毫秒
press(500, 1000, 1);
}
模拟从坐标(x1, y1)滑动到坐标(x2, y2),并返回是否成功。只有滑动操作执行完成时脚本才会继续执行。
一般而言,只有滑动过程中被其他事件中断才会滑动失败。
模拟手势操作。例如gesture(1000, [0, 0], [500, 500], [500, 1000])为模拟一个从(0, 0)到(500, 500)到(500, 100)的手势操作,时长为2秒。
同时模拟多个手势。每个手势的参数为[delay, duration, 坐标], delay为延迟多久(毫秒)才执行该手势;duration为手势执行时长;坐标为手势经过的点的坐标。其中delay参数可以省略,默认为0。
例如手指捏合:
gestures([0, 500, [800, 300], [500, 1000]],
[0, 500, [300, 1500], [500, 1000]]);
Stability: 2 - Stable
RootAutomator是一个使用root权限来模拟触摸的对象,用它可以完成触摸与多点触摸,并且这些动作的执行没有延迟。
一个脚本中最好只存在一个RootAutomator,并且保证脚本结束退出他。可以在exit事件中退出RootAutomator,例如:
var ra = new RootAutomator();
events.on('exit', function(){
ra.exit();
});
//执行一些点击操作
...
注意以下命令需要root权限
点击位置(x, y)。其中id是一个整数值,用于区分多点触摸,不同的id表示不同的"手指",例如:
var ra = new RootAutomator();
//让"手指1"点击位置(100, 100)
ra.tap(100, 100, 1);
//让"手指2"点击位置(200, 200);
ra.tap(200, 200, 2);
ra.exit();
如果不需要多点触摸,则不需要id这个参数。 多点触摸通常用于手势或游戏操作,例如模拟双指捏合、双指上滑等。
某些情况下可能存在tap点击无反应的情况,这时可以用RootAutomator.press()函数代替。
模拟一次从(x1, y1)到(x2, y2)的时间为duration毫秒的滑动。
模拟按下位置(x, y),时长为duration毫秒。
模拟长按位置(x, y)。
以上为简单模拟触摸操作的函数。如果要模拟一些复杂的手势,需要更底层的函数。
模拟手指按下位置(x, y)。
模拟移动手指到位置(x, y)。
模拟手指弹起。
Stability: 1 - Experimental
注意:本章节的函数在后续版本很可能有改动!请勿过分依赖本章节函数的副作用。推荐使用RootAutomator代替本章节的触摸函数。
以下函数均需要root权限,可以实现任意位置的点击、滑动等。
例如:
Tap(100, 100);
sleep(500);
注意,动作的执行可能无法被停止,例如:
for(var i = 0; i < 100; i++){
Tap(100, 100);
}
这段代码执行后可能会出现在任务管理中停止脚本后点击仍然继续的情况。 因此,强烈建议在每个动作后加上延时:
for(var i = 0; i < 100; i++){
Tap(100, 100);
sleep(500);
}
点击位置(x, y), 您可以通过"开发者选项"开启指针位置来确定点击坐标。
滑动。从(x1, y1)位置滑动到(x2, y2)位置。
Stability: 2 - Stable
device模块提供了与设备有关的信息与操作,例如获取设备宽高,内存使用率,IMEI,调整设备亮度、音量等。
此模块的部分函数,例如调整音量,需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。
设备屏幕分辨率宽度。例如1080。
设备屏幕分辨率高度。例如1920。
Either a changelist number, or a label like "M4-rc20".
修订版本号,或者诸如"M4-rc20"的标识。
The name of the underlying board, like "goldfish".
设备的主板(?)型号。
The consumer-visible brand with which the product/hardware will be associated, if any.
与产品或硬件相关的厂商品牌,如"Xiaomi", "Huawei"等。
The name of the industrial design.
设备在工业设计中的名称。
The end-user-visible name for the end product.
设备型号。
The name of the overall product.
整个产品的名称。
The system bootloader version number.
设备Bootloader的版本。
The name of the hardware (from the kernel command line or /proc).
设备的硬件名称(来自内核命令行或者/proc)。
A string that uniquely identifies this build. Do not attempt to parse this value.
构建(build)的唯一标识码。
A hardware serial number, if available. Alphanumeric only, case-insensitive.
硬件序列号。
The user-visible SDK version of the framework; its possible values are defined in Build.VERSION_CODES.
安卓系统API版本。例如安卓4.4的sdkInt为19。
The internal value used by the underlying source control to represent this build. E.g., a perforce changelist number or a git hash.
The user-visible version string. E.g., "1.0" or "3.4b5".
Android系统版本号。例如"5.0", "7.1.1"。
The base OS build the product is based on.
The user-visible security patch level.
安全补丁程序级别。
The current development codename, or the string "REL" if this is a release build.
开发代号,例如发行版是"REL"。
返回设备的IMEI.
返回设备的Android ID。
Android ID为一个用16进制字符串表示的64位整数,在设备第一次使用时随机生成,之后不会更改,除非恢复出厂设置。
返回设备的Mac地址。该函数需要在有WLAN连接的情况下才能获取,否则会返回null。
可能的后续修改:未来可能增加有root权限的情况下通过root权限获取,从而在没有WLAN连接的情况下也能返回正确的Mac地址,因此请勿使用此函数判断WLAN连接。
返回当前的(手动)亮度。范围为0~255。
返回当前亮度模式,0为手动亮度,1为自动亮度。
设置当前手动亮度。如果当前是自动亮度模式,该函数不会影响屏幕的亮度。
此函数需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。
设置当前亮度模式。
此函数需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。
返回当前媒体音量。
返回当前通知音量。
返回当前闹钟音量。
返回媒体音量的最大值。
返回通知音量的最大值。
返回闹钟音量的最大值。
设置当前媒体音量。
此函数需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。
设置当前通知音量。
此函数需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。
设置当前闹钟音量。
此函数需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。
返回当前电量百分比。
返回设备是否正在充电。
返回设备内存总量,单位字节(B)。1MB = 1024 * 1024B。
返回设备当前可用的内存,单位字节(B)。
返回设备屏幕是否是亮着的。如果屏幕亮着,返回true; 否则返回false。
需要注意的是,类似于vivo xplay系列的息屏时钟不属于"屏幕亮着"的情况,虽然屏幕确实亮着但只能显示时钟而且不可交互,此时isScreenOn()也会返回false。
唤醒设备。包括唤醒设备CPU、屏幕等。可以用来点亮屏幕。
如果屏幕没有点亮,则唤醒设备。
保持屏幕常亮。
此函数无法阻止用户使用锁屏键等正常关闭屏幕,只能使得设备在无人操作的情况下保持屏幕常亮;同时,如果此函数调用时屏幕没有点亮,则会唤醒屏幕。
在某些设备上,如果不加参数timeout,只能在Auto.js的界面保持屏幕常亮,在其他界面会自动失效,这是因为设备的省电策略造成的。因此,建议使用比较长的时长来代替"一直保持屏幕常亮"的功能,例如device.keepScreenOn(3600 * 1000)。
可以使用device.cancelKeepingAwake()来取消屏幕常亮。
//一直保持屏幕常亮
device.keepScreenOn()
保持屏幕常亮,但允许屏幕变暗来节省电量。此函数可以用于定时脚本唤醒屏幕操作,不需要用户观看屏幕,可以让屏幕变暗来节省电量。
此函数无法阻止用户使用锁屏键等正常关闭屏幕,只能使得设备在无人操作的情况下保持屏幕常亮;同时,如果此函数调用时屏幕没有点亮,则会唤醒屏幕。
可以使用device.cancelKeepingAwake()来取消屏幕常亮。
取消设备保持唤醒状态。用于取消device.keepScreenOn(), device.keepScreenDim()等函数设置的屏幕常亮。
使设备震动一段时间。
//震动两秒
device.vibrate(2000);
如果设备处于震动状态,则取消震动。
Stability: 2 - Stable
dialogs 模块提供了简单的对话框支持,可以通过对话框和用户进行交互。最简单的例子如下:
alert("您好");
这段代码会弹出一个消息提示框显示"您好",并在用户点击"确定"后继续运行。稍微复杂一点的例子如下:
var clear = confirm("要清除所有缓存吗?");
if(clear){
alert("清除成功!");
}
confirm()会弹出一个对话框并让用户选择"是"或"否",如果选择"是"则返回true。
需要特别注意的是,对话框在ui模式下不能像通常那样使用,应该使用回调函数或者Promise的形式。理解这一点可能稍有困难。举个例子:
"ui";
//回调形式
confirm("要清除所有缓存吗?", function(clear){
if(clear){
alert("清除成功!");
}
});
//Promise形式
confirm("要清除所有缓存吗?")
.then(clear => {
if(clear){
alert("清除成功!");
}
});
显示一个只包含“确定”按钮的提示对话框。直至用户点击确定脚本才继续运行。
该函数也可以作为全局函数使用。
alert("出现错误~", "出现未知错误,请联系脚本作者”);
在ui模式下该函数返回一个Promise。例如:
"ui";
alert("嘿嘿嘿").then(()=>{
//当点击确定后会执行这里
});
显示一个包含“确定”和“取消”按钮的提示对话框。如果用户点击“确定”则返回 true ,否则返回 false 。
该函数也可以作为全局函数使用。
在ui模式下该函数返回一个Promise。例如:
"ui";
confirm("确定吗").then(value=>{
//当点击确定后会执行这里, value为true或false, 表示点击"确定"或"取消"
});
显示一个包含输入框的对话框,等待用户输入内容,并在用户点击确定时将输入的字符串返回。如果用户取消了输入,返回null。
该函数也可以作为全局函数使用。
var name = rawInput("请输入您的名字", "小明");
alert("您的名字是" + name);
在ui模式下该函数返回一个Promise。例如:
"ui";
rawInput("请输入您的名字", "小明").then(name => {
alert("您的名字是" + name);
});
当然也可以使用回调函数,例如:
rawInput("请输入您的名字", "小明", name => {
alert("您的名字是" + name);
});
等效于 eval(dialogs.rawInput(title, prefill, callback)), 该函数和rawInput的区别在于,会把输入的字符串用eval计算一遍再返回,返回的可能不是字符串。
可以用该函数输入数字、数组等。例如:
var age = dialogs.input("请输入您的年龄", "18");
// new Date().getYear() + 1900 可获取当前年份
var year = new Date().getYear() + 1900 - age;
alert("您的出生年份是" + year);
在ui模式下该函数返回一个Promise。例如:
"ui";
dialogs.input("请输入您的年龄", "18").then(age => {
var year = new Date().getYear() + 1900 - age;
alert("您的出生年份是" + year);
});
相当于 dialogs.rawInput();
显示一个带有选项列表的对话框,等待用户选择,返回用户选择的选项索引(0 ~ item.length - 1)。如果用户取消了选择,返回-1。
var options = ["选项A", "选项B", "选项C", "选项D"]
var i = dialogs.select("请选择一个选项", options);
if(i >= 0){
toast("您选择的是" + options[i]);
}else{
toast("您取消了选择");
}
在ui模式下该函数返回一个Promise。例如:
"ui";
dialogs.select("请选择一个选项", ["选项A", "选项B", "选项C", "选项D"])
.then(i => {
toast(i);
});
显示一个单选列表对话框,等待用户选择,返回用户选择的选项索引(0 ~ item.length - 1)。如果用户取消了选择,返回-1。
在ui模式下该函数返回一个Promise。
显示一个多选列表对话框,等待用户选择,返回用户选择的选项索引的数组。如果用户取消了选择,返回[]。
在ui模式下该函数返回一个Promise。
创建一个可自定义的对话框,例如:
dialogs.build({
//对话框标题
title: "发现新版本",
//对话框内容
content: "更新日志: 新增了若干了BUG",
//确定键内容
positive: "下载",
//取消键内容
negative: "取消",
//中性键内容
neutral: "到浏览器下载",
//勾选框内容
checkBoxPrompt: "不再提示"
}).on("positive", ()=>{
//监听确定键
toast("开始下载....");
}).on("neutral", ()=>{
//监听中性键
app.openUrl("https://www.autojs.org");
}).on("check", (checked)=>{
//监听勾选框
log(checked);
}).show();
选项properties可供配置的项目为:
通过这些选项可以自定义一个对话框,并通过监听返回的Dialog对象的按键、输入事件来实现交互。下面是一些例子。
模拟alert对话框:
dialogs.build({
title: "你好",
content: "今天也要元气满满哦",
positive: "好的"
}).show();
模拟confirm对话框:
dialogs.build({
title: "你好",
content: "请问你是笨蛋吗?",
positive: "是的",
negative: "我是大笨蛋"
}).on("positive", ()=>{
alert("哈哈哈笨蛋");
}).on("negative", ()=>{
alert("哈哈哈大笨蛋");
}).show();
模拟单选框:
dialogs.build({
title: "单选",
items: ["选项1", "选项2", "选项3", "选项4"],
itemsSelectMode: "single",
itemsSelectedIndex: 3
}).on("single_choice", (index, item)=>{
toast("您选择的是" + item);
}).show();
"处理中"对话框:
var d = dialogs.build({
title: "下载中...",
progress: {
max: -1
},
cancelable: false
}).show();
setTimeout(()=>{
d.dismiss();
}, 3000);
输入对话框:
dialogs.build({
title: "请输入您的年龄",
inputPrefill: "18"
}).on("input", (input)=>{
var age = parseInt(input);
toastLog(age);
}).show();
使用这个函数来构造对话框,一个明显的不同是需要使用回调函数而不能像dialogs其他函数一样同步地返回结果;但也可以通过threads模块的方法来实现。例如显示一个输入框并获取输入结果为:
var input = threads.disposable();
dialogas.build({
title: "请输入您的年龄",
inputPrefill: "18"
}).on("input", text => {
input.setAndNotify(text);
}).show();
var age = parseInt(input.blockedGet());
tosatLog(age);
dialogs.build()返回的对话框对象,内置一些事件用于响应用户的交互,也可以获取对话框的状态和信息。
对话框显示时会触发的事件。例如:
dialogs.build({
title: "标题"
}).on("show", (dialog)=>{
toast("对话框显示了");
}).show();
对话框被取消时会触发的事件。一个对话框可能按取消按钮、返回键取消或者点击对话框以外区域取消。例如:
dialogs.build({
title: "标题",
positive: "确定",
negative: "取消"
}).on("cancel", (dialog)=>{
toast("对话框取消了");
}).show();
对话框消失时会触发的事件。对话框被取消或者手动调用dialog.dismiss()函数都会触发该事件。例如:
var d = dialogs.build({
title: "标题",
positive: "确定",
negative: "取消"
}).on("dismiss", (dialog)=>{
toast("对话框消失了");
}).show();
setTimeout(()=>{
d.dismiss();
}, 5000);
确定按钮按下时触发的事件。例如:
var d = dialogs.build({
title: "标题",
positive: "确定",
negative: "取消"
}).on("positive", (dialog)=>{
toast("你点击了确定");
}).show();
取消按钮按下时触发的事件。例如:
var d = dialogs.build({
title: "标题",
positive: "确定",
negative: "取消"
}).on("negative", (dialog)=>{
toast("你点击了取消");
}).show();
中性按钮按下时触发的事件。例如:
var d = dialogs.build({
title: "标题",
positive: "确定",
negative: "取消",
neutral: "稍后提示"
}).on("positive", (dialog)=>{
toast("你点击了稍后提示");
}).show();
任意按钮按下时触发的事件。例如:
var d = dialogs.build({
title: "标题",
positive: "确定",
negative: "取消",
neutral: "稍后提示"
}).on("any", (action, dialog)=>{
if(action == "positive"){
toast("你点击了确定");
}else if(action == "negative"){
toast("你点击了取消");
}
}).show();
对话框列表(itemsSelectMode为"select")的项目被点击选中时触发的事件。例如:
var d = dialogs.build({
title: "请选择",
positive: "确定",
negative: "取消",
items: ["A", "B", "C", "D"],
itemsSelectMode: "select"
}).on("item_select", (index, item, dialog)=>{
toast("您选择的是第" + (index + 1) + "项, 选项为" + item);
}).show();
对话框单选列表(itemsSelectMode为"singleChoice")的项目被选中并点击确定时触发的事件。例如:
var d = dialogs.build({
title: "请选择",
positive: "确定",
negative: "取消",
items: ["A", "B", "C", "D"],
itemsSelectMode: "singleChoice"
}).on("item_select", (index, item, dialog)=>{
toast("您选择的是第" + (index + 1) + "项, 选项为" + item);
}).show();
对话框多选列表(itemsSelectMode为"multiChoice")的项目被选中并点击确定时触发的事件。例如:
var d = dialogs.build({
title: "请选择",
positive: "确定",
negative: "取消",
items: ["A", "B", "C", "D"],
itemsSelectMode: "multiChoice"
}).on("item_select", (indices, items, dialog)=>{
toast(util.format("您选择的项目为%o, 选项为%o", indices, items);
}).show();
带有输入框的对话框当点击确定时会触发的事件。例如:
dialogs.build({
title: "请输入",
positive: "确定",
negative: "取消",
inputPrefill: ""
}).on("input", (text, dialog)=>{
toast("你输入的是" + text);
}).show();
对话框的输入框的文本发生变化时会触发的事件。例如:
dialogs.build({
title: "请输入",
positive: "确定",
negative: "取消",
inputPrefill: ""
}).on("input_change", (text, dialog)=>{
toast("你输入的是" + text);
}).show();
获取当前进度条的进度值,是一个整数
获取当前进度条的最大进度值,是一个整数
Stability: 2 - Stable
engines模块包含了一些与脚本环境、脚本运行、脚本引擎有关的函数,包括运行其他脚本,关闭脚本等。
例如,获取脚本所在目录:
toast(engines.myEngine().cwd());
在新的脚本环境中运行脚本script。返回一个ScriptExectuion对象。
所谓新的脚本环境,指定是,脚本中的变量和原脚本的变量是不共享的,并且,脚本会在新的线程中运行。
最简单的例子如下:
engines.execScript("hello world", "toast('hello world')");
如果要循环运行,则:
//每隔3秒运行一次脚本,循环10次
engines.execScript("hello world", "toast('hello world')", {
loopTimes: 10,
interval: 3000
});
用字符串来编写脚本非常不方便,可以结合 Function.toString()的方法来执行特定函数:
function helloWorld(){
//注意,这里的变量和脚本主体的变量并不共享
toast("hello world");
}
engines.execScript("hello world", "helloWorld();\n" + helloWorld.toString());
如果要传递变量,则可以把这些封装成一个函数:
function exec(action, args){
args = args || {};
engines.execScript(action.name, action + "(" + JSON.stringify(args) + ");\n" + action.toString());
}
//要执行的函数,是一个简单的加法
function add(args){
toast(args.a + args.b);
}
//在新的脚本环境中执行 1 + 2
exec(add, {a: 1, b:2});
在新的脚本环境中运行脚本文件path。返回一个ScriptExecution对象。
engines.execScriptFile("/sdcard/脚本/1.js");
在新的脚本环境中运行录制文件path。返回一个ScriptExecution对象。
engines.execAutoFile("/sdcard/脚本/1.auto");
停止所有正在运行的脚本。包括当前脚本自身。
停止所有正在运行的脚本并显示停止的脚本数量。包括当前脚本自身。
返回当前脚本的脚本引擎对象(ScriptEngine)
[v4.1.0新增] 特别的,该对象可以通过execArgv来获取他的运行参数,包括外部参数、intent等。例如:
log(engines.myEngine().execArgv);
普通脚本的运行参数通常为空,通过定时任务的广播启动的则可以获取到启动的intent。
返回当前所有正在运行的脚本的脚本引擎ScriptEngine的数组。
log(engines.all());
执行脚本时返回的对象,可以通过他获取执行的引擎、配置等,也可以停止这个执行。
要停止这个脚本的执行,使用exectuion.getEngine().forceStop().
返回执行该脚本的脚本引擎对象(ScriptEngine)
返回该脚本的运行配置(ScriptConfig)
脚本引擎对象。
停止脚本引擎的执行。
返回脚本执行的路径。对于一个脚本文件而言为这个脚本所在的文件夹;对于其他脚本,例如字符串脚本,则为null或者执行时的设置值。
返回当前脚本引擎正在执行的脚本对象。
log(engines.myEngine().getSource());
向该脚本引擎发送一个事件,该事件可以在该脚本引擎对应的脚本的events模块监听到并在脚本主线程执行事件处理。
例如脚本receiver.js的内容如下:
//监听say事件
events.on("say", function(words){
toastLog(words);
});
//保持脚本运行
setInterval(()=>{}, 1000);
同一目录另一脚本可以启动他并发送该事件:
//运行脚本
var e = engines.execScriptFile("./receiver.js");
//等待脚本启动
sleep(2000);
//向该脚本发送事件
e.getEngine().emit("say", "你好");
脚本执行时的配置。
延迟执行的毫秒数
循环运行时两次运行之间的时间间隔
循环运行次数
返回一个字符串数组表示脚本运行时模块寻找的路径。
Stability: 2 - Stable
events模块提供了监听手机通知、按键、触摸的接口。您可以用他配合自动操作函数完成自动化工作。
events本身是一个EventEmiiter, 但内置了一些事件、包括按键事件、通知事件、Toast事件等。
需要注意的是,事件的处理是单线程的,并且仍然在原线程执行,如果脚本主体或者其他事件处理中有耗时操作、轮询等,则事件将无法得到及时处理(会进入事件队列等待脚本主体或其他事件处理完成才执行)。例如:
auto();
events.observeNotification();
events.on('toast', function(t){
//这段代码将得不到执行
log(t);
});
while(true){
//死循环
}
返回一个新的EventEmitter。这个EventEmitter没有内置任何事件。
启用按键监听,例如音量键、Home键。按键监听使用无障碍服务实现,如果无障碍服务未启用会抛出异常并提示开启。
只有这个函数成功执行后, onKeyDown, onKeyUp等按键事件的监听才有效。
该函数在安卓4.3以上才能使用。
注册一个按键监听函数,当有keyName对应的按键被按下会调用该函数。可用的按键名称参见Keys。
例如:
//启用按键监听
events.observeKey();
//监听音量上键按下
events.onKeyDown("volume_up", function(event){
toast("音量上键被按下了");
});
//监听菜单键按下
events.onKeyDown("menu", function(event){
toast("菜单键被按下了");
exit();
});
注册一个按键监听函数,当有keyName对应的按键弹起会调用该函数。可用的按键名称参见Keys。
一次完整的按键动作包括了按键按下和弹起。按下事件会在手指按下一个按键的"瞬间"触发, 弹起事件则在手指放开这个按键时触发。
例如:
//启用按键监听
events.observeKey();
//监听音量下键弹起
events.onKeyDown("volume_down", function(event){
toast("音量上键弹起");
});
//监听Home键弹起
events.onKeyDown("home", function(event){
toast("Home键弹起");
exit();
});
注册一个按键监听函数,当有keyName对应的按键被按下时会调用该函数,之后会注销该按键监听器。
也就是listener只有在onceKeyDown调用后的第一次按键事件被调用一次。
注册一个按键监听函数,当有keyName对应的按键弹起时会调用该函数,之后会注销该按键监听器。
也就是listener只有在onceKeyUp调用后的第一次按键事件被调用一次。
删除该按键的KeyDown(按下)事件的所有监听。
删除该按键的KeyUp(弹起)事件的所有监听。
设置按键屏蔽是否启用。所谓按键屏蔽指的是,屏蔽原有按键的功能,例如使得音量键不再能调节音量,但此时仍然能通过按键事件监听按键。
如果不加参数key则会屏蔽所有按键。
例如,调用events.setKeyInterceptionEnabled(true)会使系统的音量、Home、返回等键不再具有调节音量、回到主页、返回的作用,但此时仍然能通过按键事件监听按键。
该函数通常于按键监听结合,例如想监听音量键并使音量键按下时不弹出音量调节框则为:
events.setKeyInterceptionEnabled("volume_up", true);
events.observeKey();
events.onKeyDown("volume_up", ()=>{
log("音量上键被按下");
});
只要有一个脚本屏蔽了某个按键,该按键便会被屏蔽;当脚本退出时,会自动解除所有按键屏蔽。
启用屏幕触摸监听。(需要root权限)
只有这个函数被成功执行后, 触摸事件的监听才有效。
没有root权限调用该函数则什么也不会发生。
设置两个触摸事件分发的最小时间间隔。
例如间隔为10毫秒的话,前一个触摸事件发生并被注册的监听器处理后,至少要过10毫秒才能分发和处理下一个触摸事件,这10毫秒之间的触摸将会被忽略。
建议在满足需要的情况下尽量提高这个间隔。一个简单滑动动作可能会连续触发上百个触摸事件,如果timeout设置过低可能造成事件拥堵。强烈建议不要设置timeout为0。
返回触摸事件的最小时间间隔。
注册一个触摸监听函数。相当于on("touch", listener)。
例如:
//启用触摸监听
events.observeTouch();
//注册触摸监听器
events.onTouch(function(p){
//触摸事件发生时, 打印出触摸的点的坐标
log(p.x + ", " + p.y);
});
删除所有事件监听函数。
当有按键被按下或弹起时会触发该事件。 例如:
auto();
events.observeKey();
events.on("key", function(keyCode, event){
//处理按键事件
});
其中监听器的参数KeyCode包括:
例如:
auto();
events.observeKey();
events.on("key", function(keyCode, event){
if(keyCode == keys.menu && event.getAction() == event.ACTION_UP){
toast("菜单键按下");
}
});
当有按键被按下时会触发该事件。
auto();
events.observeKey();
events.on("key_down", function(keyCode, event){
//处理按键按下事件
});
当有按键弹起时会触发该事件。
auto();
events.observeKey();
events.on("key_up", function(keyCode, event){
//处理按键弹起事件
});
当脚本正常或者异常退出时会触发该事件。事件处理中如果有异常抛出,则立即中止exit事件的处理(即使exit事件有多个处理函数)并在控制台和日志中打印该异常。
一个脚本停止运行时,会关闭该脚本的所有悬浮窗,触发exit事件,之后再回收资源。如果exit事件的处理中有死循环,则后续资源无法得到及时回收。 此时脚本会停留在任务列表,如果在任务列表中关闭,则会强制结束exit事件的处理并回收后续资源。
log("开始运行")
events.on("exit", function(){
log("结束运行");
});
log("即将结束运行");
开启通知监听。例如QQ消息、微信消息、推送等通知。
通知监听依赖于通知服务,如果通知服务没有运行,会抛出异常并跳转到通知权限开启界面。(有时即使通知权限已经开启通知服务也没有运行,这时需要关闭权限再重新开启一次)
例如:
events.obverseNotification();
events.onNotification(function(notification){
log(notification.getText());
});
开启Toast监听。
Toast监听依赖于无障碍服务,因此此函数会确保无障碍服务运行。
当有应用发出toast(气泡消息)时会触发该事件。但Auto.js软件本身的toast除外。
例如,要记录发出所有toast的应用:
events.observeToast();
events.onToast(function(toast){
log("Toast内容: " + toast.getText() + " 包名: " + toast.getPackageName());
});
当有应用发出通知时会触发该事件,参数为Notification。
例如:
events.observeNotification();
events.on("notification", function(n){
log("收到新通知:\n 标题: %s, 内容: %s, \n包名: %s", n.getTitle(), n.getText(), n.getPackageName());
});
通知对象,可以获取通知详情,包括通知标题、内容、发出通知的包名、时间等,也可以对通知进行操作,比如点击、删除。
通知数量。例如QQ连续收到两条消息时number为2。
通知发出时间的时间戳,可以用于构造Date对象。例如:
events.observeNotification();
events.on("notification", function(n){
log("通知时间为}" + new Date(n.when));
});
获取发出通知的应用包名。
获取通知的标题。
获取通知的内容。
点击该通知。例如对于一条QQ消息,点击会进入具体的聊天界面。
删除该通知。该通知将从通知栏中消失。
Stability: 2 - Stable
返回事件的动作。包括:
返回按键的键值。包括:
返回事件发生的时间戳。
返回最近一次按下事件的时间戳。如果本身是按下事件,则与getEventTime()相同。
把键值转换为字符串。例如KEYCODE_HOME转换为"KEYCODE_HOME"。
Stability: 2 - Stable
按键事件中所有可用的按键名称为:
Stability: 2 - Stable
每个事件默认可以注册最多 10 个监听器。 单个 EventEmitter 实例的限制可以使用 emitter.setMaxListeners(n) 方法改变。 所有 EventEmitter 实例的默认值可以使用 EventEmitter.defaultMaxListeners 属性改变。
设置 EventEmitter.defaultMaxListeners 要谨慎,因为会影响所有 EventEmitter 实例,包括之前创建的。 因而,调用 emitter.setMaxListeners(n) 优先于 EventEmitter.defaultMaxListeners。
注意,与Node.js不同,这是一个硬性限制。 EventEmitter 实例不允许添加更多的监听器,监听器超过最大数量时会抛出TooManyListenersException。
emitter.setMaxListeners(emitter.getMaxListeners() + 1);
emitter.once('event', () => {
// 做些操作
emitter.setMaxListeners(Math.max(emitter.getMaxListeners() - 1, 0));
});
emitter.on(eventName, listener) 的别名。
按监听器的注册顺序,同步地调用每个注册到名为 eventName 事件的监听器,并传入提供的参数。
如果事件有监听器,则返回 true ,否则返回 false。
返回一个列出触发器已注册监听器的事件的数组。 数组中的值为字符串或符号。
const myEE = events.emitter();
myEE.on('foo', () => {});
myEE.on('bar', () => {});
const sym = Symbol('symbol');
myEE.on(sym, () => {});
console.log(myEE.eventNames());
// 打印: [ 'foo', 'bar', Symbol(symbol) ]
返回 EventEmitter 当前的最大监听器限制值,该值可以通过 emitter.setMaxListeners(n) 设置或默认为 EventEmitter.defaultMaxListeners。
返回正在监听名为 eventName 的事件的监听器的数量。
返回名为 eventName 的事件的监听器数组的副本。
server.on('connection', (stream) => {
console.log('someone connected!');
});
console.log(util.inspect(server.listeners('connection')));
// 打印: [ [Function] ]
添加 listener 函数到名为 eventName 的事件的监听器数组的末尾。 不会检查 listener 是否已被添加。 多次调用并传入相同的 eventName 和 listener 会导致 listener 被添加与调用多次。
server.on('connection', (stream) => {
console.log('有连接!');
});
返回一个 EventEmitter 引用,可以链式调用。
默认情况下,事件监听器会按照添加的顺序依次调用。 emitter.prependListener() 方法可用于将事件监听器添加到监听器数组的开头。
const myEE = events.emitter();
myEE.on('foo', () => console.log('a'));
myEE.prependListener('foo', () => console.log('b'));
myEE.emit('foo');
// 打印:
// b
// a
添加一个单次 listener 函数到名为 eventName 的事件。 下次触发 eventName 事件时,监听器会被移除,然后调用。
server.once('connection', (stream) => {
console.log('首次调用!');
});
返回一个 EventEmitter 引用,可以链式调用。
默认情况下,事件监听器会按照添加的顺序依次调用。 emitter.prependOnceListener() 方法可用于将事件监听器添加到监听器数组的开头。
const myEE = events.emitter();
myEE.once('foo', () => console.log('a'));
myEE.prependOnceListener('foo', () => console.log('b'));
myEE.emit('foo');
// 打印:
// b
// a
添加 listener 函数到名为 eventName 的事件的监听器数组的开头。 不会检查 listener 是否已被添加。 多次调用并传入相同的 eventName 和 listener 会导致 listener 被添加与调用多次。
server.prependListener('connection', (stream) => {
console.log('有连接!');
});
返回一个 EventEmitter 引用,可以链式调用。
添加一个单次 listener 函数到名为 eventName 的事件的监听器数组的开头。 下次触发 eventName 事件时,监听器会被移除,然后调用。
server.prependOnceListener('connection', (stream) => {
console.log('首次调用!');
});
返回一个 EventEmitter 引用,可以链式调用。
移除全部或指定 eventName 的监听器。
注意,在代码中移除其他地方添加的监听器是一个不好的做法,尤其是当 EventEmitter 实例是其他组件或模块创建的。
返回一个 EventEmitter 引用,可以链式调用。
从名为 eventName 的事件的监听器数组中移除指定的 listener。
const callback = (stream) => {
console.log('有连接!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);
removeListener 最多只会从监听器数组里移除一个监听器实例。 如果任何单一的监听器被多次添加到指定 eventName 的监听器数组中,则必须多次调用 removeListener 才能移除每个实例。
注意,一旦一个事件被触发,所有绑定到它的监听器都会按顺序依次触发。 这意味着,在事件触发后、最后一个监听器完成执行前,任何 removeListener() 或 removeAllListeners() 调用都不会从 emit() 中移除它们。 随后的事件会像预期的那样发生。
const myEmitter = events.emitter();
const callbackA = () => {
console.log('A');
myEmitter.removeListener('event', callbackB);
};
const callbackB = () => {
console.log('B');
};
myEmitter.on('event', callbackA);
myEmitter.on('event', callbackB);
// callbackA 移除了监听器 callbackB,但它依然会被调用。
// 触发是内部的监听器数组为 [callbackA, callbackB]
myEmitter.emit('event');
// 打印:
// A
// B
// callbackB 被移除了。
// 内部监听器数组为 [callbackA]
myEmitter.emit('event');
// 打印:
// A
因为监听器是使用内部数组进行管理的,所以调用它会改变在监听器被移除后注册的任何监听器的位置索引。 虽然这不会影响监听器的调用顺序,但意味着由 emitter.listeners() 方法返回的监听器数组副本需要被重新创建。
返回一个 EventEmitter 引用,可以链式调用。
默认情况下,如果为特定事件添加了超过 10 个监听器,则 EventEmitter 会打印一个警告。 此限制有助于寻找内存泄露。 但是,并不是所有的事件都要被限为 10 个。 emitter.setMaxListeners() 方法允许修改指定的 EventEmitter 实例的限制。 值设为 Infinity(或 0)表明不限制监听器的数量。
返回一个 EventEmitter 引用,可以链式调用。
脚本间通信除了使用engines模块提供的ScriptEngine.emit()方法以外,也可以使用events模块提供的broadcast广播。
events.broadcast本身是一个EventEmitter,但它的事件是在脚本间共享的,所有脚本都能发送和监听这些事件;事件处理会在脚本主线程执行(后续可能加入函数onThisThread(eventName, ...args)来提供在其他线程执行的能力)。
例如在一个脚本发送一个广播hello:
events.broadcast.emit("hello", "小明");
在其他脚本中监听并处理:
events.broadcast.on("hello", function(name){
toast("你好, " + name);
});
//保持脚本运行
setInterval(()=>{}, 1000);
floaty模块提供了悬浮窗的相关函数,可以在屏幕上显示自定义悬浮窗,控制悬浮窗大小、位置等。
悬浮窗在脚本停止运行时会自动关闭,因此,要保持悬浮窗不被关闭,可以用一个空的setInterval来实现,例如:
setInterval(()=>{}, 1000);
指定悬浮窗的布局,创建并显示一个悬浮窗,返回一个FloatyWindow对象。
该悬浮窗自带关闭、调整大小、调整位置按键,可根据需要调用setAdjustEnabled()函数来显示或隐藏。
其中layout参数可以是xml布局或者一个View,更多信息参见ui模块的说明。
例子:
var w = floaty.window(
<frame gravity="center">
<text id="text">悬浮文字text>
frame>
);
setTimeout(()=>{
w.close();
}, 2000);
这段代码运行后将会在屏幕上显示悬浮文字,并在两秒后消失。
另外,因为脚本运行的线程不是UI线程,而所有对控件的修改操作需要在UI线程执行,此时需要用ui.run,例如:
ui.run(function(){
w.text.setText("文本");
});
有关返回的FloatyWindow对象的说明,参见下面的FloatyWindow章节。
指定悬浮窗的布局,创建并显示一个原始悬浮窗,返回一个FloatyRawWindow对象。
与floaty.window()函数不同的是,该悬浮窗不会增加任何额外设施(例如调整大小、位置按钮),您可以根据自己需要编写任何布局。
而且,该悬浮窗支持完全全屏,可以覆盖状态栏,因此可以做护眼模式之类的应用。
var w = floaty.rawWindow(
<frame gravity="center">
<text id="text">悬浮文字text>
frame>
);
w.setPosition(500, 500);
setTimeout(()=>{
w.close();
}, 2000);
这段代码运行后将会在屏幕上显示悬浮文字,并在两秒后消失。
有关返回的FloatyRawWindow对象的说明,参见下面的FloatyRawWindow章节。
关闭所有本脚本的悬浮窗。
悬浮窗对象,可通过FloatyWindow.{id}获取悬浮窗界面上的元素。例如, 悬浮窗window上一个控件的id为aaa, 那么window.aaa即可获取到该控件,类似于ui。
如果enabled为true,则在悬浮窗左上角、右上角显示可供位置、大小调整的标示,就像控制台一样; 如果enabled为false,则隐藏上述标示。
设置悬浮窗位置。
返回悬浮窗位置的X坐标。
返回悬浮窗位置的Y坐标。
设置悬浮窗宽高。
返回悬浮窗宽度。
返回悬浮窗高度。
关闭悬浮窗。如果悬浮窗已经是关闭状态,则此函数将不执行任何操作。
被关闭后的悬浮窗不能再显示。
使悬浮窗被关闭时自动结束脚本运行。
原始悬浮窗对象,可通过window.{id}获取悬浮窗界面上的元素。例如, 悬浮窗window上一个控件的id为aaa, 那么window.aaa即可获取到该控件,类似于ui。
设置悬浮窗是否可触摸,如果为true, 则悬浮窗将接收到触摸、点击等事件并且无法继续传递到悬浮窗下面;如果为false, 悬浮窗上的触摸、点击等事件将被直接传递到悬浮窗下面。处于安全考虑,被悬浮窗接收的触摸事情无法再继续传递到下层。
可以用此特性来制作护眼模式脚本。
var w = floaty.rawWindow(
<frame gravity="center" bg="#44ffcc00"/>
);
w.setSize(-1, -1);
w.setTouchable(false);
setTimeout(()=>{
w.close();
}, 4000);
设置悬浮窗位置。
返回悬浮窗位置的X坐标。
返回悬浮窗位置的Y坐标。
设置悬浮窗宽高。
特别地,如果设置为-1,则为占满全屏;设置为-2则为根据悬浮窗内容大小而定。例如:
var w = floaty.rawWindow(
<frame gravity="center" bg="#77ff0000">
<text id="text">悬浮文字text>
frame>
);
w.setSize(-1, -1);
setTimeout(()=>{
w.close();
}, 2000);
返回悬浮窗宽度。
返回悬浮窗高度。
关闭悬浮窗。如果悬浮窗已经是关闭状态,则此函数将不执行任何操作。
被关闭后的悬浮窗不能再显示。
使悬浮窗被关闭时自动结束脚本运行。
Stability: 2 - Stable
files模块提供了一些常见的文件处理,包括文件读写、移动、复制、删掉等。
一次性的文件读写可以直接使用files.read(), files.write(), files.append()等方便的函数,但如果需要频繁读写或随机读写,则使用open()函数打开一个文件对象来操作文件,并在操作完毕后调用close()函数关闭文件。
返回路径path是否是文件。
log(files.isDir("/sdcard/文件夹/")); //返回false
log(files.isDir("/sdcard/文件.txt")); //返回true
返回路径path是否是文件夹。
log(files.isDir("/sdcard/文件夹/")); //返回true
log(files.isDir("/sdcard/文件.txt")); //返回false
返回文件夹path是否为空文件夹。如果该路径并非文件夹,则直接返回false。
连接两个路径并返回,例如files.join("/sdcard/", "1.txt")返回"/sdcard/1.txt"。
创建一个文件或文件夹并返回是否创建成功。如果文件已经存在,则直接返回false。
files.create("/sdcard/新文件夹/");
创建一个文件或文件夹并返回是否创建成功。如果文件所在文件夹不存在,则先创建他所在的一系列文件夹。如果文件已经存在,则直接返回false。
files.createWithDirs("/sdcard/新文件夹/新文件夹/新文件夹/1.txt");
返回在路径path处的文件是否存在。
确保路径path所在的文件夹存在。如果该路径所在文件夹不存在,则创建该文件夹。
例如对于路径"/sdcard/Download/ABC/1.txt",如果/Download/文件夹不存在,则会先创建Download,再创建ABC文件夹。
读取文本文件path的所有内容并返回。如果文件不存在,则抛出FileNotFoundException。
log(files.read("/sdcard/1.txt"));
读取文件path的所有内容并返回一个字节数组。如果文件不存在,则抛出FileNotFoundException。
注意,该数组是Java的数组,不具有JavaScript数组的forEach, slice等函数。
一个以16进制形式打印文件的例子如下:
var data = files.readBytes("/sdcard/1.png");
var sb = new java.lang.StringBuilder();
for(var i = 0; i < data.length; i++){
sb.append(data[i].toString(16));
}
log(sb.toString());
把text写入到文件path中。如果文件存在则覆盖,不存在则创建。
var text = "文件内容";
//写入文件
files.write("/sdcard/1.txt", text);
//用其他应用查看文件
app.viewFile("/sdcard/1.txt");
把bytes写入到文件path中。如果文件存在则覆盖,不存在则创建。
把text追加到文件path的末尾。如果文件不存在则创建。
var text = "追加的文件内容";
files.append("/sdcard/1.txt", text);
files.append("/sdcard/1.txt", text);
//用其他应用查看文件
app.viewFile("/sdcard/1.txt");
把bytes追加到文件path的末尾。如果文件不存在则创建。
复制文件,返回是否复制成功。例如files.copy("/sdcard/1.txt", "/sdcard/Download/1.txt")。
移动文件,返回是否移动成功。例如files.move("/sdcard/1.txt", "/sdcard/Download/1.txt")会把1.txt文件从sd卡根目录移动到Download文件夹。
重命名文件,并返回是否重命名成功。例如files.rename("/sdcard/1.txt", "2.txt")。
重命名文件,不包含拓展名,并返回是否重命名成功。例如files.rename("/sdcard/1.txt", "2")会把"1.txt"重命名为"2.txt"。
返回文件的文件名。例如files.getName("/sdcard/1.txt")返回"1.txt"。
返回不含拓展名的文件的文件名。例如files.getName("/sdcard/1.txt")返回"1"。
返回文件的拓展名。例如files.getExtension("/sdcard/1.txt")返回"txt"。
删除文件或空文件夹,返回是否删除成功。
删除文件夹,如果文件夹不为空,则删除该文件夹的所有内容再删除该文件夹,返回是否全部删除成功。
返回SD卡路径。所谓SD卡,即外部存储器。
返回脚本的"当前工作文件夹路径"。该路径指的是,如果脚本本身为脚本文件,则返回这个脚本文件所在目录;否则返回null获取其他设定路径。
例如,对于脚本文件"/sdcard/脚本/1.js"运行files.cwd()返回"/sdcard/脚本/"。
返回相对路径对应的绝对路径。例如files.path("./1.png"),如果运行这个语句的脚本位于文件夹"/sdcard/脚本/"中,则返回"/sdcard/脚本/1.png"。
列出文件夹path下的满足条件的文件和文件夹的名称的数组。如果不加filter参数,则返回所有文件和文件夹。
列出sdcard目录下所有文件和文件夹为:
var arr = files.listDir("/sdcard/");
log(arr);
列出脚本目录下所有js脚本文件为:
var dir = "/sdcard/脚本/";
var jsFiles = files.listDir(dir, function(name){
return name.endsWith(".js") && files.isFile(files.join(dir, name));
});
log(jsFiles);
打开一个文件。根据打开模式返回不同的文件对象。包括:
对于"w"模式,如果文件并不存在,则会创建一个,已存在则会清空该文件内容;其他模式文件不存在会抛出FileNotFoundException。
可读文件对象。
返回该文件剩余的所有内容的字符串。
读取该文件接下来最长为maxCount的字符串并返回。即使文件剩余内容不足maxCount也不会出错。
读取一行并返回(不包含换行符)。
读取剩余的所有行,并返回它们按顺序组成的字符串数组。
关闭该文件。
打开一个文件不再使用时务必关闭
可写文件对象。
把文本内容text写入到文件中。
把文本line写入到文件中并写入一个换行符。
把很多行写入到文件中....
把缓冲区内容输出到文件中。
关闭文件。同时会被缓冲区内容输出到文件。
打开一个文件写入后,不再使用时务必关闭,否则文件可能会丢失
Stability: 2 - Stable
http模块提供一些进行http请求的函数。
对地址url进行一次HTTP GET 请求。如果没有回调函数,则在请求完成或失败时返回此次请求的响应(参见[Response][])。
最简单GET请求如下:
console.show();
var r = http.get("www.baidu.com");
log("code = " + r.statusCode);
log("html = " + r.body.string());
采用回调形式的GET请求如下:
console.show();
http.get("www.baidu.com", {}, function(res, err){
if(err){
console.error(err);
return;
}
log("code = " + res.statusCode);
log("html = " + res.body.string());
});
如果要增加HTTP头部信息,则在options参数中添加,例如:
console.show();
var r = http.get("www.baidu.com", {
headers: {
'Accept-Language': 'zh-cn,zh;q=0.5',
'User-Agent': 'Mozilla/5.0(Macintosh;IntelMacOSX10_7_0)AppleWebKit/535.11(KHTML,likeGecko)Chrome/17.0.963.56Safari/535.11'
}
});
log("code = " + r.statusCode);
log("html = " + r.body.string());
一个请求天气并解析返回的天气JSON结果的例子如下:
var city = "广州";
var res = http.get("http://www.sojson.com/open/api/weather/json.shtml?city=" + city);
if(res.statusCode != 200){
toast("请求失败: " + res.statusCode + " " + res.statusMessage);
}else{
var weather = res.body.json();
log(weather);
toast(util.format("温度: %s 湿度: %s 空气质量: %s", weather.data.wendu,
weather.data.shidu, weather.quality));
}
对地址url进行一次HTTP POST 请求。如果没有回调函数,则在请求完成或失败时返回此次请求的响应(参见[Response][])。
其中POST数据可以是字符串或键值对。具体含义取决于options.contentType的值。默认为"application/x-www-form-urlencoded"(表单提交), 这种方式是JQuery的ajax函数的默认方式。
一个模拟表单提交登录淘宝的例子如下:
var url = "https://login.taobao.com/member/login.jhtml";
var username = "你的用户名";
var password = "你的密码";
var res = http.post(url, {
"TPL_username": username,
"TPL_password": password
});
var html = res.body.string();
if(html.contains("页面跳转中")){
toast("登录成功");
}else{
toast("登录失败");
}
以JSON格式向目标Url发起POST请求。如果没有回调函数,则在请求完成或失败时返回此次请求的响应(参见[Response][])。
JSON格式指的是,将会调用JSON.stringify()把data对象转换为JSON字符串,并在HTTP头部信息中把"Content-Type"属性置为"application/json"。这种方式是AngularJS的ajax函数的默认方式。
一个调用图灵机器人接口的例子如下:
var url = "http://www.tuling123.com/openapi/api";
r = http.postJson(url, {
key: "65458a5df537443b89b31f1c03202a80",
info: "你好啊",
userid: "1",
});
toastLog(r.body.string());
向目标地址发起类型为multipart/form-data的请求(通常用于文件上传等), 其中files参数是{name1: value1, name2: value2, ...}的键值对,value的格式可以是以下几种情况:
其中1属于非文件参数,2、3、4为文件参数。举个例子,最简单的文件上传的请求为:
var res = http.postMultipart(url, {
file: open("/sdcard/1.txt")
});
log(res.body.string());
如果使用格式2,则代码为
var res = http.postMultipart(url, {
file: ["1.txt", "/sdcard/1.txt"]
});
log(res.body.string());
如果使用格式3,则代码为
var res = http.postMultipart(url, {
file: ["1.txt", "text/plain", "/sdcard/1.txt"]
});
log(res.body.string());
如果使用格式2的同时要附带非文件参数"appId=abcdefghijk",则为:
var res = http.postMultipart(url, {
appId: "adcdefghijk",
file: open("/sdcard/1.txt")
});
log(res.body.string());
对目标地址url发起一次HTTP请求。如果没有回调函数,则在请求完成或失败时返回此次请求的响应(参见[Response][])。
选项options可以包含以下属性:
该函数是get, post, postJson等函数的基础函数。因此除非是PUT, DELET等请求,或者需要更高定制的HTTP请求,否则直接使用get, post, postJson等函数会更加方便。
HTTP请求的响应。
当前响应的HTTP状态码。例如200(OK), 404(Not Found)等。
有关HTTP状态码的信息,参见菜鸟教程:HTTP状态码。
当前响应的HTTP状态信息。例如"OK", "Bad Request", "Forbidden"。
有关HTTP状态码的信息,参见菜鸟教程:HTTP状态码。
例子:
var res = http.get("www.baidu.com");
if(res.statusCode >= 200 && res.statusCode < 300){
toast("页面获取成功!");
}else if(res.statusCode == 404){
toast("页面没找到哦...");
}else{
toast("错误: " + res.statusCode + " " + res.statusMessage);
}
当前响应的HTTP头部信息。该对象的键是响应头名称,值是各自的响应头值。 所有响应头名称都是小写的(吗)。
有关HTTP头部信息,参见菜鸟教程:HTTP响应头信息。
例子:
console.show();
var res = http.get("www.qq.com");
console.log("HTTP Headers:")
for(var headerName in res.headers){
console.log("%s: %s", headerName, res.headers[headerName]);
}
当前响应的内容。他有以下属性和函数:
Stability: 2 - Stable
在Auto.js有两种方式表示一个颜色。
一种是使用一个字符串"#AARRGGBB"或"#RRGGBB",其中 AA 是Alpha通道(透明度)的值,RR 是R通道(红色)的值,GG 是G通道(绿色)的值,BB是B通道(蓝色)的值。例如"#ffffff"表示白色, "#7F000000"表示半透明的黑色。
另一种是使用一个16进制的"32位整数" 0xAARRGGBB 来表示一个颜色,例如 0xFF112233表示颜色"#112233", 0x11223344表示颜色"#11223344"。
可以通过colors.toString()把颜色整数转换为字符串,通过colors.parseColor()把颜色字符串解析为颜色整数。
返回颜色值的字符串,格式为 "#AARRGGBB"。
返回颜色color的R通道的值,范围0~255.
返回颜色color的G通道的值,范围0~255.
返回颜色color的B通道的值,范围0~255.
返回颜色color的Alpha通道的值,范围0~255.
返回这些颜色通道构成的整数颜色值。Alpha通道将是255(不透明)。
返回这些颜色通道构成的整数颜色值。
返回颜色的整数值。
返回两个颜色是否相似。
返回两个颜色是否相等。*注意该函数会忽略Alpha通道的值进行比较。
log(colors.equals("#112233", "#112234"));
log(colors.equals(0xFF112233, 0xFF223344));
黑色,颜色值 #FF000000
深灰色,颜色值 #FF444444
灰色,颜色值 #FF888888
亮灰色,颜色值 #FFCCCCCC
白色,颜色值 #FFFFFFFF
红色,颜色值 #FFFF0000
绿色,颜色值 #FF00FF00
蓝色,颜色值 #FF0000FF
黄色,颜色值 #FFFFFF00
青色,颜色值 #FF00FFFF
品红色,颜色值 #FFFF00FF
透明,颜色值 #00000000