diff --git a/api/_404.md b/api/_404.md new file mode 100644 index 0000000..6bc0a8a --- /dev/null +++ b/api/_404.md @@ -0,0 +1 @@ +# 页面找不到了o(╥﹏╥)o \ No newline at end of file diff --git a/api/_coverpage.md b/api/_coverpage.md new file mode 100644 index 0000000..101d9b5 --- /dev/null +++ b/api/_coverpage.md @@ -0,0 +1,9 @@ +![logo](images/logo.png) + +# Auto.js + +> **不需要Root权限**的JavaScript自动化软件 + +[GitHub](https://github.com/hyb1996/Auto.js) +[软件下载](https://www.coolapk.com/apk/129872) +[阅读文档](#综述) \ No newline at end of file diff --git a/api/_sidebar.md b/api/_sidebar.md new file mode 100644 index 0000000..4972250 --- /dev/null +++ b/api/_sidebar.md @@ -0,0 +1,28 @@ +* [首页](/) +* [Q&A](/qa) +* [如何阅读本文档 - Documentation](/documentation) +* [应用 - App](/app) +* [一般全局函数 - Globals](/globals) +* [控制台 - Console](/console) +* [基于坐标的操作 - CoordinatesBasedAutomation](/coordinatesBasedAutomation) +* [加解密与消息摘要 - Crypto](/crypto) +* [设备 - Device](/device) +* [对话框 - Dialogs](/dialogs) +* [脚本引擎 - Engines](/engines) +* [事件与监听 - Events](/events) +* [悬浮窗 - Floaty](/floaty) +* [文件系统 - Files](/files) +* [HTTP - Http](/http) +* [图片与颜色 - Images](/images) +* [画布 - Canvas](/canvas) +* [按键模拟 - Keys](/keys) +* [多媒体 - Media](/media) +* [模块 - Modules](/modules) +* [基于控件的操作 - WidgetsBaseAutomation](/widgetsBasedAutomation) +* [传感器 - Sensor](/sensors) +* [Shell - Shell](/shell) +* [本地储存 - Storages](/storages) +* [多线程 - Threads](/threads) +* [定时器 - Timers](/timers) +* [用户界面 - UI](/ui) +* [调用Java - Work with Java](https://developer.mozilla.org/zh-CN/docs/Mozilla/Projects/Rhino/Scripting_Java) diff --git a/api/_toc.md b/api/_toc.md index de73482..71a2064 100644 --- a/api/_toc.md +++ b/api/_toc.md @@ -9,8 +9,9 @@ * [Overview - 综述](overview.html) * [Q&A - 常见问题](qa.html) * [App - 应用](app.html) +* [Canvas - 画布](canvas.html) * [Console - 控制台](console.html) -* [CoordinatesBasedAutomation - 基于坐标的操作](coordinates-based-automation.html) +* [CoordinatesBasedAutomation - 基于坐标的操作](coordinatesBasedAutomation.html) * [Crypto - 加解密与消息摘要](crypto.html) * [Device - 设备](device.html) * [Dialogs - 对话框](dialogs.html) @@ -24,13 +25,13 @@ * [Keys - 按键模拟](keys.html) * [Media - 多媒体](media.html) * [Modules - 模块](modules.html) -* [WidgetsBasedAutomation - 基于控件的操作](widgets-based-automation.html) * [Sensors - 传感器](sensors.html) * [Shell - Shell命令](shell.html) * [Storages - 本地存储](storages.html) * [Threads - 多线程](threads.html) * [Timers - 定时器](timers.html) * [UI - 用户界面](ui.html) +* [WidgetsBasedAutomation - 基于控件的操作](widgetsBasedAutomation.html) * [Work with Java - 调用Java API](https://developer.mozilla.org/zh-CN/docs/Mozilla/Projects/Rhino/Scripting_Java)
diff --git a/api/all.md b/api/all.md index 64d1e84..7d25d7a 100644 --- a/api/all.md +++ b/api/all.md @@ -1,8 +1,9 @@ @include overview @include qa @include app +@include canvas @include console -@include coordinates-based-automation +@include coordinatesBasedAutomation @include crypto @include device @include dialogs @@ -16,10 +17,10 @@ @include keys @include media @include modules -@include widgets-based-automation @include sensors @include shell @include storages @include threads @include timers -@include ui \ No newline at end of file +@include ui +@include widgetsBasedAutomation \ No newline at end of file diff --git a/api/canvas.md b/api/canvas.md index 256ccb9..69a739e 100644 --- a/api/canvas.md +++ b/api/canvas.md @@ -2,7 +2,7 @@ canvas提供了使用画布进行2D画图的支持,可用于简单的小游戏开发或者图片编辑。使用canvas可以轻松地在一张图片或一个界面上绘制各种线与图形。 -canvas的坐标系为平面直角坐标系,以屏幕左上角为原点,屏幕上边为x轴正方向,屏幕左边为y轴正方向。例如分辨率为1920*1080的屏幕上,画一条从屏幕左上角到屏幕右下角的线段为: +canvas的坐标系为平面直角坐标系,以控件左上角为原点,控件上边沿为x轴正方向,控件左边沿为y轴正方向。例如分辨率为1920*1080的屏幕上,canvas控件覆盖全屏,画一条从屏幕左上角到屏幕右下角的线段为: ``` canvas.drawLine(0, 0, 1080, 1920, paint); @@ -30,9 +30,140 @@ paint.setColor(colors.RED); canvas.drawRect(0, 0, 100, 100, paint); ``` -结合画笔canvas可以绘制基本图形、图片等。 +结合画笔,canvas可以绘制基本图形、图片等。 + +## canvas.getWidth() +* 返回 {number} + +返回画布当前图层的宽度。 + +## canvas.getHeight() +* 返回 {number} + +返回画布当前图层的高度。 + +## canvas.drawRGB(r, int g, int b) +* `r` {number} 红色通道值 +* `g` {number} 绿色通道值 +* `b` {number} 蓝色通道值 + +将整个可绘制区域填充为r、g、b指定的颜色。相当于 `canvas.drawColor(colors.rgb(r, g, b))`。 ## canvas.drawARGB(a, r, g, b) +* `a` {number} 透明通道值 +* `r` {number} 红色通道值 +* `g` {number} 绿色通道值 +* `b` {number} 蓝色通道值 + +将整个可绘制区域填充为a、r、g、b指定的颜色。相当于 `canvas.drawColor(colors.argb(a, r, g, b))`。 + +## canvas.drawColor(color) +* `color` {number} 颜色值 + +将整个可绘制区域填充为color指定的颜色。 + +## canvas.drawColor(color, mode) +* `color` {number} 颜色值 +* `mode` {PorterDuff.Mode} Porter-Duff操作 + +将整个可绘制区域填充为color指定的颜色。 + +## canvas.drawPaint(paint) +* `paint` {Paint} 画笔 + +将整个可绘制区域用paint指定的画笔填充。相当于绘制一个无限大的矩形,但是更快。 +通过该方法可以绘制一个指定的着色器的图案。 + +## canvas.drawPoint(x, y, paint) +* `x` {number} x坐标 +* `y` {number} y坐标 +* `paint` {Paint} 画笔 + +在可绘制区域绘制由坐标(x, y)指定的点。 +点的形状由画笔的线帽决定(参见paint.setStrokeCap(cap))。 +点的大小由画笔的宽度决定(参见paint.setStrokeWidth(width))。 +> 如果画笔宽度为0,则也会绘制1个像素至4个(若抗锯齿启用)。 + +相当于 `canvas.drawPoints([x, y], paint)`。 + +## canvas.drawPoints(pts, paint) +* `pts` {Array} 点坐标数组 [x0, y0, x1, y1, x2, y2, ...] +* `paint` {Paint} 画笔 + +在可绘制区域绘制由坐标数组指定的多个点。 + +## canvas.drawLine(startX, startY, stopX, stopY, paint) +* `startX` {number} 起点x坐标 +* `startY` {number} 起点y坐标 +* `endX` {number} 终点x坐标 +* `endY` {number} 终点y坐标 +* `paint` {Paint} 画笔 + +在可绘制区域绘制由起点坐标(startX, startY)和终点坐标(endX, endY)指定的线。 +绘制时会忽略画笔的样式(Style)。也就是说,即使样式设为“仅填充(FILL)”也会绘制。 +退化为点的线(长度为0)不会被绘制。 + +## canvas.drawRect(r, paint) +* `r` {Rect} 矩形边界 +* `paint` {Paint} 画笔 + +在可绘制区域绘制由矩形边界r指定的矩形。 +绘制时画笔的样式(Style)决定了是否绘制矩形界线和填充矩形。 + +## canvas.drawRect(left, top, right, bottom, android.graphics.Paint paint) +* `left` {number} 矩形左边界x坐标 +* `top` {number} 矩形上边界y坐标 +* `right` {number} 矩形右边界x坐标 +* `bottom` {number} 矩形下边界y坐标 +* `paint` {Paint} 画笔 + +在可绘制区域绘制由矩形边界(left, top, right, bottom)指定的矩形。 +绘制时画笔的样式(Style)决定了是否绘制矩形界线和填充矩形。 + +## canvas.drawOval(android.graphics.RectF oval, android.graphics.Paint paint) +## canvas.drawOval(float left, float top, float right, float bottom, android.graphics.Paint paint) +## canvas.drawCircle(float cx, float cy, float radius, android.graphics.Paint paint) +## canvas.drawArc(android.graphics.RectF oval, float startAngle, float sweepAngle, boolean useCenter, android.graphics.Paint paint) +## canvas.drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, android.graphics.Paint paint) +## canvas.drawRoundRect(android.graphics.RectF rect, float rx, float ry, android.graphics.Paint paint) +## canvas.drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, android.graphics.Paint paint) +## canvas.drawPath(android.graphics.Path path, android.graphics.Paint paint) +## canvas.drawBitmap(android.graphics.Bitmap bitmap, float left, float top, android.graphics.Paint paint) +## canvas.drawText(java.lang.String text, float x, float y, android.graphics.Paint paint) +## canvas.drawTextOnPath(java.lang.String text, android.graphics.Path path, float hOffset, float vOffset, android.graphics.Paint paint) + +## canvas.translate(dx, dy) +* `dx` {number} 向x轴正方向平移的距离,负数表示反方向平移 +* `dy` {number} 向y轴正方向平移的距离,负数表示反方向平移 + +平移指定距离。 + +## canvas.scale(sx, sy) +* `sx` {number} 向x轴正方向平移的距离,负数表示反方向平移 +* `sy` {number} 向y轴正方向平移的距离,负数表示反方向平移 + +以原点为中心,将坐标系平移缩放指定倍数。 + +## canvas.scale(float sx, float sy, float px, float py) +## canvas.rotate(float degrees) +## canvas.rotate(float degrees, float px, float py) +## canvas.skew(float sx, float sy) + +# 画笔 + +# 变换矩阵 + +# 路径 + +# Porter-Duff操作 + +# 着色器 + +# 遮罩过滤器 + +# 颜色过滤器 + +# 路径特效 + +# 区域 -## canvas.draw - \ No newline at end of file diff --git a/api/coordinates-based-automation.md b/api/coordinatesBasedAutomation.md similarity index 100% rename from api/coordinates-based-automation.md rename to api/coordinatesBasedAutomation.md diff --git a/api/images/ex-gravity.png b/api/images/ex-gravity.png new file mode 100644 index 0000000..8d89de7 Binary files /dev/null and b/api/images/ex-gravity.png differ diff --git a/api/images/ex-layout-gravity.png b/api/images/ex-layout-gravity.png new file mode 100644 index 0000000..055e488 Binary files /dev/null and b/api/images/ex-layout-gravity.png differ diff --git a/api/images/ex-marginLeft.png b/api/images/ex-marginLeft.png new file mode 100644 index 0000000..790e2a8 Binary files /dev/null and b/api/images/ex-marginLeft.png differ diff --git a/api/images/ex-padding.png b/api/images/ex-padding.png new file mode 100644 index 0000000..669c426 Binary files /dev/null and b/api/images/ex-padding.png differ diff --git a/api/images/ex-properties.png b/api/images/ex-properties.png new file mode 100644 index 0000000..9a22c6c Binary files /dev/null and b/api/images/ex-properties.png differ diff --git a/api/images/ex-w.png b/api/images/ex-w.png new file mode 100644 index 0000000..2e036a7 Binary files /dev/null and b/api/images/ex-w.png differ diff --git a/api/images/ex1-horizontal.png b/api/images/ex1-horizontal.png new file mode 100644 index 0000000..febe511 Binary files /dev/null and b/api/images/ex1-horizontal.png differ diff --git a/api/images/ex1-margin.png b/api/images/ex1-margin.png new file mode 100644 index 0000000..c46c1c6 Binary files /dev/null and b/api/images/ex1-margin.png differ diff --git a/api/images/ex1.png b/api/images/ex1.png new file mode 100644 index 0000000..de220d5 Binary files /dev/null and b/api/images/ex1.png differ diff --git a/api/images/ex2-margin.png b/api/images/ex2-margin.png new file mode 100644 index 0000000..a615bef Binary files /dev/null and b/api/images/ex2-margin.png differ diff --git a/api/images/logo.png b/api/images/logo.png new file mode 100644 index 0000000..6d65dbe Binary files /dev/null and b/api/images/logo.png differ diff --git a/api/index.html b/api/index.html new file mode 100644 index 0000000..f9c30ee --- /dev/null +++ b/api/index.html @@ -0,0 +1,41 @@ + + + + + + Auto.js + + + + + + + + + +
+ + + + + + + + + \ No newline at end of file diff --git a/api/overview.md b/api/overview.md index bf98096..d0c113a 100644 --- a/api/overview.md +++ b/api/overview.md @@ -5,6 +5,7 @@ Auto.js使用[JavaScript](https://developer.mozilla.org/zh-CN/docs/Web/JavaScrip * 因为Auto.js是基于JavaScript的,学习Auto.js的API之前建议先学习JavaScript的基本语法和内置对象,可以使用教程前面的两个JavaScript教程链接来学习。 * 如果您想要使用TypeScript来开发,目前已经有开发者公布了一个可以把使用TypeScript进行Auto.js开发的工具,参见[Auto.js DevTools](https://github.com/pboymt/autojs-dev)。 * 如果想要在电脑而不是手机上开发Auto.js,可以使用VS Code以及相应的Auto.js插件使得在电脑上编辑的脚本能推送到手机运行,参见[Auto.js-VSCode-Extension](https://github.com/hyb1996/Auto.js-VSCode-Extension),Auto.js Pro用户则需要使用[Auto.js-Pro-Ext](https://marketplace.visualstudio.com/items?itemName=hyb1996.auto-js-pro-ext)。 +* 支持Node.js已在计划中,预计2020年底在Auto.js Pro中支持 本文档的章节大致上是以模块来分的,总体上可以分成"自动操作"类模块(控件操作、触摸模拟、按键模拟等)和其他类模块(设备、应用、界面等)。 @@ -25,4 +26,5 @@ Auto.js使用[JavaScript](https://developer.mozilla.org/zh-CN/docs/Web/JavaScrip * threads: 多线程支持。 * ui: UI界面。用于显示自定义的UI界面,和用户交互。 -除此之外,Auto.js内置了对[Promise](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise)。 \ No newline at end of file +除此之外,Auto.js内置了对[Promise](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise)。 + diff --git a/api/static/docsify.min.js b/api/static/docsify.min.js new file mode 100644 index 0000000..f040300 --- /dev/null +++ b/api/static/docsify.min.js @@ -0,0 +1 @@ +!function(){function e(e){var t=Object.create(null);return function(n){var r=i(n)?n:JSON.stringify(n);return t[r]||(t[r]=e(n))}}var t=e(function(e){return e.replace(/([A-Z])/g,function(e){return"-"+e.toLowerCase()})}),n=Object.prototype.hasOwnProperty,r=Object.assign||function(e){for(var t=arguments,r=1;r=i.length)r(n);else if("function"==typeof t)if(2===t.length)t(n,function(t){n=t,o(e+1)});else{var a=t(n);n=void 0===a?n:a,o(e+1)}else o(e+1)};o(0)}var l=!0,c=l&&document.body.clientWidth<=600,u=l&&window.history&&window.history.pushState&&window.history.replaceState&&!navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/),h={};function p(e,t){if(void 0===t&&(t=!1),"string"==typeof e){if(void 0!==window.Vue)return m(e);e=t?m(e):h[e]||(h[e]=m(e))}return e}var d=l&&document,g=l&&d.body,f=l&&d.head;function m(e,t){return t?e.querySelector(t):d.querySelector(e)}function v(e,t){return[].slice.call(t?e.querySelectorAll(t):d.querySelectorAll(e))}function b(e,t){return e=d.createElement(e),t&&(e.innerHTML=t),e}function y(e,t){return e.appendChild(t)}function k(e,t){return e.insertBefore(t,e.children[0])}function w(e,t,n){o(t)?window.addEventListener(e,t):e.addEventListener(t,n)}function x(e,t,n){o(t)?window.removeEventListener(e,t):e.removeEventListener(t,n)}function _(e,t,n){e&&e.classList[n?t:"toggle"](n||t)}var S=Object.freeze({getNode:p,$:d,body:g,head:f,find:m,findAll:v,create:b,appendTo:y,before:k,on:w,off:x,toggleClass:_,style:function(e){y(f,b("style",e))}});function C(e,t){return void 0===t&&(t=""),e&&e.length?(e.forEach(function(e){t+='
  • '+e.title+"
  • ",e.children&&(t+='
    • '+C(e.children)+"
    ")}),t):""}function L(e,t){return'

    '+t.slice(5).trim()+"

    "}var E,A;function $(e){var t,n=e.loaded,r=e.total,i=e.step;!E&&function(){var e=b("div");e.classList.add("progress"),y(g,e),E=e}(),t=i?(t=parseInt(E.style.width||0,10)+i)>80?80:t:Math.floor(n/r*100),E.style.opacity=1,E.style.width=t>=95?"100%":t+"%",t>=95&&(clearTimeout(A),A=setTimeout(function(e){E.style.opacity=0,E.style.width="0%"},200))}var T={};function P(e,t,r){void 0===t&&(t=!1),void 0===r&&(r={});var i=new XMLHttpRequest,o=function(){i.addEventListener.apply(i,arguments)},s=T[e];if(s)return{then:function(e){return e(s.content,s.opt)},abort:a};i.open("GET",e);for(var l in r)n.call(r,l)&&i.setRequestHeader(l,r[l]);return i.send(),{then:function(n,r){if(void 0===r&&(r=a),t){var s=setInterval(function(e){return $({step:Math.floor(5*Math.random()+1)})},500);o("progress",$),o("loadend",function(e){$(e),clearInterval(s)})}o("error",r),o("load",function(t){var a=t.target;if(a.status>=400)r(a);else{var o=T[e]={content:a.response,opt:{updatedAt:i.getResponseHeader("last-modified")}};n(o.content,o.opt)}})},abort:function(e){return 4!==i.readyState&&i.abort()}}}function F(e,t){e.innerHTML=e.innerHTML.replace(/var\(\s*--theme-color.*?\)/g,t)}var O=/([^{]*?)\w(?=\})/g,M={YYYY:"getFullYear",YY:"getYear",MM:function(e){return e.getMonth()+1},DD:"getDate",HH:"getHours",mm:"getMinutes",ss:"getSeconds"};var N="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function j(e,t){return e(t={exports:{}},t.exports),t.exports}var q=j(function(e,t){(function(){var t={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:p,hr:/^( *[-*_]){3,} *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,nptable:p,lheading:/^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,blockquote:/^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,list:/^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:/^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,def:/^ *\[([^\]]+)\]: *]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,table:p,paragraph:/^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,text:/^[^\n]+/};t.bullet=/(?:[*+-]|\d+\.)/,t.item=/^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/,t.item=l(t.item,"gm")(/bull/g,t.bullet)(),t.list=l(t.list)(/bull/g,t.bullet)("hr","\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))")("def","\\n+(?="+t.def.source+")")(),t.blockquote=l(t.blockquote)("def",t.def)(),t._tag="(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b",t.html=l(t.html)("comment",//)("closed",/<(tag)[\s\S]+?<\/\1>/)("closing",/])*?>/)(/tag/g,t._tag)(),t.paragraph=l(t.paragraph)("hr",t.hr)("heading",t.heading)("lheading",t.lheading)("blockquote",t.blockquote)("tag","<"+t._tag)("def",t.def)(),t.normal=d({},t),t.gfm=d({},t.normal,{fences:/^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,paragraph:/^/,heading:/^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/}),t.gfm.paragraph=l(t.paragraph)("(?!","(?!"+t.gfm.fences.source.replace("\\1","\\2")+"|"+t.list.source.replace("\\1","\\3")+"|")(),t.tables=d({},t.gfm,{nptable:/^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,table:/^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/});function n(e){this.tokens=[],this.tokens.links={},this.options=e||g.defaults,this.rules=t.normal,this.options.gfm&&(this.options.tables?this.rules=t.tables:this.rules=t.gfm)}n.rules=t,n.lex=function(e,t){return new n(t).lex(e)},n.prototype.lex=function(e){return e=e.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n"),this.token(e,!0)},n.prototype.token=function(e,n,r){var i,a,o,s,l,c,u,h,p;for(e=e.replace(/^ +$/gm,"");e;)if((o=this.rules.newline.exec(e))&&(e=e.substring(o[0].length),o[0].length>1&&this.tokens.push({type:"space"})),o=this.rules.code.exec(e))e=e.substring(o[0].length),o=o[0].replace(/^ {4}/gm,""),this.tokens.push({type:"code",text:this.options.pedantic?o:o.replace(/\n+$/,"")});else if(o=this.rules.fences.exec(e))e=e.substring(o[0].length),this.tokens.push({type:"code",lang:o[2],text:o[3]||""});else if(o=this.rules.heading.exec(e))e=e.substring(o[0].length),this.tokens.push({type:"heading",depth:o[1].length,text:o[2]});else if(n&&(o=this.rules.nptable.exec(e))){for(e=e.substring(o[0].length),c={type:"table",header:o[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:o[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:o[3].replace(/\n$/,"").split("\n")},h=0;h ?/gm,""),this.token(o,n,!0),this.tokens.push({type:"blockquote_end"});else if(o=this.rules.list.exec(e)){for(e=e.substring(o[0].length),s=o[2],this.tokens.push({type:"list_start",ordered:s.length>1}),i=!1,p=(o=o[0].match(this.rules.item)).length,h=0;h1&&l.length>1||(e=o.slice(h+1).join("\n")+e,h=p-1)),a=i||/\n\n(?!\s*$)/.test(c),h!==p-1&&(i="\n"===c.charAt(c.length-1),a||(a=i)),this.tokens.push({type:a?"loose_item_start":"list_item_start"}),this.token(c,!1,r),this.tokens.push({type:"list_item_end"});this.tokens.push({type:"list_end"})}else if(o=this.rules.html.exec(e))e=e.substring(o[0].length),this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:!this.options.sanitizer&&("pre"===o[1]||"script"===o[1]||"style"===o[1]),text:o[0]});else if(!r&&n&&(o=this.rules.def.exec(e)))e=e.substring(o[0].length),this.tokens.links[o[1].toLowerCase()]={href:o[2],title:o[3]};else if(n&&(o=this.rules.table.exec(e))){for(e=e.substring(o[0].length),c={type:"table",header:o[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:o[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:o[3].replace(/(?: *\| *)?\n$/,"").split("\n")},h=0;h])/,autolink:/^<([^ <>]+(@|:\/)[^ <>]+)>/,url:p,tag:/^|^<\/?\w+(?:"[^"]*"|'[^']*'|[^<'">])*?>/,link:/^!?\[(inside)\]\(href\)/,reflink:/^!?\[(inside)\]\s*\[([^\]]*)\]/,nolink:/^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,strong:/^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,em:/^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,code:/^(`+)([\s\S]*?[^`])\1(?!`)/,br:/^ {2,}\n(?!\s*$)/,del:p,text:/^[\s\S]+?(?=[\\?(?:\s+['"]([\s\S]*?)['"])?\s*/,r.link=l(r.link)("inside",r._inside)("href",r._href)(),r.reflink=l(r.reflink)("inside",r._inside)(),r.normal=d({},r),r.pedantic=d({},r.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/}),r.gfm=d({},r.normal,{escape:l(r.escape)("])","~|])")(),url:/^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,del:/^~~(?=\S)([\s\S]*?\S)~~/,text:l(r.text)("]|","~]|")("|","|https?://|")()}),r.breaks=d({},r.gfm,{br:l(r.br)("{2,}","*")(),text:l(r.gfm.text)("{2,}","*")()});function i(e,t){if(this.options=t||g.defaults,this.links=e,this.rules=r.normal,this.renderer=this.options.renderer||new a,this.renderer.options=this.options,!this.links)throw new Error("Tokens array requires a `links` property.");this.options.gfm?this.options.breaks?this.rules=r.breaks:this.rules=r.gfm:this.options.pedantic&&(this.rules=r.pedantic)}i.rules=r,i.output=function(e,t,n){return new i(t,n).output(e)},i.prototype.output=function(e){for(var t,n,r,i,a="";e;)if(i=this.rules.escape.exec(e))e=e.substring(i[0].length),a+=i[1];else if(i=this.rules.autolink.exec(e))e=e.substring(i[0].length),"@"===i[2]?(n=s(":"===i[1].charAt(6)?this.mangle(i[1].substring(7)):this.mangle(i[1])),r=this.mangle("mailto:")+n):r=n=s(i[1]),a+=this.renderer.link(r,null,n);else if(this.inLink||!(i=this.rules.url.exec(e))){if(i=this.rules.tag.exec(e))!this.inLink&&/^/i.test(i[0])&&(this.inLink=!1),e=e.substring(i[0].length),a+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(i[0]):s(i[0]):i[0];else if(i=this.rules.link.exec(e))e=e.substring(i[0].length),this.inLink=!0,a+=this.outputLink(i,{href:i[2],title:i[3]}),this.inLink=!1;else if((i=this.rules.reflink.exec(e))||(i=this.rules.nolink.exec(e))){if(e=e.substring(i[0].length),t=(i[2]||i[1]).replace(/\s+/g," "),!(t=this.links[t.toLowerCase()])||!t.href){a+=i[0].charAt(0),e=i[0].substring(1)+e;continue}this.inLink=!0,a+=this.outputLink(i,t),this.inLink=!1}else if(i=this.rules.strong.exec(e))e=e.substring(i[0].length),a+=this.renderer.strong(this.output(i[2]||i[1]));else if(i=this.rules.em.exec(e))e=e.substring(i[0].length),a+=this.renderer.em(this.output(i[2]||i[1]));else if(i=this.rules.code.exec(e))e=e.substring(i[0].length),a+=this.renderer.codespan(s(i[2].trim(),!0));else if(i=this.rules.br.exec(e))e=e.substring(i[0].length),a+=this.renderer.br();else if(i=this.rules.del.exec(e))e=e.substring(i[0].length),a+=this.renderer.del(this.output(i[1]));else if(i=this.rules.text.exec(e))e=e.substring(i[0].length),a+=this.renderer.text(s(this.smartypants(i[0])));else if(e)throw new Error("Infinite loop on byte: "+e.charCodeAt(0))}else e=e.substring(i[0].length),r=n=s(i[1]),a+=this.renderer.link(r,null,n);return a},i.prototype.outputLink=function(e,t){var n=s(t.href),r=t.title?s(t.title):null;return"!"!==e[0].charAt(0)?this.renderer.link(n,r,this.output(e[1])):this.renderer.image(n,r,s(e[1]))},i.prototype.smartypants=function(e){return this.options.smartypants?e.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…"):e},i.prototype.mangle=function(e){if(!this.options.mangle)return e;for(var t,n="",r=e.length,i=0;i.5&&(t="x"+t.toString(16)),n+="&#"+t+";";return n};function a(e){this.options=e||{}}a.prototype.code=function(e,t,n){if(this.options.highlight){var r=this.options.highlight(e,t);null!=r&&r!==e&&(n=!0,e=r)}return t?'
    '+(n?e:s(e,!0))+"\n
    \n":"
    "+(n?e:s(e,!0))+"\n
    "},a.prototype.blockquote=function(e){return"
    \n"+e+"
    \n"},a.prototype.html=function(e){return e},a.prototype.heading=function(e,t,n){return"'+e+"\n"},a.prototype.hr=function(){return this.options.xhtml?"
    \n":"
    \n"},a.prototype.list=function(e,t){var n=t?"ol":"ul";return"<"+n+">\n"+e+"\n"},a.prototype.listitem=function(e){return"
  • "+e+"
  • \n"},a.prototype.paragraph=function(e){return"

    "+e+"

    \n"},a.prototype.table=function(e,t){return"\n\n"+e+"\n\n"+t+"\n
    \n"},a.prototype.tablerow=function(e){return"\n"+e+"\n"},a.prototype.tablecell=function(e,t){var n=t.header?"th":"td";return(t.align?"<"+n+' style="text-align:'+t.align+'">':"<"+n+">")+e+"\n"},a.prototype.strong=function(e){return""+e+""},a.prototype.em=function(e){return""+e+""},a.prototype.codespan=function(e){return""+e+""},a.prototype.br=function(){return this.options.xhtml?"
    ":"
    "},a.prototype.del=function(e){return""+e+""},a.prototype.link=function(e,t,n){if(this.options.sanitize){try{var r=decodeURIComponent((i=e,i.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi,function(e,t){return"colon"===(t=t.toLowerCase())?":":"#"===t.charAt(0)?"x"===t.charAt(1)?String.fromCharCode(parseInt(t.substring(2),16)):String.fromCharCode(+t.substring(1)):""}))).replace(/[^\w:]/g,"").toLowerCase()}catch(e){return n}if(0===r.indexOf("javascript:")||0===r.indexOf("vbscript:")||0===r.indexOf("data:"))return n}var i;this.options.baseUrl&&!h.test(e)&&(e=c(this.options.baseUrl,e));var a='
    "},a.prototype.image=function(e,t,n){this.options.baseUrl&&!h.test(e)&&(e=c(this.options.baseUrl,e));var r=''+n+'":">"},a.prototype.text=function(e){return e};function o(e){this.tokens=[],this.token=null,this.options=e||g.defaults,this.options.renderer=this.options.renderer||new a,this.renderer=this.options.renderer,this.renderer.options=this.options}o.parse=function(e,t,n){return new o(t,n).parse(e)},o.prototype.parse=function(e){this.inline=new i(e.links,this.options,this.renderer),this.tokens=e.reverse();for(var t="";this.next();)t+=this.tok();return t},o.prototype.next=function(){return this.token=this.tokens.pop()},o.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0},o.prototype.parseText=function(){for(var e=this.token.text;"text"===this.peek().type;)e+="\n"+this.next().text;return this.inline.output(e)},o.prototype.tok=function(){switch(this.token.type){case"space":return"";case"hr":return this.renderer.hr();case"heading":return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,this.token.text);case"code":return this.renderer.code(this.token.text,this.token.lang,this.token.escaped);case"table":var e,t,n,r,i="",a="";for(n="",e=0;e/g,">").replace(/"/g,""").replace(/'/g,"'")}function l(e,t){return e=e.source,t=t||"",function n(r,i){return r?(i=(i=i.source||i).replace(/(^|[^\[])\^/g,"$1"),e=e.replace(r,i),n):new RegExp(e,t)}}function c(e,t){return u[" "+e]||(/^[^:]+:\/*[^/]*$/.test(e)?u[" "+e]=e+"/":u[" "+e]=e.replace(/[^/]*$/,"")),e=u[" "+e],"//"===t.slice(0,2)?e.replace(/:[\s\S]*/,":")+t:"/"===t.charAt(0)?e.replace(/(:\/*[^/]*)[\s\S]*/,"$1")+t:e+t}var u={},h=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;function p(){}p.exec=p;function d(e){for(var t,n,r=arguments,i=1;iAn error occurred:

    "+s(e.message+"",!0)+"
    ";throw e}}g.options=g.setOptions=function(e){return d(g.defaults,e),g},g.defaults={gfm:!0,tables:!0,breaks:!1,pedantic:!1,sanitize:!1,sanitizer:null,mangle:!0,smartLists:!1,silent:!1,highlight:null,langPrefix:"lang-",smartypants:!1,headerPrefix:"",renderer:new a,xhtml:!1,baseUrl:null},g.Parser=o,g.parser=o.parse,g.Renderer=a,g.Lexer=n,g.lexer=n.lex,g.InlineLexer=i,g.inlineLexer=i.output,g.parse=g,e.exports=g}).call(function(){return this||("undefined"!=typeof window?window:N)}())}),R=j(function(e){var t="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},n=function(){var e=/\blang(?:uage)?-(\w+)\b/i,n=0,r=t.Prism={manual:t.Prism&&t.Prism.manual,disableWorkerMessageHandler:t.Prism&&t.Prism.disableWorkerMessageHandler,util:{encode:function(e){return e instanceof i?new i(e.type,r.util.encode(e.content),e.alias):"Array"===r.util.type(e)?e.map(r.util.encode):e.replace(/&/g,"&").replace(/e.length)return;if(!(w instanceof l)){p.lastIndex=0;var x=1;if(!(A=p.exec(w))&&f&&y!=t.length-1){if(p.lastIndex=k,!(A=p.exec(e)))break;for(var _=A.index+(g?A[1].length:0),S=A.index+A[0].length,C=y,L=k,E=t.length;C=(L+=t[C].length)&&(++y,k=L);if(t[y]instanceof l||t[C-1].greedy)continue;x=C-y,w=e.slice(k,L),A.index-=k}if(A){g&&(m=A[1].length);S=(_=A.index+m)+(A=A[0].slice(m)).length;var A,$=w.slice(0,_),T=w.slice(S),P=[y,x];$&&(++y,k+=$.length,P.push($));var F=new l(c,d?r.tokenize(A,d):A,v,A,f);if(P.push(F),T&&P.push(T),Array.prototype.splice.apply(t,P),1!=x&&r.matchGrammar(e,t,n,y,k,!0,c),o)break}else if(o)break}}}}},tokenize:function(e,t,n){var i=[e],a=t.rest;if(a){for(var o in a)t[o]=a[o];delete t.rest}return r.matchGrammar(e,i,t,0,0,!1),i},hooks:{all:{},add:function(e,t){var n=r.hooks.all;n[e]=n[e]||[],n[e].push(t)},run:function(e,t){var n=r.hooks.all[e];if(n&&n.length)for(var i,a=0;i=n[a++];)i(t)}}},i=r.Token=function(e,t,n,r,i){this.type=e,this.content=t,this.alias=n,this.length=0|(r||"").length,this.greedy=!!i};if(i.stringify=function(e,t,n){if("string"==typeof e)return e;if("Array"===r.util.type(e))return e.map(function(n){return i.stringify(n,t,e)}).join("");var a={type:e.type,content:i.stringify(e.content,t,n),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:n};if(e.alias){var o="Array"===r.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(a.classes,o)}r.hooks.run("wrap",a);var s=Object.keys(a.attributes).map(function(e){return e+'="'+(a.attributes[e]||"").replace(/"/g,""")+'"'}).join(" ");return"<"+a.tag+' class="'+a.classes.join(" ")+'"'+(s?" "+s:"")+">"+a.content+""},!t.document)return t.addEventListener?(r.disableWorkerMessageHandler||t.addEventListener("message",function(e){var n=JSON.parse(e.data),i=n.language,a=n.code,o=n.immediateClose;t.postMessage(r.highlight(a,r.languages[i],i)),o&&t.close()},!1),t.Prism):t.Prism;var a=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return a&&(r.filename=a.src,r.manual||a.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(r.highlightAll):window.setTimeout(r.highlightAll,16):document.addEventListener("DOMContentLoaded",r.highlightAll))),t.Prism}();e.exports&&(e.exports=n),void 0!==N&&(N.Prism=n),n.languages.markup={comment://,prolog:/<\?[\s\S]+?\?>/,doctype://i,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/i,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+)/i,inside:{punctuation:[/^=/,{pattern:/(^|[^\\])["']/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},n.languages.markup.tag.inside["attr-value"].inside.entity=n.languages.markup.entity,n.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),n.languages.xml=n.languages.markup,n.languages.html=n.languages.markup,n.languages.mathml=n.languages.markup,n.languages.svg=n.languages.markup,n.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(?:;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^{}\s][^{};]*?(?=\s*\{)/,string:{pattern:/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},property:/[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,important:/\B!important\b/i,function:/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},n.languages.css.atrule.inside.rest=n.util.clone(n.languages.css),n.languages.markup&&(n.languages.insertBefore("markup","tag",{style:{pattern:/()[\s\S]*?(?=<\/style>)/i,lookbehind:!0,inside:n.languages.css,alias:"language-css",greedy:!0}}),n.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:n.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:n.languages.css}},alias:"language-css"}},n.languages.markup.tag)),n.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/[a-z0-9_]+(?=\()/i,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/},n.languages.javascript=n.languages.extend("clike",{keyword:/\b(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/,number:/\b-?(?:0[xX][\dA-Fa-f]+|0[bB][01]+|0[oO][0-7]+|\d*\.?\d+(?:[Ee][+-]?\d+)?|NaN|Infinity)\b/,function:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*\()/i,operator:/-[-=]?|\+[+=]?|!=?=?|<>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/}),n.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[[^\]\r\n]+]|\\.|[^/\\\[\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0,greedy:!0},"function-variable":{pattern:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=\s*(?:function\b|(?:\([^()]*\)|[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/i,alias:"function"}}),n.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\[\s\S]|[^\\`])*`/,greedy:!0,inside:{interpolation:{pattern:/\$\{[^}]+\}/,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:n.languages.javascript}},string:/[\s\S]+/}}}),n.languages.markup&&n.languages.insertBefore("markup","tag",{script:{pattern:/()[\s\S]*?(?=<\/script>)/i,lookbehind:!0,inside:n.languages.javascript,alias:"language-javascript",greedy:!0}}),n.languages.js=n.languages.javascript,"undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector&&(self.Prism.fileHighlight=function(){var e={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"};Array.prototype.slice.call(document.querySelectorAll("pre[data-src]")).forEach(function(t){for(var r,i=t.getAttribute("data-src"),a=t,o=/\blang(?:uage)?-(?!\*)(\w+)\b/i;a&&!o.test(a.className);)a=a.parentNode;if(a&&(r=(t.className.match(o)||[,""])[1]),!r){var s=(i.match(/\.(\w+)$/)||[,""])[1];r=e[s]||s}var l=document.createElement("code");l.className="language-"+r,t.textContent="",l.textContent="Loading…",t.appendChild(l);var c=new XMLHttpRequest;c.open("GET",i,!0),c.onreadystatechange=function(){4==c.readyState&&(c.status<400&&c.responseText?(l.textContent=c.responseText,n.highlightElement(l)):c.status>=400?l.textContent="✖ Error "+c.status+" while fetching file: "+c.statusText:l.textContent="✖ Error: File does not exist or is empty")},c.send(null)})},document.addEventListener("DOMContentLoaded",self.Prism.fileHighlight))});function H(e,t){var n=[],r={};return e.forEach(function(e){var i=e.level||1,a=i-1;i>t||(r[a]?r[a].children=(r[a].children||[]).concat(e):n.push(e),r[i]=e)}),n}var z={},I=/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g;function B(e){return e.toLowerCase()}function U(e){if("string"!=typeof e)return"";var t=e.trim().replace(/[A-Z]+/g,B).replace(/<[^>\d]+>/g,"").replace(I,"").replace(/\s/g,"-").replace(/-+/g,"-").replace(/^(\d)/,"_$1"),r=z[t];return r=n.call(z,t)?r+1:0,z[t]=r,r&&(t=t+"-"+r),t}U.clear=function(){z={}};function D(e,t){return''+t+''}var Y=decodeURIComponent,W=encodeURIComponent;function G(e){var t={};return(e=e.trim().replace(/^(\?|#|&)/,""))?(e.split("&").forEach(function(e){var n=e.replace(/\+/g," ").split("=");t[n[0]]=n[1]&&Y(n[1])}),t):t}function X(e,t){void 0===t&&(t=[]);var n=[];for(var r in e)t.indexOf(r)>-1||n.push(e[r]?(W(r)+"="+W(e[r])).toLowerCase():W(r));return n.length?"?"+n.join("&"):""}var Q=e(function(e){return/(:|(\/{2}))/g.test(e)}),V=e(function(e){return/\/$/g.test(e)?e:(e=e.match(/(\S*\/)[^/]+$/))?e[1]:""}),Z=e(function(e){return e.replace(/^\/+/,"/").replace(/([^:])\/{2,}/g,"$1/")});function J(){for(var e=[],t=arguments.length;t--;)e[t]=arguments[t];return Z(e.join("/"))}var K=e(function(e){return e.replace("#","?id=")}),ee={};function te(e){void 0===e&&(e="");var t={};return e&&(e=e.replace(/:([\w-]+)=?([\w-]+)?/g,function(e,n,r){return t[n]=r&&r.replace(/"/g,"")||!0,""}).trim()),{str:e,config:t}}var ne={markdown:function(e){return{url:e}},iframe:function(e,t){return{code:'"}},video:function(e,t){return{code:'"}},audio:function(e,t){return{code:'"}},code:function(e,t){var n=e.match(/\.(\w+)$/);return"md"===(n=t||n&&n[1])&&(n="markdown"),{url:e,lang:n}}},re=function(t,n){this.config=t,this.router=n,this.cacheTree={},this.toc=[],this.linkTarget=t.externalLinkTarget||"_blank",this.contentBase=n.getBasePath();var a,s=this._initRenderer(),c=t.markdown||{};o(c)?a=c(q,s):(q.setOptions(r(c,{renderer:r(s,c.renderer)})),a=q),this._marked=a,this.compile=e(function(e){var n="";if(!e)return e;n=i(e)?a(e):a.parser(e),n=t.noEmoji?n:(r=n,r.replace(/<(pre|template|code)[^>]*?>[\s\S]+?<\/(pre|template|code)>/g,function(e){return e.replace(/:/g,"__colon__")}).replace(/:(\w+?):/gi,l&&window.emojify||D).replace(/__colon__/g,":"));var r;return U.clear(),n})};re.prototype.compileEmbed=function(e,t){var n,r=te(t),i=r.str,a=r.config;if(t=i,a.include){Q(e)||(e=J(this.contentBase,V(this.router.getCurrentPath()),e));var o;if(a.type&&(o=ne[a.type]))(n=o.call(this,e,t)).type=a.type;else{var s="code";/\.(md|markdown)/.test(e)?s="markdown":/\.html?/.test(e)?s="iframe":/\.(mp4|ogg)/.test(e)?s="video":/\.mp3/.test(e)&&(s="audio"),(n=ne[s].call(this,e,t)).type=s}return n}},re.prototype._matchNotCompileLink=function(e){for(var t=this.config.noCompileLinks||[],n=0;n
    '+e+""},a.code=e.code=function(e,t){void 0===t&&(t=""),e=e.replace(/@DOCSIFY_QM@/g,"`");return'
    '+R.highlight(e,R.languages[t]||R.languages.markup)+"
    "},a.link=e.link=function(e,r,a){void 0===r&&(r="");var o="",s=te(r),l=s.str,c=s.config;return r=l,/:|(\/{2})/.test(e)||i._matchNotCompileLink(e)||c.ignore?o+=' target="'+t+'"':(e===i.config.homepage&&(e="README"),e=n.toURL(e,null,n.getCurrentPath())),c.target&&(o+=" target="+c.target),c.disabled&&(o+=" disabled",e="javascript:void(0)"),r&&(o+=' title="'+r+'"'),'"+a+""},a.paragraph=e.paragraph=function(e){return/^!>/.test(e)?L("tip",e):/^\?>/.test(e)?L("warn",e):"

    "+e+"

    "},a.image=e.image=function(e,t,i){var a=e,o="",s=te(t);return t=s.str,s.config["no-zoom"]&&(o+=" data-no-zoom"),t&&(o+=' title="'+t+'"'),Q(e)||(a=J(r,V(n.getCurrentPath()),e)),''+i+'"};var o=/^\[([ x])\] +/;return a.listitem=e.listitem=function(e){var t=o.exec(e);return t&&(e=e.replace(o,'")),""+e+"\n"},e.origin=a,e},re.prototype.sidebar=function(e,t){var n=this.router.getCurrentPath(),r="";if(e)r=(r=this.compile(e))&&r.match(/]*>([\s\S]+)<\/ul>/g)[0];else{var i=this.cacheTree[n]||H(this.toc,t);r=C(i,"
      "),this.cacheTree[n]=i}return r},re.prototype.subSidebar=function(e){if(e){var t=this.router.getCurrentPath(),n=this.cacheTree,r=this.toc;r[0]&&r[0].ignoreAllSubs&&r.splice(0),r[0]&&1===r[0].level&&r.shift();for(var i=0;i')}this.toc=[]},re.prototype.article=function(e){return this.compile(e)},re.prototype.cover=function(e){var t=this.toc.slice(),n=this.compile(e);return this.toc=t.slice(),n};var ie=d.title;function ae(){var e=p("section.cover");if(e){var t=e.getBoundingClientRect().height;window.pageYOffset>=t||e.classList.contains("hidden")?_(g,"add","sticky"):_(g,"remove","sticky")}}function oe(e,t,n,r){var i,a=v(t=p(t),"a"),o=decodeURI(e.toURL(e.getCurrentPath()));return a.sort(function(e,t){return t.href.length-e.href.length}).forEach(function(e){var t=e.getAttribute("href"),r=n?e.parentNode:e;0!==o.indexOf(t)||i?_(r,"remove","active"):(i=e,_(r,"add","active"))}),r&&(d.title=i?i.innerText+" - "+ie:ie),i}var se=function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.duration=t.duration||1e3,this.ease=t.easing||this._defaultEase,this.start=t.start,this.end=t.end,this.frame=null,this.next=null,this.isRunning=!1,this.events={},this.direction=this.startthis.end&&e>=this.next}[this.direction]}},{key:"_defaultEase",value:function(e,t,n,r){return(e/=r/2)<1?n/2*e*e+t:-n/2*(--e*(e-2)-1)+t}}]),e}(),ce={},ue=!1,he=null,pe=!0,de=0;function ge(e){if(pe){for(var t,n=p(".sidebar"),r=v(".anchor"),i=m(n,".sidebar-nav"),a=m(n,"li.active"),o=document.documentElement,s=(o&&o.scrollTop||document.body.scrollTop)-de,l=0,c=r.length;ls){t||(t=u);break}t=u}if(t){var h=ce[fe(e,t.getAttribute("data-id"))];if(h&&h!==a&&(a&&a.classList.remove("active"),h.classList.add("active"),a=h,!ue&&g.classList.contains("sticky"))){var d=n.clientHeight,f=a.offsetTop+a.clientHeight+40,b=f-0=i.scrollTop&&f<=i.scrollTop+d?i.scrollTop:b?0:f-d;n.scrollTop=y}}}}function fe(e,t){return e+"?id="+t}function me(e,t){if(t){var n=m("#"+t);n&&(r=n,he&&he.stop(),pe=!1,he=new le({start:window.pageYOffset,end:r.getBoundingClientRect().top+window.pageYOffset,duration:500}).on("tick",function(e){return window.scrollTo(0,e)}).on("done",function(){pe=!0,he=null}).begin());var r,i=ce[fe(e,t)],a=m(p(".sidebar"),"li.active");a&&a.classList.remove("active"),i&&i.classList.add("active")}}var ve=d.scrollingElement||d.documentElement;var be={};function ye(e,t){var n=e.compiler,i=e.raw;void 0===i&&(i="");var a,o=e.fetch;if(a=be[i])return t(a);var s=n._marked,l=s.lexer(i),c=[],u=s.InlineLexer.rules.link,h=l.links;l.forEach(function(e,t){"paragraph"===e.type&&(e.text=e.text.replace(new RegExp(u.source,"g"),function(e,r,i,a){var o=n.compileEmbed(i,a);return o?("markdown"!==o.type&&"code"!==o.type||c.push({index:t,embed:o}),o.code):e}))});var p=0;!function e(t,n){var r=t.step;void 0===r&&(r=0);var i=t.embedTokens,a=t.compile,o=t.fetch,s=i[r];if(!s)return n({});P(s.embed.url).then(function(t){var l;t&&("markdown"===s.embed.type?l=a.lexer(t):"code"===s.embed.type&&(l=a.lexer("```"+s.embed.lang+"\n"+t.replace(/`/g,"@DOCSIFY_QM@")+"\n```\n"))),n({token:s,embedToken:l}),e({step:++r,compile:a,embedTokens:i,fetch:o},n)})}({compile:s,embedTokens:c,fetch:o},function(e){var n=e.embedToken,a=e.token;if(a){var o=a.index+p;r(h,n.links),l=l.slice(0,o).concat(n,l.slice(o+1)),p+=n.length-1}else be[i]=l.concat(),l.links=be[i].links=h,t(l)})}function ke(){var e=v(".markdown-section>script").filter(function(e){return!/template/.test(e.type)})[0];if(!e)return!1;var t=e.innerText.trim();if(!t)return!1;setTimeout(function(e){window.__EXECUTE_RESULT__=new Function(t)()},0)}function we(e,t,n){return t="function"==typeof n?n(t):"string"==typeof n?function(e){var t=[],n=0;return e.replace(O,function(r,i,a){t.push(e.substring(n,a-1)),n=a+=r.length+1,t.push(function(e){return("00"+("string"==typeof M[r]?e[M[r]]():M[r](e))).slice(-r.length)})}),n!==e.length&&t.push(e.substring(n)),function(e){for(var n="",r=0,i=e||new Date;r'):"")),t.coverpage&&(a+='
      \x3c!--cover--\x3e
      '),a+=function(e){var t='';return(c?t+"
      ":"
      "+t)+'
      \x3c!--main--\x3e
      '}(t),e._renderTo(i,a,!0)):e.rendered=!0;var s;t.mergeNavbar&&c?o=m(".sidebar"):(r.classList.add("app-nav"),t.repo||r.classList.add("no-badge")),t.loadNavbar&&k(o,r),t.themeColor&&(d.head.appendChild(b("div",(u=t.themeColor,"")).firstElementChild),function(e){if(!(window.CSS&&window.CSS.supports&&window.CSS.supports("(--v:red)"))){var t=v("style:not(.inserted),link");[].forEach.call(t,function(t){if("STYLE"===t.nodeName)F(t,e);else if("LINK"===t.nodeName){var n=t.getAttribute("href");if(!/\.css$/.test(n))return;P(n).then(function(t){var n=b("style",t);f.appendChild(n),F(n,e)})}})}}(t.themeColor));var u;e._updateRender(),_(g,"ready")}var Se={};var Ce=function(e){this.config=e};Ce.prototype.getBasePath=function(){return this.config.basePath},Ce.prototype.getFile=function(e,t){void 0===e&&(e=this.getCurrentPath());var n=this.config,r=this.getBasePath(),i="string"==typeof n.ext?n.ext:".md";e=n.alias?function e(t,n,r){var i=Object.keys(n).filter(function(e){return(Se[e]||(Se[e]=new RegExp("^"+e+"$"))).test(t)&&t!==r})[0];return i?e(t.replace(Se[i],n[i]),n,t):t}(e,n.alias):e,a=e,o=i;var a,o;return e=(e=new RegExp("\\.("+o.replace(/^\./,"")+"|html)$","g").test(a)?a:/\/$/g.test(a)?a+"README"+o:""+a+o)==="/README"+i?n.homepage||e:e,e=Q(e)?e:J(r,e),t&&(e=e.replace(new RegExp("^"+r),"")),e},Ce.prototype.onchange=function(e){void 0===e&&(e=a),e()},Ce.prototype.getCurrentPath=function(){},Ce.prototype.normalize=function(){},Ce.prototype.parse=function(){},Ce.prototype.toURL=function(e,t,n){var i=n&&"#"===e[0],a=this.parse(K(e));if(a.query=r({},a.query,t),e=(e=a.path+X(a.query)).replace(/\.md(\?)|\.md$/,"$1"),i){var o=n.indexOf("?");e=(o>0?n.substr(0,o):n)+e}return Z("/"+e)};function Le(e){var t=location.href.indexOf("#");location.replace(location.href.slice(0,t>=0?t:0)+"#"+e)}var Ee=function(e){function t(t){e.call(this,t),this.mode="hash"}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype.getBasePath=function(){var e=window.location.pathname||"",t=this.config.basePath;return/^(\/|https?:)/g.test(t)?t:Z(e+"/"+t)},t.prototype.getCurrentPath=function(){var e=location.href,t=e.indexOf("#");return-1===t?"":e.slice(t+1)},t.prototype.onchange=function(e){void 0===e&&(e=a),w("hashchange",e)},t.prototype.normalize=function(){var e=this.getCurrentPath();if("/"===(e=K(e)).charAt(0))return Le(e);Le("/"+e)},t.prototype.parse=function(e){void 0===e&&(e=location.href);var t="",n=e.indexOf("#");n>=0&&(e=e.slice(n+1));var r=e.indexOf("?");return r>=0&&(t=e.slice(r+1),e=e.slice(0,r)),{path:e,file:this.getFile(e,!0),query:G(t)}},t.prototype.toURL=function(t,n,r){return"#"+e.prototype.toURL.call(this,t,n,r)},t}(Ce),Ae=function(e){function t(t){e.call(this,t),this.mode="history"}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype.getCurrentPath=function(){var e=this.getBasePath(),t=window.location.pathname;return e&&0===t.indexOf(e)&&(t=t.slice(e.length)),(t||"/")+window.location.search+window.location.hash},t.prototype.onchange=function(e){void 0===e&&(e=a),w("click",function(t){var n="A"===t.target.tagName?t.target:t.target.parentNode;if("A"===n.tagName&&!/_blank/.test(n.target)){t.preventDefault();var r=n.href;window.history.pushState({key:r},"",r),e()}}),w("popstate",e)},t.prototype.parse=function(e){void 0===e&&(e=location.href);var t="",n=e.indexOf("?");n>=0&&(t=e.slice(n+1),e=e.slice(0,n));var r=J(location.origin),i=e.indexOf(r);return i>-1&&(e=e.slice(i+r.length)),{path:e,file:this.getFile(e),query:G(t)}},t}(Ce);var $e={};function Te(e){e.router.normalize(),e.route=e.router.parse(),g.setAttribute("data-page",e.route.file)}function Pe(e){!function(e){var t=function(e){return g.classList.toggle("close")};w(e=p(e),"click",function(e){e.stopPropagation(),t()}),c&&w(g,"click",function(e){return g.classList.contains("close")&&t()})}("button.sidebar-toggle",e.router),t=".sidebar",e.router,w(t=p(t),"click",function(e){var t=e.target;"A"===t.nodeName&&t.nextSibling&&t.nextSibling.classList.contains("app-sub-sidebar")&&_(t.parentNode,"collapse")});var t;e.config.coverpage?!c&&w("scroll",ae):g.classList.add("sticky")}function Fe(e,t,n,r,i,a){e=a?e:e.replace(/\/$/,""),(e=V(e))&&P(i.router.getFile(e+n)+t,!1,i.config.requestHeaders).then(r,function(a){return Fe(e,t,n,r,i)})}var Oe=Object.freeze({cached:e,hyphenate:t,hasOwn:n,merge:r,isPrimitive:i,noop:a,isFn:o,inBrowser:l,isMobile:c,supportsPushState:u,parseQuery:G,stringifyQuery:X,isAbsolutePath:Q,getParentPath:V,cleanPath:Z,getPath:J,replaceSlug:K});function Me(){this._init()}var Ne=Me.prototype;Ne._init=function(){this.config=function(){var e=r({el:"#app",repo:"",maxLevel:6,subMaxLevel:0,loadSidebar:null,loadNavbar:null,homepage:"README.md",coverpage:"",basePath:"",auto2top:!1,name:"",themeColor:"",nameLink:window.location.pathname,autoHeader:!1,executeScript:null,noEmoji:!1,ga:"",ext:".md",mergeNavbar:!1,formatUpdated:"",externalLinkTarget:"_blank",routerMode:"hash",noCompileLinks:[]},window.$docsify),a=document.currentScript||[].slice.call(document.getElementsByTagName("script")).filter(function(e){return/docsify\./.test(e.src)})[0];if(a){for(var o in e)if(n.call(e,o)){var s=a.getAttribute("data-"+t(o));i(s)&&(e[o]=""===s||s)}!0===e.loadSidebar&&(e.loadSidebar="_sidebar"+e.ext),!0===e.loadNavbar&&(e.loadNavbar="_navbar"+e.ext),!0===e.coverpage&&(e.coverpage="_coverpage"+e.ext),!0===e.repo&&(e.repo=""),!0===e.name&&(e.name="")}return window.$docsify=e,e}(),(e=this)._hooks={},e._lifecycle={},["init","mounted","beforeEach","afterEach","doneEach","ready"].forEach(function(t){var n=e._hooks[t]=[];e._lifecycle[t]=function(e){return n.push(e)}});var e;[].concat((a=this).config.plugins).forEach(function(e){return o(e)&&e(a._lifecycle,a)});var a;s(this,"init"),function(e){var t,n=e.config;t="history"===(n.routerMode||"hash")&&u?new Ae(n):new Ee(n),e.router=t,Te(e),$e=e.route,t.onchange(function(t){Te(e),e._updateRender(),$e.path!==e.route.path?(e.$fetch(),$e=e.route):e.$resetEvents()})}(this),_e(this),Pe(this),function(e){var t=e.config.loadSidebar;if(e.rendered){var n=oe(e.router,".sidebar-nav",!0,!0);t&&n&&(n.parentNode.innerHTML+=window.__SUB_SIDEBAR__),e._bindEventOnRendered(n),e.$resetEvents(),s(e,"doneEach"),s(e,"ready")}else e.$fetch(function(t){return s(e,"ready")})}(this),s(this,"mounted")};Ne.route={};(je=Ne)._renderTo=function(e,t,n){var r=p(e);r&&(r[n?"outerHTML":"innerHTML"]=t)},je._renderSidebar=function(e){var t=this.config,n=t.maxLevel,r=t.subMaxLevel,i=t.loadSidebar;this._renderTo(".sidebar-nav",this.compiler.sidebar(e,n));var a=oe(this.router,".sidebar-nav",!0,!0);i&&a?a.parentNode.innerHTML+=this.compiler.subSidebar(r)||"":this.compiler.subSidebar(),this._bindEventOnRendered(a)},je._bindEventOnRendered=function(e){var t=this.config,n=t.autoHeader,r=t.auto2top;if(function(e){var t=m(".cover.show");de=t?t.offsetHeight:0;for(var n=p(".sidebar"),r=v(n,"li"),i=0,a=r.length;i([^<]*?)

      $');if(i){if("color"===i[2])n.style.background=i[1]+(i[3]||"");else{var a=i[1];_(n,"add","has-mask"),Q(i[1])||(a=J(this.router.getBasePath(),i[1])),n.style.backgroundImage="url("+a+")",n.style.backgroundSize="cover",n.style.backgroundPosition="center center"}r=r.replace(i[0],"")}this._renderTo(".cover-main",r),ae()}else _(n,"remove","show")},je._updateRender=function(){!function(e){var t=p(".app-name-link"),n=e.config.nameLink,r=e.route.path;if(t)if(i(e.config.nameLink))t.setAttribute("href",n);else if("object"==typeof n){var a=Object.keys(n).filter(function(e){return r.indexOf(e)>-1})[0];t.setAttribute("href",n[a])}}(this)};var je;!function(e){var t,n=function(e,n,r){return t&&t.abort&&t.abort(),t=P(e,!0,r)};e._loadSideAndNav=function(e,t,n,r){var i=this;return function(){if(!n)return r();Fe(e,t,n,function(e){i._renderSidebar(e),r()},i,!0)}},e._fetch=function(e){var t=this;void 0===e&&(e=a);var r=this.route,i=r.path,o=X(r.query,["id"]),s=this.config,l=s.loadNavbar,c=s.requestHeaders,u=s.loadSidebar,h=this.router.getFile(i),p=n(h+o,0,c);this.isHTML=/\.html$/g.test(h),p.then(function(n,r){return t._renderMain(n,r,t._loadSideAndNav(i,o,u,e))},function(n){t._fetchFallbackPage(h,o,e)||t._fetch404(h,o,e)}),l&&Fe(i,o,l,function(e){return t._renderNav(e)},this,!0)},e._fetchCover=function(){var e=this,t=this.config,n=t.coverpage,r=t.requestHeaders,i=this.route.query,a=V(this.route.path);if(n){var o=null,s=this.route.path;if("string"==typeof n)"/"===s&&(o=n);else if(Array.isArray(n))o=n.indexOf(s)>-1&&"_coverpage";else{var l=n[s];o=!0===l?"_coverpage":l}var c=Boolean(o)&&this.config.onlyCover;return o?(o=this.router.getFile(a+o),this.coverIsHTML=/\.html$/g.test(o),P(o+X(i,["id"]),!1,r).then(function(t){return e._renderCover(t,c)})):this._renderCover(null,c),c}},e.$fetch=function(e){var t=this;void 0===e&&(e=a);var n=function(){s(t,"doneEach"),e()};this._fetchCover()?n():this._fetch(function(){t.$resetEvents(),n()})},e._fetchFallbackPage=function(e,t,r){var i=this;void 0===r&&(r=a);var o=this.config,s=o.requestHeaders,l=o.fallbackLanguages,c=o.loadSidebar;if(!l)return!1;var u=e.split("/")[1];if(-1===l.indexOf(u))return!1;var h=e.replace(new RegExp("^/"+u),"");return n(h+t,0,s).then(function(n,a){return i._renderMain(n,a,i._loadSideAndNav(e,t,c,r))},function(){return i._fetch404(e,t,r)}),!0},e._fetch404=function(e,t,r){var i=this;void 0===r&&(r=a);var o=this.config,s=o.loadSidebar,l=o.requestHeaders,c=o.notFoundPage,u=this._loadSideAndNav(e,t,s,r);if(c){var h=function(e,t){var n,r,i=t.notFoundPage,a="_404"+(t.ext||".md");switch(typeof i){case"boolean":r=a;break;case"string":r=i;break;case"object":r=(n=Object.keys(i).sort(function(e,t){return t.length-e.length}).find(function(t){return e.match(new RegExp("^"+t))}))&&i[n]||a}return r}(e,this.config);return n(this.router.getFile(h),0,l).then(function(e,t){return i._renderMain(e,t,u)},function(){return i._renderMain(null,{},u)}),!0}return this._renderMain(null,{},u),!1}}(Ne),Ne.$resetEvents=function(){me(this.route.path,this.route.query.id),this.config.loadNavbar&&oe(this.router,"nav")};window.Docsify={util:Oe,dom:S,get:P,slugify:U},window.DocsifyCompiler=re,window.marked=q,window.Prism=R,Me.version="4.6.10",function(e){var t=document.readyState;if("complete"===t||"interactive"===t)return setTimeout(e,0);document.addEventListener("DOMContentLoaded",e)}(function(e){return new Me})}(); diff --git a/api/static/search.min.js b/api/static/search.min.js new file mode 100644 index 0000000..e4d4064 --- /dev/null +++ b/api/static/search.min.js @@ -0,0 +1 @@ +!function(){var n,e={};function t(n){var e={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};return String(n).replace(/[&<>"'/]/g,function(n){return e[n]})}function o(t,o){n=Docsify;var a="auto"===t.paths,i=localStorage.getItem("docsify.search.expires")l.length&&(d=l.length);var h="..."+t(l).substring(i,d).replace(e,''+n+"")+"...";s+=h}}),r)){var h={title:t(c),content:s,url:d};o.push(h)}},s=0;s\n

      '+n.title+"

      \n

      "+n.content+"

      \n\n"}),i.classList.add("show"),r.classList.add("show"),i.innerHTML=s||'

      '+a+"

      "}function r(n,e){var t=e.router.parse().query.s;Docsify.dom.style("\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0 7px;\n line-height: 36px;\n font-size: 14px;\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n.search .clear-button {\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}"),function(n,e){void 0===e&&(e="");var t='
      \n \n
      \n \n \n \n \n \n
      \n
      \n
      \n ',o=Docsify.dom.create("div",t),a=Docsify.dom.find("aside");Docsify.dom.toggleClass(o,"search"),Docsify.dom.before(a,o)}(0,t),function(){var n,e=Docsify.dom.find("div.search"),t=Docsify.dom.find(e,"input"),o=Docsify.dom.find(e,".input-wrap");Docsify.dom.on(e,"click",function(n){return"A"!==n.target.tagName&&n.stopPropagation()}),Docsify.dom.on(t,"input",function(e){clearTimeout(n),n=setTimeout(function(n){return i(e.target.value.trim())},100)}),Docsify.dom.on(o,"click",function(n){"INPUT"!==n.target.tagName&&(t.value="",i())})}(),t&&setTimeout(function(n){return i(t)},500)}function s(n,e){!function(n,e){var t=Docsify.dom.getNode('.search input[type="search"]');if(t)if("string"==typeof n)t.placeholder=n;else{var o=Object.keys(n).filter(function(n){return e.indexOf(n)>-1})[0];t.placeholder=n[o]}}(n.placeholder,e.route.path),function(n,e){if("string"==typeof n)a=n;else{var t=Object.keys(n).filter(function(n){return e.indexOf(n)>-1})[0];a=n[t]}}(n.noData,e.route.path)}var c={placeholder:"Type to search",noData:"No Results!",paths:"auto",depth:2,maxAge:864e5};$docsify.plugins=[].concat(function(n,e){var t=Docsify.util,a=e.config.search||c;Array.isArray(a)?c.paths=a:"object"==typeof a&&(c.paths=Array.isArray(a.paths)?a.paths:"auto",c.maxAge=t.isPrimitive(a.maxAge)?a.maxAge:c.maxAge,c.placeholder=a.placeholder||c.placeholder,c.noData=a.noData||c.noData,c.depth=a.depth||c.depth);var i="auto"===c.paths;n.mounted(function(n){r(0,e),!i&&o(c,e)}),n.doneEach(function(n){s(c,e),i&&o(c,e)})},$docsify.plugins)}(); diff --git a/api/static/vue.css b/api/static/vue.css new file mode 100644 index 0000000..2ec9312 --- /dev/null +++ b/api/static/vue.css @@ -0,0 +1 @@ +@import url("https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600");*{-webkit-font-smoothing:antialiased;-webkit-overflow-scrolling:touch;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-text-size-adjust:none;-webkit-touch-callout:none;box-sizing:border-box}body:not(.ready){overflow:hidden}body:not(.ready) .app-nav,body:not(.ready)>nav,body:not(.ready) [data-cloak]{display:none}div#app{font-size:30px;font-weight:lighter;margin:40vh auto;text-align:center}div#app:empty:before{content:"Loading..."}.emoji{height:1.2rem;vertical-align:middle}.progress{background-color:var(--theme-color,#42b983);height:2px;left:0;position:fixed;right:0;top:0;transition:width .2s,opacity .4s;width:0;z-index:5}.search .search-keyword,.search a:hover{color:var(--theme-color,#42b983)}.search .search-keyword{font-style:normal;font-weight:700}body,html{height:100%}body{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:#34495e;font-family:Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:15px;letter-spacing:0;margin:0;overflow-x:hidden}img{max-width:100%}a[disabled]{cursor:not-allowed;opacity:.6}kbd{border:1px solid #ccc;border-radius:3px;display:inline-block;font-size:12px!important;line-height:12px;margin-bottom:3px;padding:3px 5px;vertical-align:middle}.task-list-item{list-style-type:none}li input[type=checkbox]{margin:0 .2em .25em -1.6em;vertical-align:middle}.app-nav{margin:25px 60px 0 0;position:absolute;right:0;text-align:right;z-index:2}.app-nav.no-badge{margin-right:25px}.app-nav p{margin:0}.app-nav>a{margin:0 1rem;padding:5px 0}.app-nav li,.app-nav ul{display:inline-block;list-style:none;margin:0}.app-nav a{color:inherit;font-size:16px;text-decoration:none;transition:color .3s}.app-nav a.active,.app-nav a:hover{color:var(--theme-color,#42b983)}.app-nav a.active{border-bottom:2px solid var(--theme-color,#42b983)}.app-nav li{display:inline-block;margin:0 1rem;padding:5px 0;position:relative}.app-nav li ul{background-color:#fff;border:1px solid #ddd;border-bottom-color:#ccc;border-radius:4px;box-sizing:border-box;display:none;max-height:calc(100vh - 61px);overflow-y:auto;padding:10px 0;position:absolute;right:-15px;text-align:left;top:100%;white-space:nowrap}.app-nav li ul li{display:block;font-size:14px;line-height:1rem;margin:0;margin:8px 14px;white-space:nowrap}.app-nav li ul a{display:block;font-size:inherit;margin:0;padding:0}.app-nav li ul a.active{border-bottom:0}.app-nav li:hover ul{display:block}.github-corner{border-bottom:0;position:fixed;right:0;text-decoration:none;top:0;z-index:1}.github-corner:hover .octo-arm{animation:a .56s ease-in-out}.github-corner svg{color:#fff;fill:var(--theme-color,#42b983);height:80px;width:80px}main{display:block;position:relative;width:100vw;height:100%;z-index:0}main.hidden{display:none}.anchor{display:inline-block;text-decoration:none;transition:all .3s}.anchor span{color:#34495e}.anchor:hover{text-decoration:underline}.sidebar{border-right:1px solid rgba(0,0,0,.07);overflow-y:auto;padding:40px 0 0;position:absolute;top:0;bottom:0;left:0;transition:transform .25s ease-out;width:300px;z-index:3}.sidebar>h1{margin:0 auto 1rem;font-size:1.5rem;font-weight:300;text-align:center}.sidebar>h1 a{color:inherit;text-decoration:none}.sidebar>h1 .app-nav{display:block;position:static}.sidebar .sidebar-nav{line-height:2em;padding-bottom:40px}.sidebar li.collapse .app-sub-sidebar{display:none}.sidebar ul{margin:0;padding:0}.sidebar li>p{font-weight:700;margin:0}.sidebar ul,.sidebar ul li{list-style:none}.sidebar ul li a{border-bottom:none;display:block}.sidebar ul li ul{padding-left:20px}.sidebar::-webkit-scrollbar{width:4px}.sidebar::-webkit-scrollbar-thumb{background:transparent;border-radius:4px}.sidebar:hover::-webkit-scrollbar-thumb{background:hsla(0,0%,53%,.4)}.sidebar:hover::-webkit-scrollbar-track{background:hsla(0,0%,53%,.1)}.sidebar-toggle{background-color:transparent;background-color:hsla(0,0%,100%,.8);border:0;outline:none;padding:10px;position:absolute;bottom:0;left:0;text-align:center;transition:opacity .3s;width:284px;z-index:4}.sidebar-toggle .sidebar-toggle-button:hover{opacity:.4}.sidebar-toggle span{background-color:var(--theme-color,#42b983);display:block;margin-bottom:4px;width:16px;height:2px}body.sticky .sidebar,body.sticky .sidebar-toggle{position:fixed}.content{padding-top:60px;position:absolute;top:0;right:0;bottom:0;left:300px;transition:left .25s ease}.markdown-section{margin:0 auto;max-width:800px;padding:30px 15px 40px;position:relative}.markdown-section>*{box-sizing:border-box;font-size:inherit}.markdown-section>:first-child{margin-top:0!important}.markdown-section hr{border:none;border-bottom:1px solid #eee;margin:2em 0}.markdown-section iframe{border:1px solid #eee}.markdown-section table{border-collapse:collapse;border-spacing:0;display:block;margin-bottom:1rem;overflow:auto;width:100%}.markdown-section th{font-weight:700}.markdown-section td,.markdown-section th{border:1px solid #ddd;padding:6px 13px}.markdown-section tr{border-top:1px solid #ccc}.markdown-section p.tip,.markdown-section tr:nth-child(2n){background-color:#f8f8f8}.markdown-section p.tip{border-bottom-right-radius:2px;border-left:4px solid #f66;border-top-right-radius:2px;margin:2em 0;padding:12px 24px 12px 30px;position:relative}.markdown-section p.tip:before{background-color:#f66;border-radius:100%;color:#fff;content:"!";font-family:Dosis,Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:14px;font-weight:700;left:-12px;line-height:20px;position:absolute;height:20px;width:20px;text-align:center;top:14px}.markdown-section p.tip code{background-color:#efefef}.markdown-section p.tip em{color:#34495e}.markdown-section p.warn{background:rgba(66,185,131,.1);border-radius:2px;padding:1rem}body.close .sidebar{transform:translateX(-300px)}body.close .sidebar-toggle{width:auto}body.close .content{left:0}@media print{.app-nav,.github-corner,.sidebar,.sidebar-toggle{display:none}}@media screen and (max-width:768px){.github-corner,.sidebar,.sidebar-toggle{position:fixed}.app-nav{margin-top:16px}.app-nav li ul{top:30px}main{height:auto;overflow-x:hidden}.sidebar{left:-300px;transition:transform .25s ease-out}.content{left:0;max-width:100vw;position:static;padding-top:20px;transition:transform .25s ease}.app-nav,.github-corner{transition:transform .25s ease-out}.sidebar-toggle{background-color:transparent;width:auto;padding:30px 30px 10px 10px}body.close .sidebar{transform:translateX(300px)}body.close .sidebar-toggle{background-color:hsla(0,0%,100%,.8);transition:background-color 1s;width:284px;padding:10px}body.close .content{transform:translateX(300px)}body.close .app-nav,body.close .github-corner{display:none}.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:a .56s ease-in-out}}@keyframes a{0%,to{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}section.cover{-ms-flex-align:center;align-items:center;background-position:50%;background-repeat:no-repeat;background-size:cover;height:100vh;display:none}section.cover.show{display:-ms-flexbox;display:flex}section.cover.has-mask .mask{background-color:#fff;opacity:.8;position:absolute;top:0;height:100%;width:100%}section.cover .cover-main{-ms-flex:1;flex:1;margin:-20px 16px 0;text-align:center;z-index:1}section.cover a{color:inherit}section.cover a,section.cover a:hover{text-decoration:none}section.cover p{line-height:1.5rem;margin:1em 0}section.cover h1{color:inherit;font-size:2.5rem;font-weight:300;margin:.625rem 0 2.5rem;position:relative;text-align:center}section.cover h1 a{display:block}section.cover h1 small{bottom:-.4375rem;font-size:1rem;position:absolute}section.cover blockquote{font-size:1.5rem;text-align:center}section.cover ul{line-height:1.8;list-style-type:none;margin:1em auto;max-width:500px;padding:0}section.cover .cover-main>p:last-child a{border:1px solid var(--theme-color,#42b983);border-radius:2rem;box-sizing:border-box;color:var(--theme-color,#42b983);display:inline-block;font-size:1.05rem;letter-spacing:.1rem;margin:.5rem 1rem;padding:.75em 2rem;text-decoration:none;transition:all .15s ease}section.cover .cover-main>p:last-child a:last-child{background-color:var(--theme-color,#42b983);color:#fff}section.cover .cover-main>p:last-child a:last-child:hover{color:inherit;opacity:.8}section.cover .cover-main>p:last-child a:hover{color:inherit}section.cover blockquote>p>a{border-bottom:2px solid var(--theme-color,#42b983);transition:color .3s}section.cover blockquote>p>a:hover{color:var(--theme-color,#42b983)}.sidebar,body{background-color:#fff}.sidebar{color:#364149}.sidebar li{margin:6px 0 6px 15px}.sidebar ul li a{color:#505d6b;font-size:14px;font-weight:400;overflow:hidden;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.sidebar ul li a:hover{text-decoration:underline}.sidebar ul li ul{padding:0}.sidebar ul li.active>a{border-right:2px solid;color:var(--theme-color,#42b983);font-weight:600}.app-sub-sidebar li:before{content:"-";padding-right:4px;float:left}.markdown-section h1,.markdown-section h2,.markdown-section h3,.markdown-section h4,.markdown-section strong{color:#2c3e50;font-weight:600}.markdown-section a{color:var(--theme-color,#42b983);font-weight:600}.markdown-section h1{font-size:2rem;margin:0 0 1rem}.markdown-section h2{font-size:1.75rem;margin:45px 0 .8rem}.markdown-section h3{font-size:1.5rem;margin:40px 0 .6rem}.markdown-section h4{font-size:1.25rem}.markdown-section h5{font-size:1rem}.markdown-section h6{color:#777;font-size:1rem}.markdown-section figure,.markdown-section p{margin:1.2em 0}.markdown-section ol,.markdown-section p,.markdown-section ul{line-height:1.6rem;word-spacing:.05rem}.markdown-section ol,.markdown-section ul{padding-left:1.5rem}.markdown-section blockquote{border-left:4px solid var(--theme-color,#42b983);color:#858585;margin:2em 0;padding-left:20px}.markdown-section blockquote p{font-weight:600;margin-left:0}.markdown-section iframe{margin:1em 0}.markdown-section em{color:#7f8c8d}.markdown-section code{border-radius:2px;color:#e96900;font-size:.8rem;margin:0 2px;padding:3px 5px;white-space:pre-wrap}.markdown-section code,.markdown-section pre{background-color:#f8f8f8;font-family:Roboto Mono,Monaco,courier,monospace}.markdown-section pre{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;line-height:1.5rem;margin:1.2em 0;overflow:auto;padding:0 1.4rem;position:relative;word-wrap:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#8e908c}.token.namespace{opacity:.7}.token.boolean,.token.number{color:#c76b29}.token.punctuation{color:#525252}.token.property{color:#c08b30}.token.tag{color:#2973b7}.token.string{color:var(--theme-color,#42b983)}.token.selector{color:#6679cc}.token.attr-name{color:#2973b7}.language-css .token.string,.style .token.string,.token.entity,.token.url{color:#22a2c9}.token.attr-value,.token.control,.token.directive,.token.unit{color:var(--theme-color,#42b983)}.token.keyword{color:#e96900}.token.atrule,.token.regex,.token.statement{color:#22a2c9}.token.placeholder,.token.variable{color:#3d8fd1}.token.deleted{text-decoration:line-through}.token.inserted{border-bottom:1px dotted #202746;text-decoration:none}.token.italic{font-style:italic}.token.bold,.token.important{font-weight:700}.token.important{color:#c94922}.token.entity{cursor:help}.markdown-section pre>code{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;background-color:#f8f8f8;border-radius:2px;color:#525252;display:block;font-family:Roboto Mono,Monaco,courier,monospace;font-size:.8rem;line-height:inherit;margin:0 2px;max-width:inherit;overflow:inherit;padding:2.2em 5px;white-space:inherit}.markdown-section code:after,.markdown-section code:before{letter-spacing:.05rem}code .token{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;min-height:1.5rem}pre:after{color:#ccc;content:attr(data-lang);font-size:.6rem;font-weight:600;height:15px;line-height:15px;padding:5px 10px 0;position:absolute;right:0;text-align:right;top:0} \ No newline at end of file diff --git a/api/widgets-based-automation.md b/api/widgetsBasedAutomation.md similarity index 100% rename from api/widgets-based-automation.md rename to api/widgetsBasedAutomation.md diff --git a/docs/work-with-java.html b/docs/_404.html similarity index 58% rename from docs/work-with-java.html rename to docs/_404.html index 60a4d13..9638849 100644 --- a/docs/work-with-java.html +++ b/docs/_404.html @@ -2,13 +2,13 @@ - Index | Auto.js 3.0.0 文档 - + 页面找不到了o(╥﹏╥)o | Auto.js Pro-8.2.0 文档 + - + - +
      -
      +
      -

      Auto.js 3.0.0 文档

      +

      Auto.js Pro-8.2.0 文档

      索引 | @@ -67,11 +75,15 @@

      Auto.js 3.0.0 文档

      - +

      页面找不到了o(╥﹏╥)o#

      +
      diff --git a/docs/_coverpage.html b/docs/_coverpage.html new file mode 100644 index 0000000..c4bf16b --- /dev/null +++ b/docs/_coverpage.html @@ -0,0 +1,102 @@ + + + + + Auto.js | Auto.js Pro-8.2.0 文档 + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/_sidebar.html b/docs/_sidebar.html new file mode 100644 index 0000000..7e60cfa --- /dev/null +++ b/docs/_sidebar.html @@ -0,0 +1,121 @@ + + + + + Index | Auto.js Pro-8.2.0 文档 + + + + + + +
      + + + +
      + + + + + + \ No newline at end of file diff --git a/docs/all.html b/docs/all.html index 7bcfca7..04920cd 100644 --- a/docs/all.html +++ b/docs/all.html @@ -2,7 +2,7 @@ - 综述 | Auto.js Pro-8.1.0 文档 + 综述 | Auto.js Pro-8.2.0 文档 @@ -27,8 +27,9 @@
    • Overview - 综述
    • Q&A - 常见问题
    • App - 应用
    • +
    • Canvas - 画布
    • Console - 控制台
    • -
    • CoordinatesBasedAutomation - 基于坐标的操作
    • +
    • CoordinatesBasedAutomation - 基于坐标的操作
    • Crypto - 加解密与消息摘要
    • Device - 设备
    • Dialogs - 对话框
    • @@ -42,13 +43,13 @@
    • Keys - 按键模拟
    • Media - 多媒体
    • Modules - 模块
    • -
    • WidgetsBasedAutomation - 基于控件的操作
    • Sensors - 传感器
    • Shell - Shell命令
    • Storages - 本地存储
    • Threads - 多线程
    • Timers - 定时器
    • UI - 用户界面
    • +
    • WidgetsBasedAutomation - 基于控件的操作
    • Work with Java - 调用Java API
    @@ -62,7 +63,7 @@
    -

    Auto.js Pro-8.1.0 文档

    +

    Auto.js Pro-8.2.0 文档

    索引 | @@ -115,6 +116,47 @@

    目录

  • app.getInstalledApps([options])
  • +
  • Canvas +
  • +
  • 画笔
  • +
  • 变换矩阵
  • +
  • 路径
  • +
  • Porter-Duff操作
  • +
  • 着色器
  • +
  • 遮罩过滤器
  • +
  • 颜色过滤器
  • +
  • 路径特效
  • +
  • 区域
  • Console
  • -
  • 基于坐标的触摸模拟
  • @@ -985,6 +1027,7 @@

    综述#

  • 因为Auto.js是基于JavaScript的,学习Auto.js的API之前建议先学习JavaScript的基本语法和内置对象,可以使用教程前面的两个JavaScript教程链接来学习。
  • 如果您想要使用TypeScript来开发,目前已经有开发者公布了一个可以把使用TypeScript进行Auto.js开发的工具,参见Auto.js DevTools
  • 如果想要在电脑而不是手机上开发Auto.js,可以使用VS Code以及相应的Auto.js插件使得在电脑上编辑的脚本能推送到手机运行,参见Auto.js-VSCode-Extension,Auto.js Pro用户则需要使用Auto.js-Pro-Ext
  • +
  • 支持Node.js已在计划中,预计2020年底在Auto.js Pro中支持
  • 本文档的章节大致上是以模块来分的,总体上可以分成"自动操作"类模块(控件操作、触摸模拟、按键模拟等)和其他类模块(设备、应用、界面等)。

    "自动操作"的部分又可以大致分为基于控件和基于坐标的操作。基于坐标的操作是传统按键精灵、触摸精灵等脚本软件采用的方式,通过屏幕坐标来点击、长按指定位置模拟操作,从而到达目的。例如click(100, 200), press(100, 200, 500)等。这种方式在游戏类脚本中比较有可行性,结合找图找色、坐标放缩功能也能达到较好的兼容性。但是,这种方式对一般软件脚本却难以达到想要的效果,而且这种方式需要安卓7.0版本以上或者root权限才能执行。所以对于一般软件脚本(例如批量添加联系人、自动提取短信验证码等等),我们采用基于控件的模拟操作方式,结合通知事情、按键事情等达成更好的工作流。这些部分的文档参见基于控件的操作基于坐标的操作

    @@ -1004,8 +1047,8 @@

    综述#

  • threads: 多线程支持。
  • ui: UI界面。用于显示自定义的UI界面,和用户交互。
  • -

    除此之外,Auto.js内置了对Promise。 -

    +

    除此之外,Auto.js内置了对Promise

    +

    Q & A#

    如何定时运行脚本#

    @@ -1359,6 +1402,154 @@

    app.getInstalledApps([options])#

    +

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

    +

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

    +
    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.getWidth()#

    +
    +

    返回画布当前图层的宽度。

    +

    canvas.getHeight()#

    +
    +

    返回画布当前图层的高度。

    +

    canvas.drawRGB(r, int g, int b)#

    +
    +

    将整个可绘制区域填充为r、g、b指定的颜色。相当于 canvas.drawColor(colors.rgb(r, g, b))

    +

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

    +
    +

    将整个可绘制区域填充为a、r、g、b指定的颜色。相当于 canvas.drawColor(colors.argb(a, r, g, b))

    +

    canvas.drawColor(color)#

    +
    +

    将整个可绘制区域填充为color指定的颜色。

    +

    canvas.drawColor(color, mode)#

    +
      +
    • color <number> 颜色值
    • +
    • mode <PorterDuff.Mode> Porter-Duff操作
    • +
    +

    将整个可绘制区域填充为color指定的颜色。

    +

    canvas.drawPaint(paint)#

    +
      +
    • paint <Paint> 画笔
    • +
    +

    将整个可绘制区域用paint指定的画笔填充。相当于绘制一个无限大的矩形,但是更快。 +通过该方法可以绘制一个指定的着色器的图案。

    +

    canvas.drawPoint(x, y, paint)#

    +
    +

    在可绘制区域绘制由坐标(x, y)指定的点。 +点的形状由画笔的线帽决定(参见paint.setStrokeCap(cap))。 +点的大小由画笔的宽度决定(参见paint.setStrokeWidth(width))。

    +
    +

    如果画笔宽度为0,则也会绘制1个像素至4个(若抗锯齿启用)。

    +
    +

    相当于 canvas.drawPoints([x, y], paint)

    +

    canvas.drawPoints(pts, paint)#

    +
      +
    • pts <Array> 点坐标数组 [x0, y0, x1, y1, x2, y2, ...]
    • +
    • paint <Paint> 画笔
    • +
    +

    在可绘制区域绘制由坐标数组指定的多个点。

    +

    canvas.drawLine(startX, startY, stopX, stopY, paint)#

    +
    +

    在可绘制区域绘制由起点坐标(startX, startY)和终点坐标(endX, endY)指定的线。 +绘制时会忽略画笔的样式(Style)。也就是说,即使样式设为“仅填充(FILL)”也会绘制。 +退化为点的线(长度为0)不会被绘制。

    +

    canvas.drawRect(r, paint)#

    +
      +
    • r <Rect> 矩形边界
    • +
    • paint <Paint> 画笔
    • +
    +

    在可绘制区域绘制由矩形边界r指定的矩形。 +绘制时画笔的样式(Style)决定了是否绘制矩形界线和填充矩形。

    +

    canvas.drawRect(left, top, right, bottom, android.graphics.Paint paint)#

    +
      +
    • left <number> 矩形左边界x坐标
    • +
    • top <number> 矩形上边界y坐标
    • +
    • right <number> 矩形右边界x坐标
    • +
    • bottom <number> 矩形下边界y坐标
    • +
    • paint <Paint> 画笔
    • +
    +

    在可绘制区域绘制由矩形边界(left, top, right, bottom)指定的矩形。 +绘制时画笔的样式(Style)决定了是否绘制矩形界线和填充矩形。

    +

    canvas.drawOval(android.graphics.RectF oval, android.graphics.Paint paint)#

    +

    canvas.drawOval(float left, float top, float right, float bottom, android.graphics.Paint paint)#

    +

    canvas.drawCircle(float cx, float cy, float radius, android.graphics.Paint paint)#

    +

    canvas.drawArc(android.graphics.RectF oval, float startAngle, float sweepAngle, boolean useCenter, android.graphics.Paint paint)#

    +

    canvas.drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, android.graphics.Paint paint)#

    +

    canvas.drawRoundRect(android.graphics.RectF rect, float rx, float ry, android.graphics.Paint paint)#

    +

    canvas.drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, android.graphics.Paint paint)#

    +

    canvas.drawPath(android.graphics.Path path, android.graphics.Paint paint)#

    +

    canvas.drawBitmap(android.graphics.Bitmap bitmap, float left, float top, android.graphics.Paint paint)#

    +

    canvas.drawText(java.lang.String text, float x, float y, android.graphics.Paint paint)#

    +

    canvas.drawTextOnPath(java.lang.String text, android.graphics.Path path, float hOffset, float vOffset, android.graphics.Paint paint)#

    +

    canvas.translate(dx, dy)#

    +
      +
    • dx <number> 向x轴正方向平移的距离,负数表示反方向平移
    • +
    • dy <number> 向y轴正方向平移的距离,负数表示反方向平移
    • +
    +

    平移指定距离。

    +

    canvas.scale(sx, sy)#

    +
      +
    • sx <number> 向x轴正方向平移的距离,负数表示反方向平移
    • +
    • sy <number> 向y轴正方向平移的距离,负数表示反方向平移
    • +
    +

    以原点为中心,将坐标系平移缩放指定倍数。

    +

    canvas.scale(float sx, float sy, float px, float py)#

    +

    canvas.rotate(float degrees)#

    +

    canvas.rotate(float degrees, float px, float py)#

    +

    canvas.skew(float sx, float sy)#

    +

    画笔#

    +

    变换矩阵#

    +

    路径#

    +

    Porter-Duff操作#

    +

    着色器#

    +

    遮罩过滤器#

    +

    颜色过滤器#

    +

    路径特效#

    +

    区域#

    +

    Console#

    Stability: 2 - Stable

    控制台模块提供了一个和Web浏览器中相似的用于调试的控制台。用于输出一些调试信息、中间结果等。 @@ -1514,8 +1705,8 @@

    print(text)#

    + +

    基于坐标的触摸模拟#

    Stability: 2 - Stable

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

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

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

    @@ -1525,7 +1716,7 @@

    基于坐标的触摸模拟#

    +

    setScreenMetrics(width, height)#

    • width <number> 屏幕宽度,单位像素
    • height <number> 屏幕高度,单位像素
    • @@ -1536,9 +1727,9 @@

      基于坐标的触摸模拟#

      +

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

      Stability: 2 - Stable

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

      -

      click(x, y)#

      +

      click(x, y)#

      • x <number> 要点击的坐标的x值
      • y <number> 要点击的坐标的y值
      • @@ -1546,14 +1737,14 @@

        click(x, y)#

        +

        longClick(x, y)#

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

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

        -

        press(x, y, duration)#

        +

        press(x, y, duration)#

        • x <number> 要按住的坐标的x值
        • y <number> 要按住的坐标的y值
        • @@ -1568,7 +1759,7 @@

          press(x, y, duration)#

          +

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

          • x1 <number> 滑动的起始坐标的x值
          • y1 <number> 滑动的起始坐标的y值
          • @@ -1578,18 +1769,18 @@

            press(x, y, duration)#

            +

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

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

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

            -

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

            +

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

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

            例如手指捏合:

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

            RootAutomator#

            +

            RootAutomator#

            Stability: 2 - Stable

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

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

            var ra = new RootAutomator();
            @@ -1600,7 +1791,7 @@ 

            gestures([delay1, duration1, [x1, y1], [x2, y2], ...], [delay2, duration2, [ ...

            注意以下命令需要root权限

            -

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

            +

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

            • x <number> 横坐标
            • y <number> 纵坐标
            • @@ -1616,7 +1807,7 @@

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

              +

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

              • x1 <number> 滑动起点横坐标
              • y1 <number> 滑动起点纵坐标
              • @@ -1626,7 +1817,7 @@

                RootAutomator.swipe(x1, x2, y1, y2[, duration, id])id <number> 多点触摸id,可选,默认为1

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

              -

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

              +

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

              • x <number> 横坐标
              • y <number> 纵坐标
              • @@ -1634,7 +1825,7 @@

                RootAutomator.press(x, y, duration[, id])<number> 多点触摸id,可选,默认为1

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

              -

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

              +

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

              • x <number> 横坐标
              • y <number> 纵坐标
              • @@ -1643,26 +1834,26 @@

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

                +

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

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

                -

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

                +

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

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

                -

                RootAutomator.touchUp([id])#

                +

                RootAutomator.touchUp([id])#

                • id <number> 多点触摸id,可选,默认为1

                模拟手指弹起。

                -

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

                +

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

                Stability: 1 - Experimental

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

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

                  @@ -1683,19 +1874,19 @@

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

                  +

                  Tap(x, y)#

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

                  -

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

                  +

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

                  • x1, y1 <number> 滑动起点的坐标
                  • x2, y2 <number> 滑动终点的坐标
                  • duration <number> 滑动动作所用的时间

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

                  +

                  Crypto#

                  [Pro 8.0.0新增]

                  @@ -4870,2601 +5061,2601 @@

                  module (模块)#

                  -

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

                  -

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

                  -

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

                  -

                  auto([mode])#

                  -
                  -

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

                  + +

                  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)。甚至在某些设备上的传感器参数有所增加,例如华为手机的距离传感器为三个参数,一般手机只有一个参数。

                  +

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

                    -
                  • fast 快速模式。该模式下会启用控件缓存,从而选择器获取屏幕控件更快。对于需要快速的控件操作的脚本可以使用该模式,一般脚本则没有必要使用该函数。
                  • -
                  • normal 正常模式,默认。
                  • +
                  • 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轴垂直于设备屏幕表面):

                      +

                      !axis_device

                      +
                    -

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

                    -

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

                    -

                    示例:

                    -
                    auto("fast");
                    -

                    示例2:

                    -
                    auto();
                    -

                    auto.waitFor()#

                    -

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

                    -

                    因为该函数是阻塞的,因此除非是有协程特性,否则不能在ui模式下运行该函数,建议在ui模式下使用auto()函数。

                    -

                    auto.setMode(mode)#

                    -
                      -
                    • mode <string> 模式
                    • + +
                    • orientation 方向传感器,参数(event, azimuth, pitch, roll):

                      +
                        +
                      • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
                      • +
                      • azimuth <number> 方位角,从地磁指北方向线起,依顺时针方向到y轴之间的水平夹角,单位角度,范围0~359
                      • +
                      • pitch <number> 绕x轴旋转的角度,当设备水平放置时该值为0,当设备顶部翘起时该值为正数,当设备尾部翘起时该值为负数,单位角度,范围-180~180
                      • +
                      • roll <number> 绕y轴顺时针旋转的角度,单位角度,范围-90~90
                      -

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

                    +
                  • +
                  • gyroscope 陀螺仪传感器,参数(event, wx, wy, wz):

                      -
                    • fast 快速模式。该模式下会启用控件缓存,从而选择器获取屏幕控件更快。对于需要快速的控件查看和操作的脚本可以使用该模式,一般脚本则没有必要使用该函数。
                    • -
                    • normal 正常模式,默认。
                    • +
                    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
                    • +
                    • wx <number> 绕x轴的角速度,单位弧度/s
                    • +
                    • wy <number> 绕y轴的角速度,单位弧度/s
                    • +
                    • wz <number> 绕z轴的角速度,单位弧度/s
                    -

                    auto.setFlags(flags)#

                    -

                    [v4.1.0新增]

                    +
                  • +
                  • magnetic_field 磁场传感器,参数(event, bx, by, bz):

                      -
                    • flags <string> | <Array> 一些标志,来启用和禁用某些特性,包括:
                        -
                      • findOnUiThread 使用该特性后,选择器搜索时会在主进程进行。该特性用于解决线程安全问题导致的次生问题,不过目前貌似已知问题并不是线程安全问题。
                      • -
                      • useUsageStats 使用该特性后,将会以"使用情况统计"服务的结果来检测当前正在运行的应用包名(需要授予"查看使用情况统计"权限)。如果觉得currentPackage()返回的结果不太准确,可以尝试该特性。
                      • -
                      • useShell 使用该特性后,将使用shell命令获取当前正在运行的应用的包名、活动名称,但是需要root权限。
                      • +
                      • 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
                      -

                      启用有关automator的一些特性。例如:

                      -
                      auto.setFlags(["findOnUiThread", "useShell"]);
                      -

                      auto.serivce#

                      -

                      [v4.1.0新增]

                      +
                    • +
                    • linear_acceleration 线性加速度传感器,参数(event, ax, ay, az):

                      -

                      获取无障碍服务。如果无障碍服务没有启动,则返回null

                      -

                      参见AccessibilityService

                      -

                      auto.windows#

                      -

                      [v4.1.0新增]

                      +
                    • +
                    • ambient_temperature 环境温度传感器,大部分设备并不支持,参数(event, t):

                        -
                      • <Array>
                      • +
                      • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
                      • +
                      • t <number> 环境温度,单位摄氏度。
                      -

                      当前所有窗口(AccessibilityWindowInfo)的数组,可能包括状态栏、输入法、当前应用窗口,弹出窗口、悬浮窗、分屏应用窗口等。可以分别获取每个窗口的布局信息。

                      -

                      该函数需要Android 5.0以上才能运行。

                      -

                      auto.root#

                      -

                      [v4.1.0新增]

                      +
                    • +
                    • light 光线传感器,参数(event, light):

                        -
                      • <UiObject>
                      • +
                      • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
                      • +
                      • light <number> 环境光强度,单位lux
                      -

                      当前窗口的布局根元素。如果无障碍服务未启动或者WindowFilter均返回false,则会返回null

                      -

                      如果不设置windowFilter,则当前窗口即为活跃的窗口(获取到焦点、正在触摸的窗口);如果设置了windowFilter,则获取的是过滤的窗口中的第一个窗口。

                      -

                      如果系统是Android5.0以下,则始终返回当前活跃的窗口的布局根元素。

                      -

                      auto.rootInActiveWindow#

                      -

                      [v4.1.0新增]

                      +
                    • +
                    • pressure 压力传感器,参数(event, p):

                        -
                      • <UiObject>
                      • +
                      • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
                      • +
                      • p <number> 大气压,单位hPa
                      -

                      当前活跃的窗口(获取到焦点、正在触摸的窗口)的布局根元素。如果无障碍服务未启动则为null

                      -

                      auto.setWindowFilter(filter)#

                      -

                      [v4.1.0新增]

                      +
                    • +
                    • proximity 距离传感器,参数(event, distance):

                        -
                      • filter <Function> 参数为窗口(AccessibilityWindowInfo),返回值为Boolean的函数。
                      • +
                      • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
                      • +
                      • distance <number> 一般指设备前置摄像头旁边的距离传感器到前方障碍物的距离,并且很多设备上这个值只有两种情况:当障碍物较近时该值为0,当障碍物较远或在范围内没有障碍物时该值为5
                      -

                      设置窗口过滤器。这个过滤器可以决定哪些窗口是目标窗口,并影响选择器的搜索。例如,如果想要选择器在所有窗口(包括状态栏、输入法等)中搜索,只需要使用以下代码:

                      -
                      auto.setWindowFilter(function(window){
                      -    //不管是如何窗口,都返回true,表示在该窗口中搜索
                      -    return true;
                      -});
                      -

                      又例如,当前使用了分屏功能,屏幕上有Auto.js和QQ两个应用,但我们只想选择器对QQ界面进行搜索,则:

                      -
                      auto.setWindowFilter(function(window){
                      -    // 对于应用窗口,他的title属性就是应用的名称,因此可以通过title属性来判断一个应用
                      -    return window.title == "QQ";
                      -});
                      -

                      选择器默认是在当前活跃的窗口中搜索,不会搜索诸如悬浮窗、状态栏之类的,使用WindowFilter则可以控制搜索的窗口。

                      -

                      需要注意的是, 如果WindowFilter返回的结果均为false,则选择器的搜索结果将为空。

                      -

                      另外setWindowFilter函数也会影响auto.windowRoots的结果。

                      -

                      该函数需要Android 5.0以上才有效。

                      -

                      auto.windowRoots#

                      -

                      [v4.1.0新增]

                      +
                    • +
                    • relative_humidity 湿度传感器,大部分设备并不支持,参数(event, rh):

                        -
                      • <Array>
                      • +
                      • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
                      • +
                      • rh <number> 相对湿度,范围为0~100(百分比)
                      -

                      返回当前被WindowFilter过滤的窗口的布局根元素组成的数组。

                      -

                      如果系统是Android5.0以下,则始终返回当前活跃的窗口的布局根元素的数组。

                      -

                      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)#

                      +

                      sensors.register(sensorName[, delay])#

                        -
                      • left <number> 要点击的长方形区域左边与屏幕左边的像素距离
                      • -
                      • top <number> 要点击的长方形区域上边与屏幕上边的像素距离
                      • -
                      • bottom <number> 要点击的长方形区域下边与屏幕下边的像素距离
                      • -
                      • right <number> 要点击的长方形区域右边与屏幕右边的像素距离
                      • +
                      • sensorName <string> 传感器名称,常用的传感器名称如上面所述
                      • +
                      • delay <number> 传感器数据更新频率,可选,默认为sensors.delay.normal。可用的值如下:
                          +
                        • sensors.delay.normal 正常频率
                        • +
                        • sensors.delay.ui 适合于用户界面的更新频率
                        • +
                        • sensors.delay.game 适合于游戏的更新频率
                        • +
                        • sensors.delay.fastest 最快的更新频率】
                        -

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

                      -

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

                      -

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

                      -

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

                      -

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

                      -

                      longClick(text[, i]))#

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

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

                      -

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

                      -

                      scrollUp([i])#

                      +

              注册一个传感器监听并返回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)#

              -

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

              -

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

              -

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

              -

              scrollDown([i])#

              +

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

            +
            //注册一个传感器监听器
            +var sensor = sensors.register("gravity");
            +if(sensor == null){
            +    exit();
            +}
            +//2秒后注销该监听器
            +setTimeout(()=> {
            +    sensors.unregister(sensor);
            +}, 2000);
            +

            sensors.unregisterAll()#

            +

            注销所有传感器监听器。

            +

            sensors.ignoresUnsupportedSensor#

            -

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

            -

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

            -

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

            -

            setText([i, ]text)#

            +

          表示是否忽略不支持的传感器。如果该值被设置为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'#

            -
          • i <number> 表示要输入的为第i + 1个输入框
          • -
          • text <string> 要输入的文本
          • +
          • sensorName <string> 不支持的传感器名称
          -

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

          -

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

          -

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

          -

          input([i, ]text)#

          -
            -
          • i <number> 表示要输入的为第i + 1个输入框
          • -
          • text <string> 要输入的文本
          • +

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

          +

          SensorEventEmitter#

          +

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

          +

          事件: 'change'#

          +
            +
          • ..args <Any> 传感器参数
          -

          返回是否输入成功。当找不到对应的文本框时返回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联系人列表,微信联系人列表等
          • +

        当传感器数据改变时触发该事件;该事件触发的最高频繁由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 高精度
          -

          尽管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(),通过链式调用来组合条件。

          -

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

          +

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

        + + +

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

        +

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

        +

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

          -
        • click() 点击。点击一个控件,前提是这个控件的clickable属性为true
        • -
        • longClick() 长按。长按一个控件,前提是这个控件的longClickable属性为true
        • -
        • setText() 设置文本,用于编辑框控件设置文本。
        • -
        • scrollForward(), scrollBackward() 滑动。滑动一个控件(列表等), 前提是这个控件的scrollable属性为true
        • -
        • exits() 判断控件是否存在
        • -
        • waitFor() 等待控件出现
        • +
        • 通过java.lang.Runtime.exec执行(shell, Tap, Home等函数)
        • +
        • 通过内嵌终端模拟器执行(RootAutomator, Shell等对象)
        -

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

        -
        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()#

        +

        shell函数#

        +
        Stability: 2 - Stable

        shell(cmd[, root])#

          -
        • 返回 <UiSelector>
        • +
        • cmd <string> 要执行的命令
        • +
        • root <Boolean> 是否以root权限运行,默认为false。
        -

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

        -

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

        -

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

        -

        UiSelector.algorithm(algorithm)#

        -

        [v4.1.0新增]

        +

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

        -
      • algorithm <string> 搜索算法,可选的值有:
          -
        • DFS 深度优先算法,选择器的默认算法
        • -
        • BFS 广度优先算法
        • -
        -
      • -
      -

      指定选择器的搜索算法。例如:

      -
      log(selector().text("文本").algorithm("BFS").find());
      -

      广度优先在控件所在层次较低时,或者布局的层次不多时,通常能更快找到控件。

      -

      UiSelector.text(str)#

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

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

      -

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

      -

      UiSelector.textContains(str)#

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

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

      -

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

      -

      UiSelector.textStartsWith(prefix)#

      +

      示例(强制停止微信) :

      +
      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)#

        -
      • prefix <string> 前缀
      • +
      • root <Boolean> 是否以root权限运行一个shell进程,默认为false。这将会影响其后使用该Shell对象执行的命令的权限
      -

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

      -

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

      -

      UiSelector.textEndsWith(suffix)#

      +

    Shell对象的"构造函数"。

    +
    var sh = new Shell(true);
    +//强制停止微信
    +sh.exec("am force-stop com.tencent.mm");
    +sh.exit();
    +

    Shell.exec(cmd)#

    -

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

    -

    UiSelector.textMatches(reg)#

    +

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

    +

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

    +

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

    +

    Shell.exit()#

    +

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

    +

    Shell.exitAndWaitFor()#

    +

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

    +

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

    +

    Shell.setCallback(callback)#

      -
    • reg <string> | <Regex> 要满足的正则表达式。
    • +
    • callback <Object> 回调函数
    -

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

    -

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

    -

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

    -

    UiSelector.desc(str)#

    -
      -
    • str <string> 控件文本
    • -
    • 返回 <UiSelector> 返回选择器自身以便链式调用
    • +

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

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

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

    -

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

    -

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

    -

    UiSelector.descContains(str)#

    -
      -
    • str <string> 要包含的字符串
    • +

      例如:

      +
      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:指定要作为哪个用户运行;如果未指定,则作为当前用户运行。
      -

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

    -

    UiSelector.descStartsWith(prefix)#

    -
      -
    • prefix <string> 前缀
    • +

      startservice [options] intent#

      +

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

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

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

    -

    UiSelector.descEndsWith(suffix)#

    -
      -
    • suffix <string> 后缀
    • +

      force-stop package#

      +

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

      +

      kill [options] package#

      +

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

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

    为当前选择器附加控件"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)#

    -
    -

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

    -

    UiSelector.idEndsWith(suffix)#

    -
      -
    • suffix <string> id后缀
    • +

      kill-all#

      +

      终止所有后台进程。

      +

      broadcast [options] intent#

      +

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

      +

      选项包括:

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

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

    -

    UiSelector.idMatches(reg)#

    -
      -
    • reg <Regex> | <string> id要满足的正则表达式
    • +

      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 的分析器。
      -

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

    -

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

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

    UiSelector.className(str)#

    -
      -
    • str <string> 控件文本
    • -
    • 返回 <UiSelector> 返回选择器自身以便链式调用
    • +

      dumpheap [options] process file#

      +

      转储 process 的堆,写入 file。

      +

      选项包括:

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

    为当前选择器附加控件"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 通常也是列表控件
    • +
    • -w:应用启动时等待调试程序。
    • +
    • --persistent:保留此值。
    • +
    • clear-debug-app 使用 set-debug-app 清除以前针对调试用途设置的软件包。
    -

    UiSelector.classNameContains(str)#

    -
      -
    • str <string> 要包含的字符串
    • +

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

      +

      选项包括:

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

        screen-compat <on> | <off> package#

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

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

    -

    UiSelector.classNameStartsWith(prefix)#

    -
      -
    • prefix <string> 前缀
    • +

      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;如果参数包含一个“/”(正斜杠),则此工具假定参数是一个组件名称;否则,此工具假定参数是一个软件包名称。
      -

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

    -

    UiSelector.classNameEndsWith(suffix)#

    -
      -
    • suffix <string> 后缀
    • +

      应用包名#

      +

      所谓应用包名,是唯一确定应用的标识。例如微信的包名是"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:要查询的用户空间。
      -

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

    -

    UiSelector.classNameMatches(reg)#

    -
      -
    • reg <string> | <Regex> 要满足的正则表达式。
    • +

      list permission-groups#

      +

      输出所有已知的权限组。

      +

      list permissions [options] group#

      +

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

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

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

    -

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

    -

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

    -

    UiSelector.packageName(str)#

    -
      -
    • str <string> 控件文本
    • -
    • 返回 <UiSelector> 返回选择器自身以便链式调用
    • +

      list instrumentation [options]#

      +

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

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

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

    -

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

    -

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

    -

    UiSelector.packageNameContains(str)#

    -
      -
    • str <string> 要包含的字符串
    • +

      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:授予应用清单文件中列出的所有权限。
      -

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

    -

    UiSelector.packageNameStartsWith(prefix)#

    -
      -
    • prefix <string> 前缀
    • +

      uninstall [options] package#

      +

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

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

        clear package#

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

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

    -

    UiSelector.packageNameEndsWith(suffix)#

    -
      -
    • suffix <string> 后缀
    • +

      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)和更低版本的设备上,必须是应用定义的可选权限。
      -

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

    -

    UiSelector.packageNameMatches(reg)#

    -
      -
    • reg <string> | <Regex> 要满足的正则表达式。
    • +

      revoke package_name permission#

      +

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

      +

      set-install-location location#

      +

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

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

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

    -

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

    -

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

    -
      -
    • left <number> 控件左边缘与屏幕左边的距离
    • -
    • top <number> 控件上边缘与屏幕上边的距离
    • -
    • right <number> 控件右边缘与屏幕左边的距离
    • -
    • bottom <number> 控件下边缘与屏幕上边的距离
    • +
      +

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

      +
      +

      get-install-location#

      +

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

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

    一个控件的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, 500, 300).clickable().findOne();
    -w.click();
    -

    UiSelector.drawingOrder(order)#

    -
      -
    • order <number> 控件在父视图中的绘制顺序
    • -
    -

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

    -

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

    -

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

    -

    UiSelector.clickable([b = true])#

    -
    -

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

    -

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

    -

    UiSelector.longClickable([b = true])#

    -
    -

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

    -

    UiSelector.checkable([b = true])#

    -
    -

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

    -

    UiSelector.selected([b = true])#

    +

    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, ArrayJSON.stringify序列化存取。

    +

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

    +

    storages.create(name)#

    -

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

    -

    UiSelector.enabled([b = true])#

    +

    创建一个本地存储并返回一个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)#

    -

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

    -

    UiSelector.scrollable([b = true])#

    +

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

    +

    Storages#

    +

    Storage.get(key[, defaultValue])#

      -
    • b <Boolean> 表示控件是否可滑动
    • +
    • key <string> 键值
    • +
    • defaultValue <any> 可选,默认值
    -

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

    -

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

    -
    className("android.support.v7.widget.RecyclerView").scrollable().findOne().scrollForward();
    -//或者classNameEndsWith("RecyclerView").scrollable().findOne().scrollForward();
    -

    UiSelector.editable([b = true])#

    +

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

    +

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

    +

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

    +

    Storage.put(key, value)#

    -

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

    -

    UiSelector.multiLine([b = true])#

    +

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

    +

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

    +

    Storage.remove(key)#

      -
    • b <Boolean> 表示文本或输入框控件是否是多行显示的
    • +
    • key <string> 键值
    -

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

    -

    UiSelector.findOne()#

    +

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

    +

    Storage.contains(key)#

    -

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

    -

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

    -

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

    -

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

    -

    UiSelector.findOne(timeout)#

    +

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

    +

    Storage.clear()#

    +

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

    + +

    Threads#

    +
    Stability: 1 - Experiment

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

    +

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

    +

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

    +

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

    +

    threads.start(action)#

    -

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

    -

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

    -

    示例:

    -
    //启动Auto.js
    -launchApp("Auto.js");
    -//在6秒内找出日志图标的控件
    -var w = id("action_log").findOne(6000);
    -//如果找到控件则点击
    -if(w != null){
    -    w.click();
    -}else{
    -    //否则提示没有找到
    -    toast("没有找到日志图标");
    +

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

    +

    例如:

    +
    threads.start(function(){
    +    //在新线程执行的代码
    +    while(true){
    +        log("子线程");
    +    }
    +});
    +while(true){
    +    log("脚本主线程");
     }
    -

    UiSelector.findOnce()#

    -
    -

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

    -

    UiSelector.findOnce(i)#

    +

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

    +
    var thread = threads.start(function(){
    +    while(true){
    +        log("子线程");
    +    }
    +});
    +//停止线程执行
    +thread.interrupt();
    +

    更多信息参见Thread

    +

    threads.shutDownAll()#

    +

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

    +

    threads.currentThread()#

    -

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

    -

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

    -

    UiSelector.find()#

    +

    返回当前线程。

    +

    threads.disposable()#

    -

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

    -

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

    -

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

    -
    var c = className("AbsListView").find();
    -if(c.empty()){
    -    toast("找到啦");
    -}else{
    -    toast("没找到╭(╯^╰)╮");
    -}
    -

    UiSelector.untilFind()#

    +

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

    +

    threads.atomic([initialValue])#

    -

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

    -

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

    -

    UiSelector.exists()#

    +

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

    +

    threads.lock()#

    -

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

    -
    if(text("某个文本").exists()){
    -    //要支持的动作
    -}
    -

    UiSelector.waitFor()#

    -

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

    -

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

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

    UiSelector.filter(f)#

    +

    新建一个可重入锁。更多信息参见线程安全以及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])#

      -
    • f <Function> 过滤函数,参数为UiObject,返回值为boolean
    • +
    • timeout <number> 等待时间,单位毫秒
    -

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

    -

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

    -
    var uc = className("TextView").filter(function(w){
    -    return w.text().length == 10;
    +

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

    +

    例如:

    +
    var sum = 0;
    +//启动子线程计算1加到10000
    +var thread = threads.start(function(){
    +    for(var i = 0; i < 10000; i++){
    +        sum += i;
    +    }
     });
    -

    UiObject#

    -

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

    -

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

    -

    UiObject.click()#

    +//等待该线程完成 +thread.join(); +toast("sum = " + sum); +

    isAlive()#

    -

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

    -

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

    -

    UiObject.longClick()#

    -
    -

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

    -

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

    -

    UiObject.setText(text)#

    -
    -

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

    -

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

    -

    UiObject.copy()#

    -
    -

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

    -

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

    -
    var et = className("EditText").findOne();
    -//选中前两个字
    -et.setSelection(0, 2);
    -//对选中内容进行复制
    -if(et.copy()){
    -    toast("复制成功");
    -}else{
    -    toast("复制失败");
    -}
    -

    UiObject.cut()#

    -

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

    -

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

    -

    UiObject.paste()#

    -
    -

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

    -
    //设置剪贴板内容为“你好”
    -setClip("你好");
    -var et = className("EditText").findOne();
    -et.paste();
    -

    UiObject.setSelection(start, end)#

    -
    -

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

    -

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

    -

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

    -

    UiObject.scrollForward()#

    -
    -

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

    -

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

    -

    UiObject.scrollBackward()#

    -
    -

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

    -

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

    -

    UiObject.select()#

    -
    -

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

    -

    UiObject.collapse()#

    -
    -

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

    -

    UiObject.expand()#

    -
    -

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

    -

    UiObject.show()#

    -

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

    -

    UiObject.scrollUp()#

    -

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

    -

    UiObject.scrollDown()#

    -

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

    -

    UiObject.scrollLeft()#

    -

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

    -

    UiObject.scrollRight()#

    -

    children()#

    -
    -

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

    -
    className("AbsListView").findOne().children()
    -    .forEach(function(child){
    -        log(child.className());
    -    });
    -

    childCount()#

    -
    -

    返回子控件数目。

    -

    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对象。

    -

    示例:

    -
    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对象。

    -

    drawingOrder()#

    -
    -

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

    -

    id()#

    -
    -

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

    -

    text()#

    -
    -

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

    -

    findByText(str)#

    -
    -

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

    -

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

    -

    findOne(selector)#

    -
    -

    根据选择器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();
    +

    返回线程是否存活。如果线程仍未开始或已经结束,返回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++);
         }
     });
    -

    find(selector)#

    -
    -

    根据选择器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());
    +while(true){
    +    log(i++);
    +}
    +

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

    +

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

    +
    //atomic返回的对象保证了自增的原子性
    +var i = threads.atomic();
    +threads.start(function(){
    +    while(true){
    +        log(i.getAndIncrement());
         }
     });
    -

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

    -
    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());
    +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();
     }
    -

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

    -

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

    -

    UiCollection.size()#

    -
    -

    返回集合中的控件数。

    -

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

    -

    UiCollection.get(i)#

    +

    或者:

    +
    //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)#

    -

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

    -

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

    -

    UiCollection.each(func)#

    +

    给函数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])#

      -
    • func <Function> 遍历函数,参数为UiObject。
    • +
    • callback <Function> 当定时器到点时要调用的函数。
    • +
    • delay <number> 调用 callback 之前要等待的毫秒数。
    • +
    • ...args <any> 当调用 callback 时要传入的可选参数。
    -

    遍历集合。

    -

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

    -

    empty()#

    +

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

    +

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

    +

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

      -
    • 返回 <Boolean>
    • +
    • callback <Function> 当定时器到点时要调用的函数。
    • +
    • delay <number> 调用 callback 之前要等待的毫秒数。
    • +
    • ...args <any> 当调用 callback 时要传入的可选参数。
    -

    返回控件集合是否为空。

    -

    nonEmpty()#

    +

    预定在 delay 毫秒之后执行的单次 callback。 返回一个用于 clearTimeout() 的 id。

    +

    callback 可能不会精确地在 delay 毫秒被调用。 Auto.js 不能保证回调被触发的确切时间,也不能保证它们的顺序。 回调会在尽可能接近所指定的时间上调用。

    +

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

    +

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

      -
    • 返回 <Boolean>
    • +
    • callback <Function> 在Looper循环的当前回合结束时要调用的函数。
    • +
    • ...args <any> 当调用 callback 时要传入的可选参数。
    -

    返回控件集合是否非空。

    -

    UiCollection.find(selector)#

    +

    预定立即执行的 callback,它是在 I/O 事件的回调之后被触发。 返回一个用于 clearImmediate() 的 id。

    +

    当多次调用 setImmediate() 时,callback 函数会按照它们被创建的顺序依次执行。 每次事件循环迭代都会处理整个回调队列。 如果一个立即定时器是被一个正在执行的回调排入队列的,则该定时器直到下一次事件循环迭代才会被触发。

    +

    setImmediate()、setInterval() 和 setTimeout() 方法每次都会返回表示预定的计时器的id。 它们可用于取消定时器并防止触发。

    +

    clearInterval(id)#

    -

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

    -

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

    +

    取消一个由 setInterval() 创建的循环定时任务。

    例如:

    -
    var names = id("name").find();
    -//在集合
    -var clickableNames = names.find(clickable());
    -

    UiCollection.findOne(selector)#

    +
    //每5秒就发出一次hello
    +var id = setInterval(function(){
    +    toast("hello");
    +}, 5000);
    +//1分钟后取消循环
    +setTimeout(function(){
    +    clearInterval(id);
    +}, 60 * 1000);
    +

    clearTimeout(id)#

    -

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

    -

    Rect#

    -

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

    -

    Rect.left#

    +

    取消一个由 setTimeout() 创建的定时任务。

    +

    clearImmediate(id)#

    -

    长方形左边界的x坐标、

    -

    Rect.right#

    -

    取消一个由 setImmediate() 创建的 Immediate 对象。

    + + +

    用户界面: UI#

    +

    ui模块提供了编写用户界面的支持。

    +
    给Android开发者或者高阶用户的提醒,Auto.js的UI系统来自于Android,所有属性和方法都能在Android源码中找到。如果某些代码或属性没有出现在Auto.js的文档中,可以参考Android的文档。
    +View: https://developer.android.google.cn/reference/android/view/View?hl=cn
    +Widget: https://developer.android.google.cn/reference/android/widget/package-summary?hl=cn
    +

    带有ui的脚本的的最前面必须使用"ui";指定ui模式,否则脚本将不会以ui模式运行。正确示范:s

    +
    "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行一样,也是一个按钮控件,只不过他的文本内容为"第二个按钮"。这两个控件在垂直布局中,因此会纵向排列,效果如图:

    +

    ex1

    +

    如果我们把这个例子的垂直布局(vertical)改成水平布局(horizontal),也即:

    +
    "ui";
    +ui.layout(
    +    <horizontal>
    +        <button text="第一个按钮"/>
    +        <button text="第二个按钮"/>
    +    </horizontal>
    +);
    +

    则这两个按钮会横向排列,效果如图:

    +

    ex1-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是一个字体单位,暂时不用深入理会。上述代码的效果如图:

    +

    ex-properties

    +

    一个界面便由一些布局和控件组成。为了便于文档阅读,我们再说明一下以下术语:

    +
      +
    • 子视图, 子控件: 布局里面的控件是这个布局的子控件/子视图。实际上布局里面不仅仅只能有控件,还可以是嵌套的布局。因此用子视图(Child View)更准确一些。在上面的例子中,按钮便是垂直布局的子控件。
    • +
    • 父视图,父布局:直接包含一个控件的布局是这个控件的父布局/父视图(Parent View)。在上面的例子中,垂直布局便是按钮的父布局。
    -

    长方形右边界的x坐标、

    -

    Rect.top#

    +

    视图: View#

    +

    控件和布局都属于视图(View)。在这个章节中将介绍所有控件和布局的共有的属性和函数。例如属性背景,宽高等(所有控件和布局都能设置背景和宽高),函数click()设置视图(View)被点击时执行的动作。

    +

    attr(name, value)#

    -

    长方形上边界的y坐标、

    -

    Rect.bottom#

    -
    -

    长方形下边界的y坐标、

    -

    Rect.centerX()#

    -
    -

    长方形中点x坐标。

    -

    Rect.centerY()#

    -
    -

    长方形中点y坐标。

    -

    Rect.width()#

    -
    -

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

    -

    Rect.height()#

    -
    -

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

    -

    Rect.contains(r)#

    -
    -

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

    -

    Rect.intersect(r)#

    +

    设置属性的值。属性指定是View在xml中的属性。例如可以通过语句attr("text", "文本")来设置文本控件的文本值。

    +
    "ui";
    +
    +$ui.layout(
    +    <frame>
    +        <text id="example" text="Hello"/>
    +    </frame>
    +);
    +
    +// 5秒后执行
    +$ui.post(() => {
    +    // 修改文本
    +    $ui.example.attr("text", "Hello, Auto.js UI");
    +    // 修改背景
    +    $ui.example.attr("bg", "#ff00ff");
    +    // 修改高度
    +    $ui.example.attr("h", "500dp");
    +}, 5000);
    +
    +

    注意:并不是所有属性都能在js代码设置,有一些属性只能在布局创建时设置,例如style属性;还有一些属性虽然能在代码中设置,但是还没支持;对于这些情况,在Auto.js Pro 8.1.0+会抛出异常,其他版本则不会抛出异常。

    +

    attr(name)#

    -

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

    -

    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轴垂直于设备屏幕表面):

        -

        !axis_device

        -
      • -
      -
    • -
    • 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
      • +
      • name <string> 属性名称
      • +
      • 返回 <string>
      -
    • -
    • gravity 重力传感器,参数(event, gx, gy, gz):

      +

      获取属性的值。

      +
      "ui";
      +
      +$ui.layout(
      +    <frame>
      +        <text id="example" text="1"/>
      +    </frame>
      +);
      +
      +plusOne();
      +
      +function plusOne() {
      +    // 获取文本
      +    let text = $ui.example.attr("text");
      +    // 解析为数字
      +    let num = parseInt(text);
      +    // 数字加1
      +    num++;
      +    // 设置文本
      +    $ui.example.attr("text", String(num));
      +    // 1秒后继续
      +    $ui.post(plusOne, 1000);
      +}
      +
      +
      +

      w#

      +

      View的宽度,是属性width的缩写形式。可以设置的值为*, auto和具体数值。其中*表示宽度尽量填满父布局,而auto表示宽度将根据View的内容自动调整(自适应宽度)。例如:

      +
      "ui";
      +ui.layout(
      +    <horizontal>
      +        <button w="auto" text="自适应宽度"/>
      +        <button w="*" text="填满父布局"/>
      +    </horizontal>
      +);
      +

      在这个例子中,第一个按钮为自适应宽度,第二个按钮为填满父布局,显示效果为:

      +

      ex-w

      +

      如果不设置该属性,则不同的控件和布局有不同的默认宽度,大多数为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的位置,可以设置的值为:

        -
      • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
      • -
      • gx <number> x轴上的重力加速度,单位m/s^2
      • -
      • gy <number> y轴上的重力加速度,单位m/s^2
      • -
      • gz <number> z轴上的重力加速度,单位m/s^2
      • +
      • left 靠左
      • +
      • right 靠右
      • +
      • top 靠顶部
      • +
      • bottom 靠底部
      • +
      • center 居中
      • +
      • center_vertical 垂直居中
      • +
      • center_horizontal 水平居中
      -
    • -
    • linear_acceleration 线性加速度传感器,参数(event, ax, ay, az):

      +

      例如对于一个按钮控件,gravity="right"会使其中的文本内容靠右显示。例如:

      +
      "ui";
      +ui.layout(
      +    <frame>
      +        <button gravity="right" w="*" h="auto" text="靠右的文字"/>
      +    </frame>
      +);
      +

      显示效果为:

      +

      ex-gravity

      +

      这些属性是可以组合的,例如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"使得他在帧布局中位于右下角。效果如图:

      +

      ex-layout-gravity

      +

      要注意的是,layout_gravity的属性不一定总是生效的,具体取决于布局的类别。例如不能让水平布局中的第一个子控件靠底部显示(否则和水平布局本身相违背)。

      +

      margin#

      +

      margin为View和其他View的间距,即外边距。margin属性包括四个值:

        -
      • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
      • -
      • ax <number> x轴上的线性加速度,单位m/s^2
      • -
      • ay <number> y轴上的线性加速度,单位m/s^2
      • -
      • az <number> z轴上的线性加速度,单位m/s^2
      • +
      • marginLeft 左外边距
      • +
      • marginRight 右外边距
      • +
      • marginTop 上外边距
      • +
      • marginBottom 下外边距
      -
    • -
    • ambient_temperature 环境温度传感器,大部分设备并不支持,参数(event, t):

      +

      而margin属性本身的值可以有三种格式:

        -
      • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
      • -
      • t <number> 环境温度,单位摄氏度。
      • +
      • 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。
      -
    • -
    • light 光线传感器,参数(event, light):

      +

      用一个例子来具体理解外边距的含义:

      +
      "ui";
      +ui.layout(
      +    <horizontal>
      +        <button margin="30" text="距离四周30"/>
      +        <button text="普通的按钮"/>
      +    </horizontal>
      +);
      +

      第一个按钮的margin属性指定了他的边距为30dp, 也就是他与水平布局以及第二个按钮的间距都是30dp, 其显示效果如图:

      +

      ex1-margin

      +

      如果把margin="30"改成margin="10 40"那么第一个按钮的左右间距为10dp, 上下间距为40dp, 效果如图:

      +

      ex2-margin

      +

      有关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, 效果如图:

      +

      ex-marginLeft

      +

      marginRight#

      +

      View的右外边距。如果该属性和margin属性指定的值冲突,则在后面的属性生效,前面的属性无效。

      +

      marginTop#

      +

      View的上外边距。如果该属性和margin属性指定的值冲突,则在后面的属性生效,前面的属性无效。

      +

      marginBottom#

      +

      View的下外边距。如果该属性和margin属性指定的值冲突,则在后面的属性生效,前面的属性无效。

      +

      padding#

      +

      View和他的自身内容的间距,也就是内边距。注意和margin属性区分开来,margin属性是View之间的间距,而padding是View和他自身内容的间距。举个例子,一个文本控件的padding也即文本控件的边缘和他的文本内容的间距,paddingLeft即文本控件的左边和他的文本内容的间距。

      +

      paddding属性的值同样有三种格式:

        -
      • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
      • -
      • light <number> 环境光强度,单位lux
      • +
      • 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。
      -
    • -
    • pressure 压力传感器,参数(event, p):

      +

      用一个例子来具体理解内边距的含义:

      +
      "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,其显示效果如图:

      +

      ex-padding

      +

      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

      +

      visibility#

      +

      View的可见性,该属性可以决定View是否显示出来。其值可以为:

        -
      • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
      • -
      • p <number> 大气压,单位hPa
      • +
      • gone 不可见。
      • +
      • visible 可见。默认情况下View都是可见的。
      • +
      • invisible 不可见,但仍然占用位置。
      -
    • -
    • proximity 距离传感器,参数(event, distance):

      +

      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#

      +

      设置字体的样式,比如斜体、粗体等。可选的值为:

        -
      • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
      • -
      • distance <number> 一般指设备前置摄像头旁边的距离传感器到前方障碍物的距离,并且很多设备上这个值只有两种情况:当障碍物较近时该值为0,当障碍物较远或在范围内没有障碍物时该值为5
      • +
      • bold 加粗字体
      • +
      • italic 斜体
      • +
      • normal 正常字体
      -
    • -
    • relative_humidity 湿度传感器,大部分设备并不支持,参数(event, rh):

      +

      可以用或("|")把他们组合起来,比如粗斜体为"bold|italic"。

      +

      例如,粗体:`

      +

      lines#

      +

      设置文本控件的行数。即使文本内容没有达到设置的行数,控件也会留出相应的宽度来显示空白行;如果文本内容超出了设置的行数,则超出的部分不会显示。

      +

      另外在xml中是不能设置多行文本的,要在代码中设置。例如:

      +
      "ui";
      +ui.layout(
      +    <vertical>
      +        <text id="myText" line="3">
      +    </vertical>
      +)
      +//通过\n换行
      +ui.myText.setText("第一行\n第二行\n第三行\n第四行");
      +

      maxLines#

      +

      设置文本控件的最大行数。

      +

      typeface#

      +

      设置字体。可选的值为:

        -
      • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
      • -
      • rh <number> 相对湿度,范围为0~100(百分比)
      • +
      • normal 正常字体
      • +
      • sans 衬线字体
      • +
      • serif 非衬线字体
      • +
      • monospace 等宽字体
      -
    • +

      示例,等宽字体: <text text="等宽字体" typeface="monospace"/>

      +

      ellipsize#

      +

      设置文本的省略号位置。文本的省略号会在文本内容超出文本控件时显示。可选的值为:

      +
        +
      • end 在文本末尾显示省略号
      • +
      • marquee 跑马灯效果,文本将滚动显示
      • +
      • middle 在文本中间显示省略号
      • +
      • none 不显示省略号
      • +
      • start 在文本开头显示省略号
      -

      sensors.register(sensorName[, delay])#

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

          ems#

          +

          当设置该属性后,TextView显示的字符长度(单位是em),超出的部分将不显示,或者根据ellipsize属性的设置显示省略号。

          +

          例如,限制文本最长为5em: `

          +

          autoLink#

          +

          控制是否自动找到url和电子邮件地址等链接,并转换为可点击的链接。默认值为“none”。

          +

          设置该值可以让文本中的链接、电话等变成可点击状态。

          +

          可选的值为以下的值以其通过或("|")的组合:

          +
            +
          • all 匹配所有连接、邮件、地址、电话
          • +
          • email 匹配电子邮件地址
          • +
          • map 匹配地图地址
          • +
          • none 不匹配 (默认)
          • +
          • phone 匹配电话号码
          • +
          • web 匹配URL地址
          - -
        • 返回 SensorEventEmiiter
        • +

          示例:<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 带颜色的无边框按钮
          -

      注册一个传感器监听并返回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);
      +

      这些样式的具体效果参见"示例/界面控件/按钮控件.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 + "您好!");
       });
      -

      更多信息,参见SensorEventEmittersensors.ignoresUnsupportedSensor

      -

      sensors.unregister(emitter)#

      -
      -

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

      -
      //注册一个传感器监听器
      -var sensor = sensors.register("gravity");
      -if(sensor == null){
      -    exit();
      -}
      -//2秒后注销该监听器
      -setTimeout(()=> {
      -    sensors.unregister(sensor);
      -}, 2000);
      -

      sensors.unregisterAll()#

      -

      注销所有传感器监听器。

      -

      sensors.ignoresUnsupportedSensor#

      -

      效果如图:

      +

      ex-input

      +

      除此之外,输入框控件有另外一些主要属性(虽然这些属性对于文本控件也是可用的但一般只用于输入框控件):

      +

      hint#

      +

      输入提示。这个提示会在输入框为空的时候显示出来。如图所示:

      +

      ex-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 用于输入时间。
      -

      表示是否忽略不支持的传感器。如果该值被设置为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'#

      +

      例如,想指定一个输入框的输入类型为小数数字,为: <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数据("data:image/png;base64,...")。

      +

      如果使用图片地址或本地路径,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="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEu0lEQVRoge3bW4iVVRQH8N+ZnDKxvJUGCSWUlXYle/ChiKAkIiu7UXQjonwNIopM8cHoAhkRGQXdfIiE0Ep8KalQoptRTiFFZiRlOo6TPuSk4zk97G9w5vidc77LPjNi84f1MN+391rrf9a+rL32N4xiFMcUjouo5zyciYPYH0FnBadiNiZiD2oR9JbGRdgiOFPDIXRhCWYU0Dcj6duV6BrQuyWxNaLowBcOO1Uv+7EKc4WINUIlabMq6dNI35eJzRHDWOzS2MEB6cd6XI/OQf07k2frkzat9HQnNkcUG7R2dECq2I53EtmePMvaf+MwcWqKu+RzuqhUcfcwcWqKTvmiXFQ2GDodRhQz0aN9ZHsSG0cVrkGf+GT7MG8YeeTCHeKS7sOdMR1stjcWxY2YH0nXh1gdSdf/E+2I8KVYigkl9ewVUsxNpT1qMzaKN4ejJxrtyEt7IuraE1EX2jOkp+JBnFxSzz68KuTqoyiK2BHuxDO4NpK+j/GoOAWF6BiH98Q/SHyCycPIIxMm4FPZCPTj30SynIFr+A7ThotMK4wXopA1Ym9gSiKv5Oj3bdKnFMpuS514E1fm6NMnbF098s3NS4QS0Ik5+hyBsoSXYkGO9jvxy6C/t+IPIYJZcBWW57AXFfMNrSo2kqqw2l4hvSzcIRTw1sm24FVxb5s4NcR0/JXBuUNYJttI6sDjsi1kvTgrGpsMjq3O4FQNa+SbNhWsyKj7I4wpzSYDbpFtKB/EOSn9ZwpRfx5Xp7yfhN0Z9FdxXxxKjTEe2zI4U8NnKf3PNrT2VcWTKe1eyGjjT+Eapm14IqMjNTyd0n9JSrsDwhmaEN2H8GMOO8viUjyMSfJVJh9O0bGoQdt1eFm2oVwve7UpC1ssX568KEXH6fghp54s8lRkrk7CjpxOrGqg6wQ8IKSKWXPpVtIt8ly+v4ATf2t+yqlgDl5SbCjXy8JIXFXweQEHqngxo43JeEw54l+JVLKaJeypRZzoFxavrIWG6cKPW2SO9+PCMkQHsLiA8fpIv5/DmUn4qaCtpWWIEiLzdUHj9XJA2H5uFRbBZriuoI1NSpatpio+nJtFvFvYd2c1sDsGvxfQ3a/knrwgMtm0qD8rPSprCuq8uRmhVqvanBbvm+EQfsNKIcnvTmnTiUdwQcq73oJ2L2v2stXx6vyCRr8RDuk/C8OMUK24J6VtBaekPG81zxuh0TTJhC7FhtUOHF+n61whGalvu8uRWVJFvgPEYOkqQzhLVSPPXLoYa4Xh3Stcls1NaTdb8Xx7ZxnCvSUIfy/kzWno0Pyzx3dL2C0695Hto7NGUhXy5Lzp3kLZKiqNpNTl2+YShgdIvyXbVck44TB/oKTNzWUIv13S+IDsFmpY84QvZAcwTbh4e04o18SwtbIM4dsiOTFYVgzSv7wN+m9vRqjV/PrA0JuCox1bhYNKQ7Qi3CcU1fpiedRG9AkLXhRfbxCnKlET0s21ifwaSWcPbopBdDDOwGtClTD2vCsq+/C68K8HmVDk7DhFyIsvFzKnGThN+689+oU9dptwQb5B+LB8dx4lMb7xqAhkJwo/xljhFFSfSdUc3mPrcbwj15P+pP0/QiR7hYSkGsHnUYziWMF/mXV4JVcZ8G0AAAAASUVORK5CYII="/>
      +    </frame>
      +);
      +

      tint#

      +

      图片着色,其值是一个颜色名称或RGB颜色值。使用该属性会将图片中的非透明区域都涂上同一颜色。可以用于改变图片的颜色。

      +

      例如,对于上面的base64的图片: <img w="40" h="40" tint="red" src="data:image/png;base64,..."/>,则钱包图标颜色会变成红色。

      +

      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的属性,用于指定布局的方向,可选的值为verticalhorizontal

      +

      例如<linear orientation="vertical"></linear>相当于<vertical></vertical>

      +

      线性布局的默认方向是横向的,因此,一个没有指定orientation属性的线性布局就是横向布局。

      +

      帧布局: frame#

      +

      帧布局

      +

      相对布局: relative#

      +

      勾选框控件: checkbox#

      +

      选择框控件: radio#

      +

      选择框布局: radiogroup#

      +

      开关控件: Switch#

      +

      开关控件用于表示一个选项是否被选中。

      +

      checked#

      +

      表示开关是否被选中。可选的值为:

      +
        +
      • true 打开开关
      • +
      • false 关闭开关
      • +
      +

      text#

      +

      对开关进行描述的文字。

      +

      进度条控件: progressbar#

      +

      拖动条控件: seekbar#

      +

      下来菜单控件: spinner#

      +

      时间选择控件: timepicker#

      +

      日期选择控件: datepicker#

      +

      浮动按钮控件: fab#

      +

      标题栏控件: toolbar#

      +

      卡片: card#

      +

      卡片控件是一个拥有圆角、阴影的控件。

      +

      cardBackgroundColor#

      +

      卡片的背景颜色。

      +

      cardCornerRadius#

      +

      卡片的圆角半径。

      +

      cardElevation#

      +

      设置卡片在z轴上的高度,来控制阴影的大小。

      +

      contentPadding#

      +

      设置卡片的内边距。该属性包括四个值:

      +
        +
      • contentPaddingLeft 左内边距
      • +
      • contentPaddingRight 右内边距
      • +
      • contentPaddingTop 上内边距
      • +
      • contentPaddingBottom 下内边距
      • +
      +

      foreground#

      +

      使用foreground="?selectableItemBackground"属性可以为卡片添加点击效果。

      +

      抽屉布局: drawer#

      +

      列表: list#

      +

      Tab: tab#

      +

      ui#

      +

      ui.layout(xml)#

        -
      • sensorName <string> 不支持的传感器名称
      • +
      • xml <XML> | <string> 布局XML或者XML字符串
      -

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

      -

      SensorEventEmitter#

      -

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

      -

      事件: 'change'#

      +

      将布局XML渲染为视图(View)对象, 并设置为当前视图。

      +

      ui.layoutFile(xmlFile)#

        -
      • ..args <Any> 传感器参数
      • +
      • xml <string> 布局XML文件的路径
      -

      当传感器数据改变时触发该事件;该事件触发的最高频繁由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'#

      +

      此函数和ui.layout相似,只不过允许传入一个xml文件路径来渲染布局。

      +

      ui.inflate(xml[, parent = null, attachToParent = false])#

        -
      • accuracy <number> 表示传感器精度。为以下值之一:
          -
        • -1 传感器未连接
        • -
        • 0 传感器不可读
        • -
        • 1 低精度
        • -
        • 2 中精度
        • -
        • 3 高精度
        • +
        • xml <string> | <XML> 布局XML或者XML字符串
        • +
        • parent <View> 父视图
        • +
        • attachToParent <boolean> 是否渲染的View加到父视图中,默认为false
        • +
        • 返回 <View>
        -
      • -
      -

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

      - - -

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

      -

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

      -

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

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

        将布局XML渲染为视图(View)对象。如果该View将作为某个View的子View,我们建议传入parent参数,这样在渲染时依赖于父视图的一些布局属性能够正确应用。

        +

        此函数用于动态创建、显示View。

        +
        "ui";
        +
        +$ui.layout(
        +    <linear id="container">
        +    </linear>
        +);
        +
        +// 动态创建3个文本控件,并加到container容器中
        +// 这里仅为实例,实际上并不推荐这种做法,如果要展示列表,
        +// 使用list组件;动态创建十几个、几十个View会让界面卡顿
        +for (let i = 0; i < 3; i++) {
        +    let textView = $ui.inflate(
        +        <text textColor="#000000" textSize="14sp"/>
        +    , $ui.container);
        +    textView.attr("text", "文本控件" + i);
        +    $ui.container.addView(textView);
        +}
        +
        +

        ui.registerWidget(name, widget)#

        +
        -

        shell函数#

        -
        Stability: 2 - Stable

        shell(cmd[, root])#

        +

        注册一个自定义组件。参考示例->界面控件->自定义控件。

        +

        ui.isUiThread()#

        -

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

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

          返回当前线程是否是UI线程。

          +
          "ui";
          +
          +log($ui.isUiThread()); // => true
          +
          +$threads.start(function () {
          +    log($ui.isUiThread()); // => false
          +});
          +
          +
          +

          ui.findView(id)#

          +
          -

          示例(强制停止微信) :

          -
          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)#

          +

          在当前视图中根据ID查找相应的视图对象并返回。如果当前未设置视图或找不到此ID的视图时返回null

          +

          一般我们都是通过ui.xxx来获取id为xxx的控件,如果xxx是一个ui已经有的属性,就可以通过$ui.findView()来获取这个控件。

          +

          ui.finish()#

          +

          结束当前活动并销毁界面。

          +

          ui.setContentView(view)#

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

          Shell对象的"构造函数"。

          -
          var sh = new Shell(true);
          -//强制停止微信
          -sh.exec("am force-stop com.tencent.mm");
          -sh.exit();
          -

          Shell.exec(cmd)#

          +

          将视图对象设置为当前视图。

          +

          ui.post(callback[, delay = 0])#

          -

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

          -

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

          -

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

          -

          Shell.exit()#

          -

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

          -

          Shell.exitAndWaitFor()#

          -

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

          -

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

          -

          Shell.setCallback(callback)#

          +

          callback加到UI线程的消息循环中,并延迟delay毫秒后执行(不能准确保证一定在delay毫秒后执行)。

          +

          此函数可以用于UI线程中延时执行动作(sleep不能在UI线程中使用),也可以用于子线程中更新UI。

          +
          "ui";
          +
          +ui.layout(
          +    <frame>
          +        <text id="result"/>
          +    </frame>
          +);
          +
          +ui.result.attr("text", "计算中");
          +// 在子线程中计算1+ ... + 10000000
          +threads.start({
          +    let sum = 0;
          +    for (let i = 0; i < 1000000; i++) {
          +        sum += i;
          +    }
          +    // 由于不能在子线程操作UI,所以要抛到UI线程执行
          +    ui.post(() => {
          +        ui.result.attr("text", String(sum));
          +    });
          +});
          +
          +

          ui.run(callback)#

            -
          • callback <Object> 回调函数
          • +
          • callback <Function> 回调函数
          • +
          • 返回 callback的执行结果
          -

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

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

            callback在UI线程中执行。如果当前已经在UI线程中,则直接执行callback;否则将callback抛到UI线程中执行(加到UI线程的消息循环的末尾),并等待callback执行结束(阻塞当前线程)

            +

            ui.statusBarColor(color)#

            +
            -

            例如:

            -
            var sh = new Shell();
            -sh.setCallback({
            -    onNewLine: function(line){
            -        //有新的一行输出时打印到控制台
            -        log(line);
            -    }
            -})
            -while(true){
            -    //循环输入命令
            -    var cmd = dialogs.rawInput("请输入要执行的命令,输入exit退出");
            -    if(cmd == "exit"){
            -        break;
            +

            设置当前界面的状态栏颜色。

            +
            "ui";
            +ui.statusBarColor("#000000");
            +
            +

            ui.useAndroidResources()#

            +

            启用使用Android的布局(layout)、绘图(drawable)、动画(anim)、样式(style)等资源的特性。启用该特性后,在project.json中进行以下配置,就可以像写Android原生一样写界面:

            +
            {
            +    // ...
            +    androidResources: {
            +        "resDir": "res",  // 资源文件夹
            +        "manifest": "AndroidManifest.xml" // AndroidManifest文件路径
                 }
            -    //执行命令
            -    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 参数的规范

            -

            选项包括:

            +
            +

            res文件夹通常为以下结构:

            +
            - res
            +    - layout  // 布局资源
            +    - drawable // 图片、形状等资源
            +    - menu // 菜单资源
            +    - values // 样式、字符串等资源
            +    // ...
            +

            可参考示例->复杂界面->Android原生界面。

            +

            尺寸的单位: Dimension#

            +

            Drawables#

            +

            颜色#

            +

            (完善中...)

            + + +

            基于控件的操作#

            +

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

            +

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

            +

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

            +

            auto([mode])#

            +
            +

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

              -
            • -D:启用调试。
            • -
            • -W:等待启动完成。
            • -
            • --start-profiler file:启动分析器并将结果发送到 file。
            • -
            • -P file:类似于 --start-profiler,但当应用进入空闲状态时分析停止。
            • -
            • -R count:重复 Activity 启动 count 次数。在每次重复前,将完成顶部 Activity。
            • -
            • -S:启动 Activity 前强行停止目标应用。
            • -
            • --opengl-trace:启用 OpenGL 函数的跟踪。
            • -
            • --user user_id | current:指定要作为哪个用户运行;如果未指定,则作为当前用户运行。
            • +
            • fast 快速模式。该模式下会启用控件缓存,从而选择器获取屏幕控件更快。对于需要快速的控件操作的脚本可以使用该模式,一般脚本则没有必要使用该函数。
            • +
            • normal 正常模式,默认。
            -

            startservice [options] intent#

            -

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

            +

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

            +

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

            +

            示例:

            +
            auto("fast");
            +

            示例2:

            +
            auto();
            +

            auto.waitFor()#

            +

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

            +

            因为该函数是阻塞的,因此除非是有协程特性,否则不能在ui模式下运行该函数,建议在ui模式下使用auto()函数。

            +

            auto.setMode(mode)#

            +
            +

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

              -
            • --user user_id | current:指定要作为哪个用户运行;如果未指定,则作为当前用户运行。
            • +
            • fast 快速模式。该模式下会启用控件缓存,从而选择器获取屏幕控件更快。对于需要快速的控件查看和操作的脚本可以使用该模式,一般脚本则没有必要使用该函数。
            • +
            • normal 正常模式,默认。
            -

            force-stop package#

            -

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

            -

            kill [options] package#

            -

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

            +

            auto.setFlags(flags)#

            +

            [v4.1.0新增]

              -
            • --user user_id | all | current:指定将终止其进程的用户;如果未指定,则终止所有用户的进程。
            • +
            • flags <string> | <Array> 一些标志,来启用和禁用某些特性,包括:
                +
              • findOnUiThread 使用该特性后,选择器搜索时会在主进程进行。该特性用于解决线程安全问题导致的次生问题,不过目前貌似已知问题并不是线程安全问题。
              • +
              • useUsageStats 使用该特性后,将会以"使用情况统计"服务的结果来检测当前正在运行的应用包名(需要授予"查看使用情况统计"权限)。如果觉得currentPackage()返回的结果不太准确,可以尝试该特性。
              • +
              • useShell 使用该特性后,将使用shell命令获取当前正在运行的应用的包名、活动名称,但是需要root权限。
              -

              kill-all#

              -

              终止所有后台进程。

              -

              broadcast [options] intent#

              -

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

              -

              选项包括:

              +
            • +
            +

            启用有关automator的一些特性。例如:

            +
            auto.setFlags(["findOnUiThread", "useShell"]);
            +

            auto.serivce#

            +

            [v4.1.0新增]

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

            instrument [options] component#

            -

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

            +

            获取无障碍服务。如果无障碍服务没有启动,则返回null

            +

            参见AccessibilityService

            +

            auto.windows#

            +

            [v4.1.0新增]

              -
            • -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 的分析器。
            • +
            • <Array>
            -

            dumpheap [options] process file#

            -

            转储 process 的堆,写入 file。

            -

            选项包括:

            +

            当前所有窗口(AccessibilityWindowInfo)的数组,可能包括状态栏、输入法、当前应用窗口,弹出窗口、悬浮窗、分屏应用窗口等。可以分别获取每个窗口的布局信息。

            +

            该函数需要Android 5.0以上才能运行。

            +

            auto.root#

            +

            [v4.1.0新增]

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

            选项包括:

            +

            当前窗口的布局根元素。如果无障碍服务未启动或者WindowFilter均返回false,则会返回null

            +

            如果不设置windowFilter,则当前窗口即为活跃的窗口(获取到焦点、正在触摸的窗口);如果设置了windowFilter,则获取的是过滤的窗口中的第一个窗口。

            +

            如果系统是Android5.0以下,则始终返回当前活跃的窗口的布局根元素。

            +

            auto.rootInActiveWindow#

            +

            [v4.1.0新增]

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

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

            -

            选项包括:

            +

            当前活跃的窗口(获取到焦点、正在触摸的窗口)的布局根元素。如果无障碍服务未启动则为null

            +

            auto.setWindowFilter(filter)#

            +

            [v4.1.0新增]

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

              screen-compat <on> | <off> package#

              -控制 package 的屏幕兼容性模式。
            • +
            • filter <Function> 参数为窗口(AccessibilityWindowInfo),返回值为Boolean的函数。
            -

            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:

            +

            设置窗口过滤器。这个过滤器可以决定哪些窗口是目标窗口,并影响选择器的搜索。例如,如果想要选择器在所有窗口(包括状态栏、输入法等)中搜索,只需要使用以下代码:

            +
            auto.setWindowFilter(function(window){
            +    //不管是如何窗口,都返回true,表示在该窗口中搜索
            +    return true;
            +});
            +

            又例如,当前使用了分屏功能,屏幕上有Auto.js和QQ两个应用,但我们只想选择器对QQ界面进行搜索,则:

            +
            auto.setWindowFilter(function(window){
            +    // 对于应用窗口,他的title属性就是应用的名称,因此可以通过title属性来判断一个应用
            +    return window.title == "QQ";
            +});
            +

            选择器默认是在当前活跃的窗口中搜索,不会搜索诸如悬浮窗、状态栏之类的,使用WindowFilter则可以控制搜索的窗口。

            +

            需要注意的是, 如果WindowFilter返回的结果均为false,则选择器的搜索结果将为空。

            +

            另外setWindowFilter函数也会影响auto.windowRoots的结果。

            +

            该函数需要Android 5.0以上才有效。

            +

            auto.windowRoots#

            +

            [v4.1.0新增]

              -
            • -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;如果参数包含一个“/”(正斜杠),则此工具假定参数是一个组件名称;否则,此工具假定参数是一个软件包名称。
            • +
            • <Array>
            -

            应用包名#

            -

            所谓应用包名,是唯一确定应用的标识。例如微信的包名是"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:要查询的用户空间。
            • +

              返回当前被WindowFilter过滤的窗口的布局根元素组成的数组。

              +

              如果系统是Android5.0以下,则始终返回当前活跃的窗口的布局根元素的数组。

              +

              SimpleActionAutomator#

              +
              Stability: 2 - Stable

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

              +

              click(text[, i])#

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

              list permission-groups#

              -

              输出所有已知的权限组。

              -

              list permissions [options] group#

              -

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

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

              返回是否点击成功。当屏幕中并未包含该文本,或者该文本所在区域不能点击时返回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> 要点击的长方形区域右边与屏幕右边的像素距离
              -

              list instrumentation [options]#

              -

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

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

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

              +

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

              +

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

              +

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

              +

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

              +

              longClick(text[, i]))#

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

              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:授予应用清单文件中列出的所有权限。
              • +

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

              +

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

              +

              scrollUp([i])#

              +
              -

              uninstall [options] package#

              -

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

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

                clear package#

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

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

              +

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

              +

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

              +

              scrollDown([i])#

              +
              -

              enable package_or_component#

              -

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

              -

              disable package_or_component#

              -

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

              -

              disable-user [options] package_or_component#

              -

              选项:

              +

              找到第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属性,那么这个属性很可能是唯一的,除了以下几种情况:

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

                grant package_name permission#

                -向应用授予权限。在运行 Android 6.0(API 级别 23)及更高版本的设备上,可以是应用清单中声明的任何权限。在运行 Android 5.1(API 级别 22)和更低版本的设备上,必须是应用定义的可选权限。
              • +
              • QQ的控件的id属性很多都是"name",也就是在QQ界面难以通过id来定位一个控件
              • +
              • 列表中的控件,比如QQ联系人列表,微信联系人列表等
              -

              revoke package_name permission#

              -

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

              -

              set-install-location location#

              -

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

              +

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

              +

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

                -
              • 0:自动—让系统决定最佳位置。
              • -
              • 1:内部—安装在内部设备存储上。
              • -
              • 2:外部—安装在外部介质上。
              • +
              • 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 控件的布局深度。
              -
              -

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

              -
              -

              get-install-location#

              -

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

              +

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

              +

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

                -
              • 0 [auto]:让系统决定最佳位置。
              • -
              • 1 [internal]:安装在内部设备存储上
              • -
              • 2 [external]:安装在外部介质上
              • +
              • click() 点击。点击一个控件,前提是这个控件的clickable属性为true
              • +
              • longClick() 长按。长按一个控件,前提是这个控件的longClickable属性为true
              • +
              • setText() 设置文本,用于编辑框控件设置文本。
              • +
              • scrollForward(), scrollBackward() 滑动。滑动一个控件(列表等), 前提是这个控件的scrollable属性为true
              • +
              • exits() 判断控件是否存在
              • +
              • waitFor() 等待控件出现
              -

              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, ArrayJSON.stringify序列化存取。

              -

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

              -

              storages.create(name)#

              +

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

              +
              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()#

                -
              • name <string> 本地存储名称
              • +
              • 返回 <UiSelector>
              -

              创建一个本地存储并返回一个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)#

              +

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

              +

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

              +

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

              +

              UiSelector.algorithm(algorithm)#

              +

              [v4.1.0新增]

              +
                +
              • algorithm <string> 搜索算法,可选的值有:
                  +
                • DFS 深度优先算法,选择器的默认算法
                • +
                • BFS 广度优先算法
                • +
                +
              • +
              +

              指定选择器的搜索算法。例如:

              +
              log(selector().text("文本").algorithm("BFS").find());
              +

              广度优先在控件所在层次较低时,或者布局的层次不多时,通常能更快找到控件。

              +

              UiSelector.text(str)#

                -
              • name <string> 本地存储名称
              • +
              • str <string> 控件文本
              • +
              • 返回 <UiSelector> 返回选择器自身以便链式调用
              -

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

              -

              Storages#

              -

              Storage.get(key[, defaultValue])#

              +

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

              +

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

              +

              UiSelector.textContains(str)#

                -
              • key <string> 键值
              • -
              • defaultValue <any> 可选,默认值
              • +
              • str <string> 要包含的字符串
              -

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

              -

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

              -

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

              -

              Storage.put(key, value)#

              +

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

              +

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

              +

              UiSelector.textStartsWith(prefix)#

              -

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

              -

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

              -

              Storage.remove(key)#

              +

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

              +

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

              +

              UiSelector.textEndsWith(suffix)#

              -

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

              -

              Storage.contains(key)#

              +

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

              +

              UiSelector.textMatches(reg)#

              -

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

              -

              Storage.clear()#

              -

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

              - -

              Threads#

              -
              Stability: 1 - Experiment

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

              -

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

              -

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

              -

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

              -

              threads.start(action)#

              +

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

              +

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

              +

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

              +

              UiSelector.desc(str)#

                -
              • action <Function> 要在新线程执行的函数
              • -
              • 返回 Thread
              • +
              • str <string> 控件文本
              • +
              • 返回 <UiSelector> 返回选择器自身以便链式调用
              -

              启动一个新线程并执行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()#

              +

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

              +

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

              +

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

              +

              UiSelector.descContains(str)#

              -

              返回当前线程。

              -

              threads.disposable()#

              +

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

              +

              UiSelector.descStartsWith(prefix)#

              -

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

              -

              threads.atomic([initialValue])#

              +

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

              +

              UiSelector.descEndsWith(suffix)#

              -

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

              -

              threads.lock()#

              +

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

              +

              UiSelector.descMatches(reg)#

              -

              新建一个可重入锁。更多信息参见线程安全以及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])#

              +

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

              +

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

              +

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

              +

              UiSelector.id(resId)#

                -
              • timeout <number> 等待时间,单位毫秒
              • +
              • resId <string> 控件的id,以"包名:id/"开头,例如"com.tencent.mm:id/send_btn"。也可以不指定包名,这时会以当前正在运行的应用的包名来补全id。例如id("send_btn"),在QQ界面想当于id("com.tencent.mobileqq:id/send_btn")。
              -

              等待线程执行完成。如果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()#

              +

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

              +

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

              +

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

              +

              UiSelector.idContains(str)#

              -

              返回线程是否存活。如果线程仍未开始或已经结束,返回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)#

              +

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

              +

              UiSelector.idStartsWith(prefix)#

              -

              给函数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])#

              +

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

              +

              UiSelector.idEndsWith(suffix)#

                -
              • callback <Function> 当定时器到点时要调用的函数。
              • -
              • delay <number> 调用 callback 之前要等待的毫秒数。
              • -
              • ...args <any> 当调用 callback 时要传入的可选参数。
              • +
              • suffix <string> id后缀
              -

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

              -

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

              -

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

              +

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

              +

              UiSelector.idMatches(reg)#

                -
              • callback <Function> 当定时器到点时要调用的函数。
              • -
              • delay <number> 调用 callback 之前要等待的毫秒数。
              • -
              • ...args <any> 当调用 callback 时要传入的可选参数。
              • +
              • reg <Regex> | <string> id要满足的正则表达式
              -

              预定在 delay 毫秒之后执行的单次 callback。 返回一个用于 clearTimeout() 的 id。

              -

              callback 可能不会精确地在 delay 毫秒被调用。 Auto.js 不能保证回调被触发的确切时间,也不能保证它们的顺序。 回调会在尽可能接近所指定的时间上调用。

              -

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

              -

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

              +

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

              +

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

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

              UiSelector.className(str)#

                -
              • callback <Function> 在Looper循环的当前回合结束时要调用的函数。
              • -
              • ...args <any> 当调用 callback 时要传入的可选参数。
              • +
              • str <string> 控件文本
              • +
              • 返回 <UiSelector> 返回选择器自身以便链式调用
              -

              预定立即执行的 callback,它是在 I/O 事件的回调之后被触发。 返回一个用于 clearImmediate() 的 id。

              -

              当多次调用 setImmediate() 时,callback 函数会按照它们被创建的顺序依次执行。 每次事件循环迭代都会处理整个回调队列。 如果一个立即定时器是被一个正在执行的回调排入队列的,则该定时器直到下一次事件循环迭代才会被触发。

              -

              setImmediate()、setInterval() 和 setTimeout() 方法每次都会返回表示预定的计时器的id。 它们可用于取消定时器并防止触发。

              -

              clearInterval(id)#

              +

              为当前选择器附加控件"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)#

                -
              • id <number> 一个 setInterval() 返回的 id。
              • +
              • str <string> 要包含的字符串
              -

              取消一个由 setInterval() 创建的循环定时任务。

              -

              例如:

              -
              //每5秒就发出一次hello
              -var id = setInterval(function(){
              -    toast("hello");
              -}, 5000);
              -//1分钟后取消循环
              -setTimeout(function(){
              -    clearInterval(id);
              -}, 60 * 1000);
              -

              clearTimeout(id)#

              +

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

              +

              UiSelector.classNameStartsWith(prefix)#

              -

              取消一个由 setTimeout() 创建的定时任务。

              -

              clearImmediate(id)#

              +

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

              +

              UiSelector.classNameEndsWith(suffix)#

              -

              取消一个由 setImmediate() 创建的 Immediate 对象。

              - - -

              用户界面: UI#

              -

              ui模块提供了编写用户界面的支持。

              -
              给Android开发者或者高阶用户的提醒,Auto.js的UI系统来自于Android,所有属性和方法都能在Android源码中找到。如果某些代码或属性没有出现在Auto.js的文档中,可以参考Android的文档。
              -View: https://developer.android.google.cn/reference/android/view/View?hl=cn
              -Widget: https://developer.android.google.cn/reference/android/widget/package-summary?hl=cn
              -

              带有ui的脚本的的最前面必须使用"ui";指定ui模式,否则脚本将不会以ui模式运行。正确示范:s

              -
              "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行一样,也是一个按钮控件,只不过他的文本内容为"第二个按钮"。这两个控件在垂直布局中,因此会纵向排列,效果如图:

              -

              ex1

              -

              如果我们把这个例子的垂直布局(vertical)改成水平布局(horizontal),也即:

              -
              "ui";
              -ui.layout(
              -    <horizontal>
              -        <button text="第一个按钮"/>
              -        <button text="第二个按钮"/>
              -    </horizontal>
              -);
              -

              则这两个按钮会横向排列,效果如图:

              -

              ex1-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是一个字体单位,暂时不用深入理会。上述代码的效果如图:

              -

              ex-properties

              -

              一个界面便由一些布局和控件组成。为了便于文档阅读,我们再说明一下以下术语:

              -
                -
              • 子视图, 子控件: 布局里面的控件是这个布局的子控件/子视图。实际上布局里面不仅仅只能有控件,还可以是嵌套的布局。因此用子视图(Child View)更准确一些。在上面的例子中,按钮便是垂直布局的子控件。
              • -
              • 父视图,父布局:直接包含一个控件的布局是这个控件的父布局/父视图(Parent View)。在上面的例子中,垂直布局便是按钮的父布局。
              • +

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

                +

                UiSelector.classNameMatches(reg)#

                +
                  +
                • reg <string> | <Regex> 要满足的正则表达式。
                -

                视图: View#

                -

                控件和布局都属于视图(View)。在这个章节中将介绍所有控件和布局的共有的属性和函数。例如属性背景,宽高等(所有控件和布局都能设置背景和宽高),函数click()设置视图(View)被点击时执行的动作。

                -

                attr(name, value)#

                +

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

                +

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

                +

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

                +

                UiSelector.packageName(str)#

                  -
                • name <string> 属性名称
                • -
                • value <string> 属性的值
                • +
                • str <string> 控件文本
                • +
                • 返回 <UiSelector> 返回选择器自身以便链式调用
                -

                设置属性的值。属性指定是View在xml中的属性。例如可以通过语句attr("text", "文本")来设置文本控件的文本值。

                -
                "ui";
                -
                -$ui.layout(
                -    <frame>
                -        <text id="example" text="Hello"/>
                -    </frame>
                -);
                -
                -// 5秒后执行
                -$ui.post(() => {
                -    // 修改文本
                -    $ui.example.attr("text", "Hello, Auto.js UI");
                -    // 修改背景
                -    $ui.example.attr("bg", "#ff00ff");
                -    // 修改高度
                -    $ui.example.attr("h", "500dp");
                -}, 5000);
                -
                -

                注意:并不是所有属性都能在js代码设置,有一些属性只能在布局创建时设置,例如style属性;还有一些属性虽然能在代码中设置,但是还没支持;对于这些情况,在Auto.js Pro 8.1.0+会抛出异常,其他版本则不会抛出异常。

                -

                attr(name)#

                +

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

                +

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

                +

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

                +

                UiSelector.packageNameContains(str)#

                -

                获取属性的值。

                -
                "ui";
                -
                -$ui.layout(
                -    <frame>
                -        <text id="example" text="1"/>
                -    </frame>
                -);
                -
                -plusOne();
                -
                -function plusOne() {
                -    // 获取文本
                -    let text = $ui.example.attr("text");
                -    // 解析为数字
                -    let num = parseInt(text);
                -    // 数字加1
                -    num++;
                -    // 设置文本
                -    $ui.example.attr("text", String(num));
                -    // 1秒后继续
                -    $ui.post(plusOne, 1000);
                -}
                -
                -
                -

                w#

                -

                View的宽度,是属性width的缩写形式。可以设置的值为*, auto和具体数值。其中*表示宽度尽量填满父布局,而auto表示宽度将根据View的内容自动调整(自适应宽度)。例如:

                -
                "ui";
                -ui.layout(
                -    <horizontal>
                -        <button w="auto" text="自适应宽度"/>
                -        <button w="*" text="填满父布局"/>
                -    </horizontal>
                -);
                -

                在这个例子中,第一个按钮为自适应宽度,第二个按钮为填满父布局,显示效果为:

                -

                ex-w

                -

                如果不设置该属性,则不同的控件和布局有不同的默认宽度,大多数为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 水平居中
                • +

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

                  +

                  UiSelector.packageNameStartsWith(prefix)#

                  +
                  -

                  例如对于一个按钮控件,gravity="right"会使其中的文本内容靠右显示。例如:

                  -
                  "ui";
                  -ui.layout(
                  -    <frame>
                  -        <button gravity="right" w="*" h="auto" text="靠右的文字"/>
                  -    </frame>
                  -);
                  -

                  显示效果为:

                  -

                  ex-gravity

                  -

                  这些属性是可以组合的,例如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"使得他在帧布局中位于右下角。效果如图:

                  -

                  ex-layout-gravity

                  -

                  要注意的是,layout_gravity的属性不一定总是生效的,具体取决于布局的类别。例如不能让水平布局中的第一个子控件靠底部显示(否则和水平布局本身相违背)。

                  -

                  margin#

                  -

                  margin为View和其他View的间距,即外边距。margin属性包括四个值:

                  -
                    -
                  • marginLeft 左外边距
                  • -
                  • marginRight 右外边距
                  • -
                  • marginTop 上外边距
                  • -
                  • marginBottom 下外边距
                  • +

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

                  +

                  UiSelector.packageNameEndsWith(suffix)#

                  +
                  -

                  而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。
                  • +

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

                  +

                  UiSelector.packageNameMatches(reg)#

                  +
                    +
                  • reg <string> | <Regex> 要满足的正则表达式。
                  -

                  用一个例子来具体理解外边距的含义:

                  -
                  "ui";
                  -ui.layout(
                  -    <horizontal>
                  -        <button margin="30" text="距离四周30"/>
                  -        <button text="普通的按钮"/>
                  -    </horizontal>
                  -);
                  -

                  第一个按钮的margin属性指定了他的边距为30dp, 也就是他与水平布局以及第二个按钮的间距都是30dp, 其显示效果如图:

                  -

                  ex1-margin

                  -

                  如果把margin="30"改成margin="10 40"那么第一个按钮的左右间距为10dp, 上下间距为40dp, 效果如图:

                  -

                  ex2-margin

                  -

                  有关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, 效果如图:

                  -

                  ex-marginLeft

                  -

                  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。
                  • +

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

                  +

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

                  +

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

                  +
                    +
                  • left <number> 控件左边缘与屏幕左边的距离
                  • +
                  • top <number> 控件上边缘与屏幕上边的距离
                  • +
                  • right <number> 控件右边缘与屏幕左边的距离
                  • +
                  • bottom <number> 控件下边缘与屏幕上边的距离
                  -

                  用一个例子来具体理解内边距的含义:

                  -
                  "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,其显示效果如图:

                  -

                  ex-padding

                  -

                  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

                  -

                  visibility#

                  -

                  View的可见性,该属性可以决定View是否显示出来。其值可以为:

                  -
                    -
                  • gone 不可见。
                  • -
                  • visible 可见。默认情况下View都是可见的。
                  • -
                  • invisible 不可见,但仍然占用位置。
                  • +

                  一个控件的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> 范围下边缘与屏幕上边的距离
                  -

                  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 正常字体
                  • +

                  为当前选择器附加控件"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> 范围下边缘与屏幕上边的距离
                  -

                  可以用或("|")把他们组合起来,比如粗斜体为"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 等宽字体
                  • +

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

                  +

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

                  +
                  var w = boundsContains(500, 300, 500, 300).clickable().findOne();
                  +w.click();
                  +

                  UiSelector.drawingOrder(order)#

                  +
                    +
                  • order <number> 控件在父视图中的绘制顺序
                  -

                  示例,等宽字体: <text text="等宽字体" typeface="monospace"/>

                  -

                  ellipsize#

                  -

                  设置文本的省略号位置。文本的省略号会在文本内容超出文本控件时显示。可选的值为:

                  -
                    -
                  • end 在文本末尾显示省略号
                  • -
                  • marquee 跑马灯效果,文本将滚动显示
                  • -
                  • middle 在文本中间显示省略号
                  • -
                  • none 不显示省略号
                  • -
                  • start 在文本开头显示省略号
                  • +

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

                  +

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

                  +

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

                  +

                  UiSelector.clickable([b = true])#

                  +
                  -

                  ems#

                  -

                  当设置该属性后,TextView显示的字符长度(单位是em),超出的部分将不显示,或者根据ellipsize属性的设置显示省略号。

                  -

                  例如,限制文本最长为5em: `

                  -

                  autoLink#

                  -

                  控制是否自动找到url和电子邮件地址等链接,并转换为可点击的链接。默认值为“none”。

                  -

                  设置该值可以让文本中的链接、电话等变成可点击状态。

                  -

                  可选的值为以下的值以其通过或("|")的组合:

                  -
                    -
                  • all 匹配所有连接、邮件、地址、电话
                  • -
                  • email 匹配电子邮件地址
                  • -
                  • map 匹配地图地址
                  • -
                  • none 不匹配 (默认)
                  • -
                  • phone 匹配电话号码
                  • -
                  • web 匹配URL地址
                  • +

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

                  +

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

                  +

                  UiSelector.longClickable([b = true])#

                  +
                  -

                  示例:<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 带颜色的无边框按钮
                  • +

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

                  +

                  UiSelector.checkable([b = true])#

                  +
                  -

                  这些样式的具体效果参见"示例/界面控件/按钮控件.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 + "您好!");
                  -});
                  -

                  效果如图:

                  -

                  ex-input

                  -

                  除此之外,输入框控件有另外一些主要属性(虽然这些属性对于文本控件也是可用的但一般只用于输入框控件):

                  -

                  hint#

                  -

                  输入提示。这个提示会在输入框为空的时候显示出来。如图所示:

                  -

                  ex-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 用于输入时间。
                  • +

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

                  +

                  UiSelector.selected([b = true])#

                  +
                  -

                  例如,想指定一个输入框的输入类型为小数数字,为: <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数据("data:image/png;base64,...")。

                  -

                  如果使用图片地址或本地路径,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="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEu0lEQVRoge3bW4iVVRQH8N+ZnDKxvJUGCSWUlXYle/ChiKAkIiu7UXQjonwNIopM8cHoAhkRGQXdfIiE0Ep8KalQoptRTiFFZiRlOo6TPuSk4zk97G9w5vidc77LPjNi84f1MN+391rrf9a+rL32N4xiFMcUjouo5zyciYPYH0FnBadiNiZiD2oR9JbGRdgiOFPDIXRhCWYU0Dcj6duV6BrQuyWxNaLowBcOO1Uv+7EKc4WINUIlabMq6dNI35eJzRHDWOzS2MEB6cd6XI/OQf07k2frkzat9HQnNkcUG7R2dECq2I53EtmePMvaf+MwcWqKu+RzuqhUcfcwcWqKTvmiXFQ2GDodRhQz0aN9ZHsSG0cVrkGf+GT7MG8YeeTCHeKS7sOdMR1stjcWxY2YH0nXh1gdSdf/E+2I8KVYigkl9ewVUsxNpT1qMzaKN4ejJxrtyEt7IuraE1EX2jOkp+JBnFxSzz68KuTqoyiK2BHuxDO4NpK+j/GoOAWF6BiH98Q/SHyCycPIIxMm4FPZCPTj30SynIFr+A7ThotMK4wXopA1Ym9gSiKv5Oj3bdKnFMpuS514E1fm6NMnbF098s3NS4QS0Ik5+hyBsoSXYkGO9jvxy6C/t+IPIYJZcBWW57AXFfMNrSo2kqqw2l4hvSzcIRTw1sm24FVxb5s4NcR0/JXBuUNYJttI6sDjsi1kvTgrGpsMjq3O4FQNa+SbNhWsyKj7I4wpzSYDbpFtKB/EOSn9ZwpRfx5Xp7yfhN0Z9FdxXxxKjTEe2zI4U8NnKf3PNrT2VcWTKe1eyGjjT+Eapm14IqMjNTyd0n9JSrsDwhmaEN2H8GMOO8viUjyMSfJVJh9O0bGoQdt1eFm2oVwve7UpC1ssX568KEXH6fghp54s8lRkrk7CjpxOrGqg6wQ8IKSKWXPpVtIt8ly+v4ATf2t+yqlgDl5SbCjXy8JIXFXweQEHqngxo43JeEw54l+JVLKaJeypRZzoFxavrIWG6cKPW2SO9+PCMkQHsLiA8fpIv5/DmUn4qaCtpWWIEiLzdUHj9XJA2H5uFRbBZriuoI1NSpatpio+nJtFvFvYd2c1sDsGvxfQ3a/knrwgMtm0qD8rPSprCuq8uRmhVqvanBbvm+EQfsNKIcnvTmnTiUdwQcq73oJ2L2v2stXx6vyCRr8RDuk/C8OMUK24J6VtBaekPG81zxuh0TTJhC7FhtUOHF+n61whGalvu8uRWVJFvgPEYOkqQzhLVSPPXLoYa4Xh3Stcls1NaTdb8Xx7ZxnCvSUIfy/kzWno0Pyzx3dL2C0695Hto7NGUhXy5Lzp3kLZKiqNpNTl2+YShgdIvyXbVck44TB/oKTNzWUIv13S+IDsFmpY84QvZAcwTbh4e04o18SwtbIM4dsiOTFYVgzSv7wN+m9vRqjV/PrA0JuCox1bhYNKQ7Qi3CcU1fpiedRG9AkLXhRfbxCnKlET0s21ifwaSWcPbopBdDDOwGtClTD2vCsq+/C68K8HmVDk7DhFyIsvFzKnGThN+689+oU9dptwQb5B+LB8dx4lMb7xqAhkJwo/xljhFFSfSdUc3mPrcbwj15P+pP0/QiR7hYSkGsHnUYziWMF/mXV4JVcZ8G0AAAAASUVORK5CYII="/>
                  -    </frame>
                  -);
                  -

                  tint#

                  -

                  图片着色,其值是一个颜色名称或RGB颜色值。使用该属性会将图片中的非透明区域都涂上同一颜色。可以用于改变图片的颜色。

                  -

                  例如,对于上面的base64的图片: <img w="40" h="40" tint="red" src="data:image/png;base64,..."/>,则钱包图标颜色会变成红色。

                  -

                  scaleType#

                  -

                  控制图片根据图片控件的宽高放缩时的模式。可选的值为:

                  -
                    -
                  • center 在控件中居中显示图像, 但不执行缩放。
                  • -
                  • centerCrop 保持图像的长宽比缩放图片, 使图像的尺寸 (宽度和高度) 等于或大于控件的相应尺寸 (不包括内边距padding)并且使图像在控件中居中显示。
                  • -
                  • centerInside 保持图像的长宽比缩放图片, 使图像的尺寸 (宽度和高度) 小于视图的相应尺寸 (不包括内边距padding)并且图像在控件中居中显示。
                  • -
                  • fitCenter 保持图像的长宽比缩放图片, 使图片的宽高和控件的宽高相同并使图片在控件中居中显示
                  • -
                  • fitEnd 保持图像的长宽比缩放图片, 使图片的宽高和控件的宽高相同并使图片在控件中靠右下角显示
                  • -
                  • fitStart 保持图像的长宽比缩放图片, 使图片的宽高和控件的宽高相同并使图片在控件靠左上角显示
                  • -
                  • fitXY 使图片和宽高和控件的宽高完全匹配,但图片的长宽比可能不能保持一致
                  • -
                  • matrix 绘制时使用图像矩阵进行缩放。需要在代码中使用setImageMatrix(Matrix)函数才能生效。
                  • +

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

                  +

                  UiSelector.enabled([b = true])#

                  +
                  -

                  默认的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的属性,用于指定布局的方向,可选的值为verticalhorizontal

                  -

                  例如<linear orientation="vertical"></linear>相当于<vertical></vertical>

                  -

                  线性布局的默认方向是横向的,因此,一个没有指定orientation属性的线性布局就是横向布局。

                  -

                  帧布局: frame#

                  -

                  帧布局

                  -

                  相对布局: relative#

                  -

                  勾选框控件: checkbox#

                  -

                  选择框控件: radio#

                  -

                  选择框布局: radiogroup#

                  -

                  开关控件: Switch#

                  -

                  开关控件用于表示一个选项是否被选中。

                  -

                  checked#

                  -

                  表示开关是否被选中。可选的值为:

                  -
                    -
                  • true 打开开关
                  • -
                  • false 关闭开关
                  • +

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

                  +

                  UiSelector.scrollable([b = true])#

                  +
                  -

                  text#

                  -

                  对开关进行描述的文字。

                  -

                  进度条控件: progressbar#

                  -

                  拖动条控件: seekbar#

                  -

                  下来菜单控件: spinner#

                  -

                  时间选择控件: timepicker#

                  -

                  日期选择控件: datepicker#

                  -

                  浮动按钮控件: fab#

                  -

                  标题栏控件: toolbar#

                  -

                  卡片: card#

                  -

                  卡片控件是一个拥有圆角、阴影的控件。

                  -

                  cardBackgroundColor#

                  -

                  卡片的背景颜色。

                  -

                  cardCornerRadius#

                  -

                  卡片的圆角半径。

                  -

                  cardElevation#

                  -

                  设置卡片在z轴上的高度,来控制阴影的大小。

                  -

                  contentPadding#

                  -

                  设置卡片的内边距。该属性包括四个值:

                  -
                    -
                  • contentPaddingLeft 左内边距
                  • -
                  • contentPaddingRight 右内边距
                  • -
                  • contentPaddingTop 上内边距
                  • -
                  • contentPaddingBottom 下内边距
                  • +

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

                  +

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

                  +
                  className("android.support.v7.widget.RecyclerView").scrollable().findOne().scrollForward();
                  +//或者classNameEndsWith("RecyclerView").scrollable().findOne().scrollForward();
                  +

                  UiSelector.editable([b = true])#

                  +
                  -

                  foreground#

                  -

                  使用foreground="?selectableItemBackground"属性可以为卡片添加点击效果。

                  -

                  抽屉布局: drawer#

                  -

                  列表: list#

                  -

                  Tab: tab#

                  -

                  ui#

                  -

                  ui.layout(xml)#

                  +

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

                  +

                  UiSelector.multiLine([b = true])#

                  +
                    +
                  • b <Boolean> 表示文本或输入框控件是否是多行显示的
                  • +
                  +

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

                  +

                  UiSelector.findOne()#

                  +
                  +

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

                  +

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

                  +

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

                  +

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

                  +

                  UiSelector.findOne(timeout)#

                  +
                  +

                  根据当前的选择器所确定的筛选条件,对屏幕上的控件进行搜索,直到屏幕上出现满足条件的一个控件为止,并返回该控件;如果在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()#

                  +
                  +

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

                  +

                  UiSelector.findOnce(i)#

                  +
                  +

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

                  +

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

                  +

                  UiSelector.find()#

                  +
                  +

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

                  +

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

                  +

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

                  +
                  var c = className("AbsListView").find();
                  +if(c.empty()){
                  +    toast("找到啦");
                  +}else{
                  +    toast("没找到╭(╯^╰)╮");
                  +}
                  +

                  UiSelector.untilFind()#

                  +
                  +

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

                  +

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

                  +

                  UiSelector.exists()#

                  +
                  +

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

                  +
                  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()#

                  +
                  +

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

                  +

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

                  +

                  UiObject.longClick()#

                  +
                  +

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

                  +

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

                  +

                  UiObject.setText(text)#

                  +
                  +

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

                  +

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

                  +

                  UiObject.copy()#

                  +
                  +

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

                  +

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

                  +
                  var et = className("EditText").findOne();
                  +//选中前两个字
                  +et.setSelection(0, 2);
                  +//对选中内容进行复制
                  +if(et.copy()){
                  +    toast("复制成功");
                  +}else{
                  +    toast("复制失败");
                  +}
                  +

                  UiObject.cut()#

                  +

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

                  +

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

                  +

                  UiObject.paste()#

                  +
                  +

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

                  +
                  //设置剪贴板内容为“你好”
                  +setClip("你好");
                  +var et = className("EditText").findOne();
                  +et.paste();
                  +

                  UiObject.setSelection(start, end)#

                  +
                  +

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

                  +

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

                  +

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

                  +

                  UiObject.scrollForward()#

                  +
                  +

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

                  +

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

                  +

                  UiObject.scrollBackward()#

                  +
                  +

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

                  +

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

                  +

                  UiObject.select()#

                  +
                  +

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

                  +

                  UiObject.collapse()#

                  +
                  +

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

                  +

                  UiObject.expand()#

                  +
                  +

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

                  +

                  UiObject.show()#

                  +

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

                  +

                  UiObject.scrollUp()#

                  +

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

                  +

                  UiObject.scrollDown()#

                  +

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

                  +

                  UiObject.scrollLeft()#

                  +

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

                  +

                  UiObject.scrollRight()#

                  +

                  children()#

                  +
                  +

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

                  +
                  className("AbsListView").findOne().children()
                  +    .forEach(function(child){
                  +        log(child.className());
                  +    });
                  +

                  childCount()#

                  +
                  +

                  返回子控件数目。

                  +

                  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()#

                    -
                  • xml <XML> | <string> 布局XML或者XML字符串
                  • +
                  • 返回 Rect
                  -

                  将布局XML渲染为视图(View)对象, 并设置为当前视图。

                  -

                  ui.layoutFile(xmlFile)#

                  +

                  返回控件在屏幕上的范围,其值是一个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()#

                  -

                  此函数和ui.layout相似,只不过允许传入一个xml文件路径来渲染布局。

                  -

                  ui.inflate(xml[, parent = null, attachToParent = false])#

                  +

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

                  +

                  drawingOrder()#

                    -
                  • xml <string> | <XML> 布局XML或者XML字符串
                  • -
                  • parent <View> 父视图
                  • -
                  • attachToParent <boolean> 是否渲染的View加到父视图中,默认为false
                  • -
                  • 返回 <View>
                  • +
                  • 返回 <number>
                  -

                  将布局XML渲染为视图(View)对象。如果该View将作为某个View的子View,我们建议传入parent参数,这样在渲染时依赖于父视图的一些布局属性能够正确应用。

                  -

                  此函数用于动态创建、显示View。

                  -
                  "ui";
                  -
                  -$ui.layout(
                  -    <linear id="container">
                  -    </linear>
                  -);
                  -
                  -// 动态创建3个文本控件,并加到container容器中
                  -// 这里仅为实例,实际上并不推荐这种做法,如果要展示列表,
                  -// 使用list组件;动态创建十几个、几十个View会让界面卡顿
                  -for (let i = 0; i < 3; i++) {
                  -    let textView = $ui.inflate(
                  -        <text textColor="#000000" textSize="14sp"/>
                  -    , $ui.container);
                  -    textView.attr("text", "文本控件" + i);
                  -    $ui.container.addView(textView);
                  -}
                  -
                  -

                  ui.registerWidget(name, widget)#

                  +

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

                  +

                  id()#

                  -

                  注册一个自定义组件。参考示例->界面控件->自定义控件。

                  -

                  ui.isUiThread()#

                  +

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

                  +

                  text()#

                  -

                  返回当前线程是否是UI线程。

                  -
                  "ui";
                  -
                  -log($ui.isUiThread()); // => true
                  -
                  -$threads.start(function () {
                  -    log($ui.isUiThread()); // => false
                  -});
                  -
                  -
                  -

                  ui.findView(id)#

                  +

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

                  +

                  findByText(str)#

                  -

                  在当前视图中根据ID查找相应的视图对象并返回。如果当前未设置视图或找不到此ID的视图时返回null

                  -

                  一般我们都是通过ui.xxx来获取id为xxx的控件,如果xxx是一个ui已经有的属性,就可以通过$ui.findView()来获取这个控件。

                  -

                  ui.finish()#

                  -

                  结束当前活动并销毁界面。

                  -

                  ui.setContentView(view)#

                  +

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

                  +

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

                  +

                  findOne(selector)#

                  -

                  将视图对象设置为当前视图。

                  -

                  ui.post(callback[, delay = 0])#

                  +

                  根据选择器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)#

                  -

                  callback加到UI线程的消息循环中,并延迟delay毫秒后执行(不能准确保证一定在delay毫秒后执行)。

                  -

                  此函数可以用于UI线程中延时执行动作(sleep不能在UI线程中使用),也可以用于子线程中更新UI。

                  -
                  "ui";
                  -
                  -ui.layout(
                  -    <frame>
                  -        <text id="result"/>
                  -    </frame>
                  -);
                  -
                  -ui.result.attr("text", "计算中");
                  -// 在子线程中计算1+ ... + 10000000
                  -threads.start({
                  -    let sum = 0;
                  -    for (let i = 0; i < 1000000; i++) {
                  -        sum += i;
                  +

                  根据选择器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());
                       }
                  -    // 由于不能在子线程操作UI,所以要抛到UI线程执行
                  -    ui.post(() => {
                  -        ui.result.attr("text", String(sum));
                  -    });
                   });
                  -
                  -

                  ui.run(callback)#

                  +

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

                  +
                  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()#

                  -

                  callback在UI线程中执行。如果当前已经在UI线程中,则直接执行callback;否则将callback抛到UI线程中执行(加到UI线程的消息循环的末尾),并等待callback执行结束(阻塞当前线程)

                  -

                  ui.statusBarColor(color)#

                  +

                  返回集合中的控件数。

                  +

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

                  +

                  UiCollection.get(i)#

                  -

                  设置当前界面的状态栏颜色。

                  -
                  "ui";
                  -ui.statusBarColor("#000000");
                  -
                  -

                  ui.useAndroidResources()#

                  -

                  启用使用Android的布局(layout)、绘图(drawable)、动画(anim)、样式(style)等资源的特性。启用该特性后,在project.json中进行以下配置,就可以像写Android原生一样写界面:

                  -
                  {
                  -    // ...
                  -    androidResources: {
                  -        "resDir": "res",  // 资源文件夹
                  -        "manifest": "AndroidManifest.xml" // AndroidManifest文件路径
                  -    }
                  -}
                  -
                  -

                  res文件夹通常为以下结构:

                  -
                  - res
                  -    - layout  // 布局资源
                  -    - drawable // 图片、形状等资源
                  -    - menu // 菜单资源
                  -    - values // 样式、字符串等资源
                  -    // ...
                  -

                  可参考示例->复杂界面->Android原生界面。

                  -

                  尺寸的单位: Dimension#

                  -

                  Drawables#

                  -

                  颜色#

                  -

                  (完善中...)

                  - +

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

                  +

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

                  +

                  UiCollection.each(func)#

                  +
                    +
                  • func <Function> 遍历函数,参数为UiObject。
                  • +
                  +

                  遍历集合。

                  +

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

                  +

                  empty()#

                  +
                  +

                  返回控件集合是否为空。

                  +

                  nonEmpty()#

                  +
                  +

                  返回控件集合是否非空。

                  +

                  UiCollection.find(selector)#

                  +
                  +

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

                  +

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

                  +

                  例如:

                  +
                  var names = id("name").find();
                  +//在集合
                  +var clickableNames = names.find(clickable());
                  +

                  UiCollection.findOne(selector)#

                  +
                  +

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

                  +

                  Rect#

                  +

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

                  +

                  Rect.left#

                  +
                  +

                  长方形左边界的x坐标、

                  +

                  Rect.right#

                  +
                  +

                  长方形右边界的x坐标、

                  +

                  Rect.top#

                  +
                  +

                  长方形上边界的y坐标、

                  +

                  Rect.bottom#

                  +
                  +

                  长方形下边界的y坐标、

                  +

                  Rect.centerX()#

                  +
                  +

                  长方形中点x坐标。

                  +

                  Rect.centerY()#

                  +
                  +

                  长方形中点y坐标。

                  +

                  Rect.width()#

                  +
                  +

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

                  +

                  Rect.height()#

                  +
                  +

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

                  +

                  Rect.contains(r)#

                  +
                  +

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

                  +

                  Rect.intersect(r)#

                  +
                  +

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

                  +

                  UiSelector进阶#

                  +

                  未完待续。 +

                  @@ -7474,4 +7665,4 @@

                  颜色#

                  - + \ No newline at end of file diff --git a/docs/app.html b/docs/app.html index 5570c4d..3895103 100644 --- a/docs/app.html +++ b/docs/app.html @@ -2,7 +2,7 @@ - App | Auto.js Pro-8.1.0 文档 + App | Auto.js Pro-8.2.0 文档 @@ -27,8 +27,9 @@
                • Overview - 综述
                • Q&A - 常见问题
                • App - 应用
                • +
                • Canvas - 画布
                • Console - 控制台
                • -
                • CoordinatesBasedAutomation - 基于坐标的操作
                • +
                • CoordinatesBasedAutomation - 基于坐标的操作
                • Crypto - 加解密与消息摘要
                • Device - 设备
                • Dialogs - 对话框
                • @@ -42,13 +43,13 @@
                • Keys - 按键模拟
                • Media - 多媒体
                • Modules - 模块
                • -
                • WidgetsBasedAutomation - 基于控件的操作
                • Sensors - 传感器
                • Shell - Shell命令
                • Storages - 本地存储
                • Threads - 多线程
                • Timers - 定时器
                • UI - 用户界面
                • +
                • WidgetsBasedAutomation - 基于控件的操作
                • Work with Java - 调用Java API
                @@ -62,7 +63,7 @@
              @@ -62,7 +63,7 @@
              -

              Auto.js Pro-8.1.0 文档

              +

              Auto.js Pro-8.2.0 文档

              索引 | @@ -76,10 +77,46 @@

              Auto.js Pro-8.1.0 文档

              目录

              @@ -87,7 +124,7 @@

              目录

              Canvas#

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

              -

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

              +

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

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

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

              var paint = new Paint();
              @@ -105,9 +142,132 @@ 

              Canvas#

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

              +

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

              +

              canvas.getWidth()#

              +
              +

              返回画布当前图层的宽度。

              +

              canvas.getHeight()#

              +
              +

              返回画布当前图层的高度。

              +

              canvas.drawRGB(r, int g, int b)#

              +
              +

              将整个可绘制区域填充为r、g、b指定的颜色。相当于 canvas.drawColor(colors.rgb(r, g, b))

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

              -

              canvas.draw#

              +
              +

              将整个可绘制区域填充为a、r、g、b指定的颜色。相当于 canvas.drawColor(colors.argb(a, r, g, b))

              +

              canvas.drawColor(color)#

              +
              +

              将整个可绘制区域填充为color指定的颜色。

              +

              canvas.drawColor(color, mode)#

              +
                +
              • color <number> 颜色值
              • +
              • mode <PorterDuff.Mode> Porter-Duff操作
              • +
              +

              将整个可绘制区域填充为color指定的颜色。

              +

              canvas.drawPaint(paint)#

              +
                +
              • paint <Paint> 画笔
              • +
              +

              将整个可绘制区域用paint指定的画笔填充。相当于绘制一个无限大的矩形,但是更快。 +通过该方法可以绘制一个指定的着色器的图案。

              +

              canvas.drawPoint(x, y, paint)#

              +
              +

              在可绘制区域绘制由坐标(x, y)指定的点。 +点的形状由画笔的线帽决定(参见paint.setStrokeCap(cap))。 +点的大小由画笔的宽度决定(参见paint.setStrokeWidth(width))。

              +
              +

              如果画笔宽度为0,则也会绘制1个像素至4个(若抗锯齿启用)。

              +
              +

              相当于 canvas.drawPoints([x, y], paint)

              +

              canvas.drawPoints(pts, paint)#

              +
                +
              • pts <Array> 点坐标数组 [x0, y0, x1, y1, x2, y2, ...]
              • +
              • paint <Paint> 画笔
              • +
              +

              在可绘制区域绘制由坐标数组指定的多个点。

              +

              canvas.drawLine(startX, startY, stopX, stopY, paint)#

              +
              +

              在可绘制区域绘制由起点坐标(startX, startY)和终点坐标(endX, endY)指定的线。 +绘制时会忽略画笔的样式(Style)。也就是说,即使样式设为“仅填充(FILL)”也会绘制。 +退化为点的线(长度为0)不会被绘制。

              +

              canvas.drawRect(r, paint)#

              +
                +
              • r <Rect> 矩形边界
              • +
              • paint <Paint> 画笔
              • +
              +

              在可绘制区域绘制由矩形边界r指定的矩形。 +绘制时画笔的样式(Style)决定了是否绘制矩形界线和填充矩形。

              +

              canvas.drawRect(left, top, right, bottom, android.graphics.Paint paint)#

              +
                +
              • left <number> 矩形左边界x坐标
              • +
              • top <number> 矩形上边界y坐标
              • +
              • right <number> 矩形右边界x坐标
              • +
              • bottom <number> 矩形下边界y坐标
              • +
              • paint <Paint> 画笔
              • +
              +

              在可绘制区域绘制由矩形边界(left, top, right, bottom)指定的矩形。 +绘制时画笔的样式(Style)决定了是否绘制矩形界线和填充矩形。

              +

              canvas.drawOval(android.graphics.RectF oval, android.graphics.Paint paint)#

              +

              canvas.drawOval(float left, float top, float right, float bottom, android.graphics.Paint paint)#

              +

              canvas.drawCircle(float cx, float cy, float radius, android.graphics.Paint paint)#

              +

              canvas.drawArc(android.graphics.RectF oval, float startAngle, float sweepAngle, boolean useCenter, android.graphics.Paint paint)#

              +

              canvas.drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, android.graphics.Paint paint)#

              +

              canvas.drawRoundRect(android.graphics.RectF rect, float rx, float ry, android.graphics.Paint paint)#

              +

              canvas.drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, android.graphics.Paint paint)#

              +

              canvas.drawPath(android.graphics.Path path, android.graphics.Paint paint)#

              +

              canvas.drawBitmap(android.graphics.Bitmap bitmap, float left, float top, android.graphics.Paint paint)#

              +

              canvas.drawText(java.lang.String text, float x, float y, android.graphics.Paint paint)#

              +

              canvas.drawTextOnPath(java.lang.String text, android.graphics.Path path, float hOffset, float vOffset, android.graphics.Paint paint)#

              +

              canvas.translate(dx, dy)#

              +
                +
              • dx <number> 向x轴正方向平移的距离,负数表示反方向平移
              • +
              • dy <number> 向y轴正方向平移的距离,负数表示反方向平移
              • +
              +

              平移指定距离。

              +

              canvas.scale(sx, sy)#

              +
                +
              • sx <number> 向x轴正方向平移的距离,负数表示反方向平移
              • +
              • sy <number> 向y轴正方向平移的距离,负数表示反方向平移
              • +
              +

              以原点为中心,将坐标系平移缩放指定倍数。

              +

              canvas.scale(float sx, float sy, float px, float py)#

              +

              canvas.rotate(float degrees)#

              +

              canvas.rotate(float degrees, float px, float py)#

              +

              canvas.skew(float sx, float sy)#

              +

              画笔#

              +

              变换矩阵#

              +

              路径#

              +

              Porter-Duff操作#

              +

              着色器#

              +

              遮罩过滤器#

              +

              颜色过滤器#

              +

              路径特效#

              +

              区域#

              diff --git a/docs/console.html b/docs/console.html index 6df312a..32b3d50 100644 --- a/docs/console.html +++ b/docs/console.html @@ -2,7 +2,7 @@ - Console | Auto.js Pro-8.1.0 文档 + Console | Auto.js Pro-8.2.0 文档 @@ -27,8 +27,9 @@
            • Overview - 综述
            • Q&A - 常见问题
            • App - 应用
            • +
            • Canvas - 画布
            • Console - 控制台
            • -
            • CoordinatesBasedAutomation - 基于坐标的操作
            • +
            • CoordinatesBasedAutomation - 基于坐标的操作
            • Crypto - 加解密与消息摘要
            • Device - 设备
            • Dialogs - 对话框
            • @@ -42,13 +43,13 @@
            • Keys - 按键模拟
            • Media - 多媒体
            • Modules - 模块
            • -
            • WidgetsBasedAutomation - 基于控件的操作
            • Sensors - 传感器
            • Shell - Shell命令
            • Storages - 本地存储
            • Threads - 多线程
            • Timers - 定时器
            • UI - 用户界面
            • +
            • WidgetsBasedAutomation - 基于控件的操作
            • Work with Java - 调用Java API
            @@ -62,7 +63,7 @@
            -

            Auto.js Pro-8.1.0 文档

            +

            Auto.js Pro-8.2.0 文档

            索引 | diff --git a/docs/coordinates-based-automation.html b/docs/coordinatesBasedAutomation.html similarity index 75% rename from docs/coordinates-based-automation.html rename to docs/coordinatesBasedAutomation.html index 81cb4e6..f18c077 100644 --- a/docs/coordinates-based-automation.html +++ b/docs/coordinatesBasedAutomation.html @@ -2,13 +2,13 @@ - 基于坐标的触摸模拟 | Auto.js Pro-8.1.0 文档 + 基于坐标的触摸模拟 | Auto.js Pro-8.2.0 文档 - + - +

          @@ -60,9 +61,9 @@ -
          +
          -

          Auto.js Pro-8.1.0 文档

          +

          Auto.js Pro-8.2.0 文档

          索引 | @@ -75,32 +76,32 @@

          Auto.js Pro-8.1.0 文档

          -

          基于坐标的触摸模拟#

          +

          基于坐标的触摸模拟#

          Stability: 2 - Stable

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

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

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

          @@ -118,7 +119,7 @@

          基于坐标的触摸模拟#

          +

          setScreenMetrics(width, height)#

          @@ -62,7 +63,7 @@
          -

          Auto.js Pro-8.1.0 文档

          +

          Auto.js Pro-8.2.0 文档

        @@ -62,7 +63,7 @@
        -

        Auto.js Pro-8.1.0 文档

        +

        Auto.js Pro-8.2.0 文档

        索引 | @@ -115,7 +116,7 @@

        sleep(n)#
      • n <number> 毫秒数

      暂停运行n毫秒的时间。1秒等于1000毫秒。

      -
      //暂停5毫秒
      +
      //暂停5秒
       sleep(5000);
       

      currentPackage()#

      @@ -62,7 +63,7 @@
    @@ -62,7 +63,7 @@
    -

    Auto.js Pro-8.1.0 文档

    +

    Auto.js Pro-8.2.0 文档

    索引 | diff --git a/docs/index.html b/docs/index.html index 50206b1..b890444 100644 --- a/docs/index.html +++ b/docs/index.html @@ -2,7 +2,7 @@ - Index | Auto.js Pro-8.1.0 文档 + Index | Auto.js Pro-8.2.0 文档 @@ -27,8 +27,9 @@

  • Overview - 综述
  • Q&A - 常见问题
  • App - 应用
  • +
  • Canvas - 画布
  • Console - 控制台
  • -
  • CoordinatesBasedAutomation - 基于坐标的操作
  • +
  • CoordinatesBasedAutomation - 基于坐标的操作
  • Crypto - 加解密与消息摘要
  • Device - 设备
  • Dialogs - 对话框
  • @@ -42,13 +43,13 @@
  • Keys - 按键模拟
  • Media - 多媒体
  • Modules - 模块
  • -
  • WidgetsBasedAutomation - 基于控件的操作
  • Sensors - 传感器
  • Shell - Shell命令
  • Storages - 本地存储
  • Threads - 多线程
  • Timers - 定时器
  • UI - 用户界面
  • +
  • WidgetsBasedAutomation - 基于控件的操作
  • Work with Java - 调用Java API
  • @@ -62,7 +63,7 @@
    -

    Auto.js Pro-8.1.0 文档

    +

    Auto.js Pro-8.2.0 文档

    索引 | @@ -89,8 +90,9 @@

    目录

  • Overview - 综述
  • Q&A - 常见问题
  • App - 应用
  • +
  • Canvas - 画布
  • Console - 控制台
  • -
  • CoordinatesBasedAutomation - 基于坐标的操作
  • +
  • CoordinatesBasedAutomation - 基于坐标的操作
  • Crypto - 加解密与消息摘要
  • Device - 设备
  • Dialogs - 对话框
  • @@ -104,13 +106,13 @@

    目录

  • Keys - 按键模拟
  • Media - 多媒体
  • Modules - 模块
  • -
  • WidgetsBasedAutomation - 基于控件的操作
  • Sensors - 传感器
  • Shell - Shell命令
  • Storages - 本地存储
  • Threads - 多线程
  • Timers - 定时器
  • UI - 用户界面
  • +
  • WidgetsBasedAutomation - 基于控件的操作
  • Work with Java - 调用Java API
  • diff --git a/docs/keys.html b/docs/keys.html index a99affa..ecd8686 100644 --- a/docs/keys.html +++ b/docs/keys.html @@ -2,7 +2,7 @@ - Keys | Auto.js Pro-8.1.0 文档 + Keys | Auto.js Pro-8.2.0 文档 @@ -27,8 +27,9 @@
  • Overview - 综述
  • Q&A - 常见问题
  • App - 应用
  • +
  • Canvas - 画布
  • Console - 控制台
  • -
  • CoordinatesBasedAutomation - 基于坐标的操作
  • +
  • CoordinatesBasedAutomation - 基于坐标的操作
  • Crypto - 加解密与消息摘要
  • Device - 设备
  • Dialogs - 对话框
  • @@ -42,13 +43,13 @@
  • Keys - 按键模拟
  • Media - 多媒体
  • Modules - 模块
  • -
  • WidgetsBasedAutomation - 基于控件的操作
  • Sensors - 传感器
  • Shell - Shell命令
  • Storages - 本地存储
  • Threads - 多线程
  • Timers - 定时器
  • UI - 用户界面
  • +
  • WidgetsBasedAutomation - 基于控件的操作
  • Work with Java - 调用Java API
  • @@ -62,7 +63,7 @@
    -

    Auto.js Pro-8.1.0 文档

    +

    Auto.js Pro-8.2.0 文档

    索引 | diff --git a/docs/media.html b/docs/media.html index 90b4ac9..7e19db9 100644 --- a/docs/media.html +++ b/docs/media.html @@ -2,7 +2,7 @@ - Media | Auto.js Pro-8.1.0 文档 + Media | Auto.js Pro-8.2.0 文档 @@ -27,8 +27,9 @@

  • Overview - 综述
  • Q&A - 常见问题
  • App - 应用
  • +
  • Canvas - 画布
  • Console - 控制台
  • -
  • CoordinatesBasedAutomation - 基于坐标的操作
  • +
  • CoordinatesBasedAutomation - 基于坐标的操作
  • Crypto - 加解密与消息摘要
  • Device - 设备
  • Dialogs - 对话框
  • @@ -42,13 +43,13 @@
  • Keys - 按键模拟
  • Media - 多媒体
  • Modules - 模块
  • -
  • WidgetsBasedAutomation - 基于控件的操作
  • Sensors - 传感器
  • Shell - Shell命令
  • Storages - 本地存储
  • Threads - 多线程
  • Timers - 定时器
  • UI - 用户界面
  • +
  • WidgetsBasedAutomation - 基于控件的操作
  • Work with Java - 调用Java API
  • @@ -62,7 +63,7 @@
    -

    Auto.js Pro-8.1.0 文档

    +

    Auto.js Pro-8.2.0 文档

    索引 | diff --git a/docs/modules.html b/docs/modules.html index 185c2d8..9089000 100644 --- a/docs/modules.html +++ b/docs/modules.html @@ -2,7 +2,7 @@ - module (模块) | Auto.js Pro-8.1.0 文档 + module (模块) | Auto.js Pro-8.2.0 文档 @@ -27,8 +27,9 @@

  • Overview - 综述
  • Q&A - 常见问题
  • App - 应用
  • +
  • Canvas - 画布
  • Console - 控制台
  • -
  • CoordinatesBasedAutomation - 基于坐标的操作
  • +
  • CoordinatesBasedAutomation - 基于坐标的操作
  • Crypto - 加解密与消息摘要
  • Device - 设备
  • Dialogs - 对话框
  • @@ -42,13 +43,13 @@
  • Keys - 按键模拟
  • Media - 多媒体
  • Modules - 模块
  • -
  • WidgetsBasedAutomation - 基于控件的操作
  • Sensors - 传感器
  • Shell - Shell命令
  • Storages - 本地存储
  • Threads - 多线程
  • Timers - 定时器
  • UI - 用户界面
  • +
  • WidgetsBasedAutomation - 基于控件的操作
  • Work with Java - 调用Java API
  • @@ -62,7 +63,7 @@
    -

    Auto.js Pro-8.1.0 文档

    +

    Auto.js Pro-8.2.0 文档

    索引 | diff --git a/docs/overview.html b/docs/overview.html index f44af93..073408f 100644 --- a/docs/overview.html +++ b/docs/overview.html @@ -2,7 +2,7 @@ - 综述 | Auto.js Pro-8.1.0 文档 + 综述 | Auto.js Pro-8.2.0 文档 @@ -27,8 +27,9 @@

  • Overview - 综述
  • Q&A - 常见问题
  • App - 应用
  • +
  • Canvas - 画布
  • Console - 控制台
  • -
  • CoordinatesBasedAutomation - 基于坐标的操作
  • +
  • CoordinatesBasedAutomation - 基于坐标的操作
  • Crypto - 加解密与消息摘要
  • Device - 设备
  • Dialogs - 对话框
  • @@ -42,13 +43,13 @@
  • Keys - 按键模拟
  • Media - 多媒体
  • Modules - 模块
  • -
  • WidgetsBasedAutomation - 基于控件的操作
  • Sensors - 传感器
  • Shell - Shell命令
  • Storages - 本地存储
  • Threads - 多线程
  • Timers - 定时器
  • UI - 用户界面
  • +
  • WidgetsBasedAutomation - 基于控件的操作
  • Work with Java - 调用Java API
  • @@ -62,7 +63,7 @@
    -

    Auto.js Pro-8.1.0 文档

    +

    Auto.js Pro-8.2.0 文档

    索引 | @@ -87,6 +88,7 @@

    综述#

  • 因为Auto.js是基于JavaScript的,学习Auto.js的API之前建议先学习JavaScript的基本语法和内置对象,可以使用教程前面的两个JavaScript教程链接来学习。
  • 如果您想要使用TypeScript来开发,目前已经有开发者公布了一个可以把使用TypeScript进行Auto.js开发的工具,参见Auto.js DevTools
  • 如果想要在电脑而不是手机上开发Auto.js,可以使用VS Code以及相应的Auto.js插件使得在电脑上编辑的脚本能推送到手机运行,参见Auto.js-VSCode-Extension,Auto.js Pro用户则需要使用Auto.js-Pro-Ext
  • +
  • 支持Node.js已在计划中,预计2020年底在Auto.js Pro中支持
  • 本文档的章节大致上是以模块来分的,总体上可以分成"自动操作"类模块(控件操作、触摸模拟、按键模拟等)和其他类模块(设备、应用、界面等)。

    "自动操作"的部分又可以大致分为基于控件和基于坐标的操作。基于坐标的操作是传统按键精灵、触摸精灵等脚本软件采用的方式,通过屏幕坐标来点击、长按指定位置模拟操作,从而到达目的。例如click(100, 200), press(100, 200, 500)等。这种方式在游戏类脚本中比较有可行性,结合找图找色、坐标放缩功能也能达到较好的兼容性。但是,这种方式对一般软件脚本却难以达到想要的效果,而且这种方式需要安卓7.0版本以上或者root权限才能执行。所以对于一般软件脚本(例如批量添加联系人、自动提取短信验证码等等),我们采用基于控件的模拟操作方式,结合通知事情、按键事情等达成更好的工作流。这些部分的文档参见基于控件的操作基于坐标的操作

    diff --git a/docs/qa.html b/docs/qa.html index 9f9865d..0b687cd 100644 --- a/docs/qa.html +++ b/docs/qa.html @@ -2,7 +2,7 @@ - Q & A | Auto.js Pro-8.1.0 文档 + Q & A | Auto.js Pro-8.2.0 文档 @@ -27,8 +27,9 @@
  • Overview - 综述
  • Q&A - 常见问题
  • App - 应用
  • +
  • Canvas - 画布
  • Console - 控制台
  • -
  • CoordinatesBasedAutomation - 基于坐标的操作
  • +
  • CoordinatesBasedAutomation - 基于坐标的操作
  • Crypto - 加解密与消息摘要
  • Device - 设备
  • Dialogs - 对话框
  • @@ -42,13 +43,13 @@
  • Keys - 按键模拟
  • Media - 多媒体
  • Modules - 模块
  • -
  • WidgetsBasedAutomation - 基于控件的操作
  • Sensors - 传感器
  • Shell - Shell命令
  • Storages - 本地存储
  • Threads - 多线程
  • Timers - 定时器
  • UI - 用户界面
  • +
  • WidgetsBasedAutomation - 基于控件的操作
  • Work with Java - 调用Java API
  • @@ -62,7 +63,7 @@
    -

    Auto.js Pro-8.1.0 文档

    +

    Auto.js Pro-8.2.0 文档

    索引 | diff --git a/docs/sensors.html b/docs/sensors.html index cc8718f..697bb7b 100644 --- a/docs/sensors.html +++ b/docs/sensors.html @@ -2,7 +2,7 @@ - Sensors | Auto.js Pro-8.1.0 文档 + Sensors | Auto.js Pro-8.2.0 文档 @@ -27,8 +27,9 @@

  • Overview - 综述
  • Q&A - 常见问题
  • App - 应用
  • +
  • Canvas - 画布
  • Console - 控制台
  • -
  • CoordinatesBasedAutomation - 基于坐标的操作
  • +
  • CoordinatesBasedAutomation - 基于坐标的操作
  • Crypto - 加解密与消息摘要
  • Device - 设备
  • Dialogs - 对话框
  • @@ -42,13 +43,13 @@
  • Keys - 按键模拟
  • Media - 多媒体
  • Modules - 模块
  • -
  • WidgetsBasedAutomation - 基于控件的操作
  • Sensors - 传感器
  • Shell - Shell命令
  • Storages - 本地存储
  • Threads - 多线程
  • Timers - 定时器
  • UI - 用户界面
  • +
  • WidgetsBasedAutomation - 基于控件的操作
  • Work with Java - 调用Java API
  • @@ -62,7 +63,7 @@
    -

    Auto.js Pro-8.1.0 文档

    +

    Auto.js Pro-8.2.0 文档

    索引 | diff --git a/docs/shell.html b/docs/shell.html index aac4f67..3192938 100644 --- a/docs/shell.html +++ b/docs/shell.html @@ -2,7 +2,7 @@ - shell函数 | Auto.js Pro-8.1.0 文档 + shell函数 | Auto.js Pro-8.2.0 文档 @@ -27,8 +27,9 @@

  • Overview - 综述
  • Q&A - 常见问题
  • App - 应用
  • +
  • Canvas - 画布
  • Console - 控制台
  • -
  • CoordinatesBasedAutomation - 基于坐标的操作
  • +
  • CoordinatesBasedAutomation - 基于坐标的操作
  • Crypto - 加解密与消息摘要
  • Device - 设备
  • Dialogs - 对话框
  • @@ -42,13 +43,13 @@
  • Keys - 按键模拟
  • Media - 多媒体
  • Modules - 模块
  • -
  • WidgetsBasedAutomation - 基于控件的操作
  • Sensors - 传感器
  • Shell - Shell命令
  • Storages - 本地存储
  • Threads - 多线程
  • Timers - 定时器
  • UI - 用户界面
  • +
  • WidgetsBasedAutomation - 基于控件的操作
  • Work with Java - 调用Java API
  • @@ -62,7 +63,7 @@
    -

    Auto.js Pro-8.1.0 文档

    +

    Auto.js Pro-8.2.0 文档

    索引 | diff --git a/docs/storages.html b/docs/storages.html index 72db4aa..1c6b768 100644 --- a/docs/storages.html +++ b/docs/storages.html @@ -2,7 +2,7 @@ - Storages | Auto.js Pro-8.1.0 文档 + Storages | Auto.js Pro-8.2.0 文档 @@ -27,8 +27,9 @@

  • Overview - 综述
  • Q&A - 常见问题
  • App - 应用
  • +
  • Canvas - 画布
  • Console - 控制台
  • -
  • CoordinatesBasedAutomation - 基于坐标的操作
  • +
  • CoordinatesBasedAutomation - 基于坐标的操作
  • Crypto - 加解密与消息摘要
  • Device - 设备
  • Dialogs - 对话框
  • @@ -42,13 +43,13 @@
  • Keys - 按键模拟
  • Media - 多媒体
  • Modules - 模块
  • -
  • WidgetsBasedAutomation - 基于控件的操作
  • Sensors - 传感器
  • Shell - Shell命令
  • Storages - 本地存储
  • Threads - 多线程
  • Timers - 定时器
  • UI - 用户界面
  • +
  • WidgetsBasedAutomation - 基于控件的操作
  • Work with Java - 调用Java API
  • @@ -62,7 +63,7 @@
    -

    Auto.js Pro-8.1.0 文档

    +

    Auto.js Pro-8.2.0 文档

    索引 | diff --git a/docs/threads.html b/docs/threads.html index ab7ec73..d3ba21f 100644 --- a/docs/threads.html +++ b/docs/threads.html @@ -2,7 +2,7 @@ - Threads | Auto.js Pro-8.1.0 文档 + Threads | Auto.js Pro-8.2.0 文档 @@ -27,8 +27,9 @@

  • Overview - 综述
  • Q&A - 常见问题
  • App - 应用
  • +
  • Canvas - 画布
  • Console - 控制台
  • -
  • CoordinatesBasedAutomation - 基于坐标的操作
  • +
  • CoordinatesBasedAutomation - 基于坐标的操作
  • Crypto - 加解密与消息摘要
  • Device - 设备
  • Dialogs - 对话框
  • @@ -42,13 +43,13 @@
  • Keys - 按键模拟
  • Media - 多媒体
  • Modules - 模块
  • -
  • WidgetsBasedAutomation - 基于控件的操作
  • Sensors - 传感器
  • Shell - Shell命令
  • Storages - 本地存储
  • Threads - 多线程
  • Timers - 定时器
  • UI - 用户界面
  • +
  • WidgetsBasedAutomation - 基于控件的操作
  • Work with Java - 调用Java API
  • @@ -62,7 +63,7 @@
    -

    Auto.js Pro-8.1.0 文档

    +

    Auto.js Pro-8.2.0 文档

    索引 | diff --git a/docs/timers.html b/docs/timers.html index 309698d..8ef6657 100644 --- a/docs/timers.html +++ b/docs/timers.html @@ -2,7 +2,7 @@ - Timers | Auto.js Pro-8.1.0 文档 + Timers | Auto.js Pro-8.2.0 文档 @@ -27,8 +27,9 @@

  • Overview - 综述
  • Q&A - 常见问题
  • App - 应用
  • +
  • Canvas - 画布
  • Console - 控制台
  • -
  • CoordinatesBasedAutomation - 基于坐标的操作
  • +
  • CoordinatesBasedAutomation - 基于坐标的操作
  • Crypto - 加解密与消息摘要
  • Device - 设备
  • Dialogs - 对话框
  • @@ -42,13 +43,13 @@
  • Keys - 按键模拟
  • Media - 多媒体
  • Modules - 模块
  • -
  • WidgetsBasedAutomation - 基于控件的操作
  • Sensors - 传感器
  • Shell - Shell命令
  • Storages - 本地存储
  • Threads - 多线程
  • Timers - 定时器
  • UI - 用户界面
  • +
  • WidgetsBasedAutomation - 基于控件的操作
  • Work with Java - 调用Java API
  • @@ -62,7 +63,7 @@
    -

    Auto.js Pro-8.1.0 文档

    +

    Auto.js Pro-8.2.0 文档

    索引 | diff --git a/docs/ui.html b/docs/ui.html index 164477a..078e4ef 100644 --- a/docs/ui.html +++ b/docs/ui.html @@ -2,7 +2,7 @@ - 用户界面: UI | Auto.js Pro-8.1.0 文档 + 用户界面: UI | Auto.js Pro-8.2.0 文档 @@ -27,8 +27,9 @@

  • Overview - 综述
  • Q&A - 常见问题
  • App - 应用
  • +
  • Canvas - 画布
  • Console - 控制台
  • -
  • CoordinatesBasedAutomation - 基于坐标的操作
  • +
  • CoordinatesBasedAutomation - 基于坐标的操作
  • Crypto - 加解密与消息摘要
  • Device - 设备
  • Dialogs - 对话框
  • @@ -42,13 +43,13 @@
  • Keys - 按键模拟
  • Media - 多媒体
  • Modules - 模块
  • -
  • WidgetsBasedAutomation - 基于控件的操作
  • Sensors - 传感器
  • Shell - Shell命令
  • Storages - 本地存储
  • Threads - 多线程
  • Timers - 定时器
  • UI - 用户界面
  • +
  • WidgetsBasedAutomation - 基于控件的操作
  • Work with Java - 调用Java API
  • @@ -62,7 +63,7 @@
    -

    Auto.js Pro-8.1.0 文档

    +

    Auto.js Pro-8.2.0 文档

    索引 | diff --git a/docs/util.html b/docs/util.html index 0d69f2a..8d88d3b 100644 --- a/docs/util.html +++ b/docs/util.html @@ -2,7 +2,7 @@ - Util | Auto.js Pro-8.1.0 文档 + Util | Auto.js Pro-8.2.0 文档 @@ -27,8 +27,9 @@

  • Overview - 综述
  • Q&A - 常见问题
  • App - 应用
  • +
  • Canvas - 画布
  • Console - 控制台
  • -
  • CoordinatesBasedAutomation - 基于坐标的操作
  • +
  • CoordinatesBasedAutomation - 基于坐标的操作
  • Crypto - 加解密与消息摘要
  • Device - 设备
  • Dialogs - 对话框
  • @@ -42,13 +43,13 @@
  • Keys - 按键模拟
  • Media - 多媒体
  • Modules - 模块
  • -
  • WidgetsBasedAutomation - 基于控件的操作
  • Sensors - 传感器
  • Shell - Shell命令
  • Storages - 本地存储
  • Threads - 多线程
  • Timers - 定时器
  • UI - 用户界面
  • +
  • WidgetsBasedAutomation - 基于控件的操作
  • Work with Java - 调用Java API
  • @@ -62,7 +63,7 @@
    -

    Auto.js Pro-8.1.0 文档

    +

    Auto.js Pro-8.2.0 文档

    索引 | diff --git a/docs/widgets-based-automation.html b/docs/widgetsBasedAutomation.html similarity index 70% rename from docs/widgets-based-automation.html rename to docs/widgetsBasedAutomation.html index e1fd1bd..ced9512 100644 --- a/docs/widgets-based-automation.html +++ b/docs/widgetsBasedAutomation.html @@ -2,13 +2,13 @@ - 基于控件的操作 | Auto.js Pro-8.1.0 文档 + 基于控件的操作 | Auto.js Pro-8.2.0 文档 - + - +

    -
    +
    -

    Auto.js Pro-8.1.0 文档

    +

    Auto.js Pro-8.2.0 文档

    索引 | @@ -75,146 +76,146 @@

    Auto.js Pro-8.1.0 文档

    目录

    -

    基于控件的操作#

    +

    基于控件的操作#

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

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

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

    -

    auto([mode])#

    +

    auto([mode])#

    @@ -229,10 +230,10 @@

    auto([mode])auto("fast");

    示例2:

    auto();
    -

    auto.waitFor()#

    +

    auto.waitFor()#

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

    因为该函数是阻塞的,因此除非是有协程特性,否则不能在ui模式下运行该函数,建议在ui模式下使用auto()函数。

    -

    auto.setMode(mode)#

    +

    auto.setMode(mode)#

    @@ -241,7 +242,7 @@

    auto.setMode(mode)#

    +

    auto.setFlags(flags)#

    [v4.1.0新增]

    • flags <string> | <Array> 一些标志,来启用和禁用某些特性,包括:
        @@ -253,21 +254,21 @@

        auto.setFlags(flags)#

        +

        auto.serivce#

        [v4.1.0新增]

        获取无障碍服务。如果无障碍服务没有启动,则返回null

        参见AccessibilityService

        -

        auto.windows#

        +

        auto.windows#

        [v4.1.0新增]

        当前所有窗口(AccessibilityWindowInfo)的数组,可能包括状态栏、输入法、当前应用窗口,弹出窗口、悬浮窗、分屏应用窗口等。可以分别获取每个窗口的布局信息。

        该函数需要Android 5.0以上才能运行。

        -

        auto.root#

        +

        auto.root#

        [v4.1.0新增]

        • <UiObject>
        • @@ -275,13 +276,13 @@

          auto.root当前窗口的布局根元素。如果无障碍服务未启动或者WindowFilter均返回false,则会返回null

          如果不设置windowFilter,则当前窗口即为活跃的窗口(获取到焦点、正在触摸的窗口);如果设置了windowFilter,则获取的是过滤的窗口中的第一个窗口。

          如果系统是Android5.0以下,则始终返回当前活跃的窗口的布局根元素。

          -

          auto.rootInActiveWindow#

          +

          auto.rootInActiveWindow#

          [v4.1.0新增]

          • <UiObject>

          当前活跃的窗口(获取到焦点、正在触摸的窗口)的布局根元素。如果无障碍服务未启动则为null

          -

          auto.setWindowFilter(filter)#

          +

          auto.setWindowFilter(filter)#

          [v4.1.0新增]

          • filter <Function> 参数为窗口(AccessibilityWindowInfo),返回值为Boolean的函数。
          • @@ -300,16 +301,16 @@

            auto.setWindowFilter(filter)#

            +

            auto.windowRoots#

            [v4.1.0新增]

            返回当前被WindowFilter过滤的窗口的布局根元素组成的数组。

            如果系统是Android5.0以下,则始终返回当前活跃的窗口的布局根元素的数组。

            -

            SimpleActionAutomator#

            +

            SimpleActionAutomator#

            Stability: 2 - Stable

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

            -

            click(text[, i])#

            +

            click(text[, i])#

            • text <string> 要点击的文本
            • i <number> 如果相同的文本在屏幕中出现多次,则i表示要点击第几个文本, i从0开始计算
            • @@ -322,7 +323,7 @@

              click(text[, i])#

              +

              click(left, top, bottom, right)#

              • left <number> 要点击的长方形区域左边与屏幕左边的像素距离
              • top <number> 要点击的长方形区域上边与屏幕上边的像素距离
              • @@ -334,28 +335,28 @@

                click(left, top, bottom, right)#

                +

                longClick(text[, i]))#

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

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

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

                -

                scrollUp([i])#

                +

                scrollUp([i])#

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

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

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

                -

                scrollDown([i])#

                +

                scrollDown([i])#

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

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

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

                -

                setText([i, ]text)#

                +

                setText([i, ]text)#

                • i <number> 表示要输入的为第i + 1个输入框
                • text <string> 要输入的文本
                • @@ -363,14 +364,14 @@

                  setText([i, ]text)#

                  +

                  input([i, ]text)#

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

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

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

                  -

                  UiSelector#

                  +

                  UiSelector#

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

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

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

                  @@ -432,14 +433,14 @@

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

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

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

                  -

                  selector()#

                  +

                  selector()#

                  • 返回 <UiSelector>

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

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

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

                  -

                  UiSelector.algorithm(algorithm)#

                  +

                  UiSelector.algorithm(algorithm)#

                  [v4.1.0新增]

                  • algorithm <string> 搜索算法,可选的值有:
                      @@ -451,38 +452,38 @@

                      UiSelector.algorithm(algorithm)#

                      +

                      UiSelector.text(str)#

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

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

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

                      -

                      UiSelector.textContains(str)#

                      +

                      UiSelector.textContains(str)#

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

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

                      -

                      UiSelector.textStartsWith(prefix)#

                      +

                      UiSelector.textStartsWith(prefix)#

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

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

                      -

                      UiSelector.textEndsWith(suffix)#

                      +

                      UiSelector.textEndsWith(suffix)#

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

                      -

                      UiSelector.textMatches(reg)#

                      +

                      UiSelector.textMatches(reg)#

                      • reg <string> | <Regex> 要满足的正则表达式。

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

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

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

                      -

                      UiSelector.desc(str)#

                      +

                      UiSelector.desc(str)#

                      • str <string> 控件文本
                      • 返回 <UiSelector> 返回选择器自身以便链式调用
                      • @@ -490,58 +491,58 @@

                        UiSelector.desc(str)#

                        +

                        UiSelector.descContains(str)#

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

                        -

                        UiSelector.descStartsWith(prefix)#

                        +

                        UiSelector.descStartsWith(prefix)#

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

                        -

                        UiSelector.descEndsWith(suffix)#

                        +

                        UiSelector.descEndsWith(suffix)#

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

                        -

                        UiSelector.descMatches(reg)#

                        +

                        UiSelector.descMatches(reg)#

                        • reg <string> | <Regex> 要满足的正则表达式。

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

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

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

                        -

                        UiSelector.id(resId)#

                        +

                        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)#

                        +

                        UiSelector.idContains(str)#

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

                        -

                        UiSelector.idStartsWith(prefix)#

                        +

                        UiSelector.idStartsWith(prefix)#

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

                        -

                        UiSelector.idEndsWith(suffix)#

                        +

                        UiSelector.idEndsWith(suffix)#

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

                        -

                        UiSelector.idMatches(reg)#

                        +

                        UiSelector.idMatches(reg)#

                        • reg <Regex> | <string> id要满足的正则表达式

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

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

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

                        UiSelector.className(str)#

                        +

                        UiSelector.className(str)#

                        • str <string> 控件文本
                        • 返回 <UiSelector> 返回选择器自身以便链式调用
                        • @@ -562,29 +563,29 @@

                          UiSelector.idMatches(reg)#

                          +

                          UiSelector.classNameContains(str)#

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

                          -

                          UiSelector.classNameStartsWith(prefix)#

                          +

                          UiSelector.classNameStartsWith(prefix)#

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

                          -

                          UiSelector.classNameEndsWith(suffix)#

                          +

                          UiSelector.classNameEndsWith(suffix)#

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

                          -

                          UiSelector.classNameMatches(reg)#

                          +

                          UiSelector.classNameMatches(reg)#

                          • reg <string> | <Regex> 要满足的正则表达式。

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

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

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

                          -

                          UiSelector.packageName(str)#

                          +

                          UiSelector.packageName(str)#

                          • str <string> 控件文本
                          • 返回 <UiSelector> 返回选择器自身以便链式调用
                          • @@ -592,28 +593,28 @@

                            UiSelector.packageName(str)#

                            +

                            UiSelector.packageNameContains(str)#

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

                            -

                            UiSelector.packageNameStartsWith(prefix)#

                            +

                            UiSelector.packageNameStartsWith(prefix)#

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

                            -

                            UiSelector.packageNameEndsWith(suffix)#

                            +

                            UiSelector.packageNameEndsWith(suffix)#

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

                            -

                            UiSelector.packageNameMatches(reg)#

                            +

                            UiSelector.packageNameMatches(reg)#

                            • reg <string> | <Regex> 要满足的正则表达式。

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

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

                            -

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

                            +

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

                            • left <number> 控件左边缘与屏幕左边的距离
                            • top <number> 控件上边缘与屏幕上边的距离
                            • @@ -623,7 +624,7 @@

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

                              +

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

                              • left <number> 范围左边缘与屏幕左边的距离
                              • top <number> 范围上边缘与屏幕上边的距离
                              • @@ -635,7 +636,7 @@

                                UiSelector.boundsInside(left, top, right, buttom)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)#

                                +

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

                                • left <number> 范围左边缘与屏幕左边的距离
                                • top <number> 范围上边缘与屏幕上边的距离
                                • @@ -646,40 +647,40 @@

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

                                  var w = boundsContains(500, 300, 500, 300).clickable().findOne();
                                   w.click();
                                  -

                                  UiSelector.drawingOrder(order)#

                                  +

                                  UiSelector.drawingOrder(order)#

                                  • order <number> 控件在父视图中的绘制顺序

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

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

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

                                  -

                                  UiSelector.clickable([b = true])#

                                  +

                                  UiSelector.clickable([b = true])#

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

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

                                  -

                                  UiSelector.longClickable([b = true])#

                                  +

                                  UiSelector.longClickable([b = true])#

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

                                  -

                                  UiSelector.checkable([b = true])#

                                  +

                                  UiSelector.checkable([b = true])#

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

                                  -

                                  UiSelector.selected([b = true])#

                                  +

                                  UiSelector.selected([b = true])#

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

                                  -

                                  UiSelector.enabled([b = true])#

                                  +

                                  UiSelector.enabled([b = true])#

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

                                  -

                                  UiSelector.scrollable([b = true])#

                                  +

                                  UiSelector.scrollable([b = true])#

                                  @@ -687,17 +688,17 @@

                                  UiSelector.scrollable([b = true])#

                                  +

                                  UiSelector.editable([b = true])#

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

                                  -

                                  UiSelector.multiLine([b = true])#

                                  +

                                  UiSelector.multiLine([b = true])#

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

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

                                  -

                                  UiSelector.findOne()#

                                  +

                                  UiSelector.findOne()#

                                  @@ -705,7 +706,7 @@

                                  UiSelector.findOne()#

                                  +

                                  UiSelector.findOne(timeout)#

                                  • timeout <number> 搜索的超时时间,单位毫秒
                                  • 返回 UiObject
                                  • @@ -724,18 +725,18 @@

                                    UiSelector.findOne(timeout)#

                                    +

                                    UiSelector.findOnce()#

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

                                    -

                                    UiSelector.findOnce(i)#

                                    +

                                    UiSelector.findOnce(i)#

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

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

                                    -

                                    UiSelector.find()#

                                    +

                                    UiSelector.find()#

                                    @@ -748,13 +749,13 @@

                                    UiSelector.find()#

                                    +

                                    UiSelector.untilFind()#

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

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

                                    -

                                    UiSelector.exists()#

                                    +

                                    UiSelector.exists()#

                                    @@ -762,11 +763,11 @@

                                    UiSelector.exists()#

                                    +

                                    UiSelector.waitFor()#

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

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

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

                                    UiSelector.filter(f)#

                                    +

                                    UiSelector.filter(f)#

                                    • f <Function> 过滤函数,参数为UiObject,返回值为boolean
                                    @@ -775,29 +776,29 @@

                                    UiSelector.exists()#

                                    +

                                    UiObject#

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

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

                                    -

                                    UiObject.click()#

                                    +

                                    UiObject.click()#

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

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

                                    -

                                    UiObject.longClick()#

                                    +

                                    UiObject.longClick()#

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

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

                                    -

                                    UiObject.setText(text)#

                                    +

                                    UiObject.setText(text)#

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

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

                                    -

                                    UiObject.copy()#

                                    +

                                    UiObject.copy()#

                                    @@ -812,10 +813,10 @@

                                    UiObject.copy()#

                                    +

                                    UiObject.cut()#

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

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

                                    -

                                    UiObject.paste()#

                                    +

                                    UiObject.paste()#

                                    @@ -824,7 +825,7 @@

                                    UiObject.paste()#

                                    +

                                    UiObject.setSelection(start, end)#

                                    • start <number> 选中内容起始位置
                                    • end <number> 选中内容结束位置(不包括)
                                    • @@ -833,43 +834,43 @@

                                      UiObject.paste()#

                                      +

                                      UiObject.scrollForward()#

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

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

                                      -

                                      UiObject.scrollBackward()#

                                      +

                                      UiObject.scrollBackward()#

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

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

                                      -

                                      UiObject.select()#

                                      +

                                      UiObject.select()#

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

                                      -

                                      UiObject.collapse()#

                                      +

                                      UiObject.collapse()#

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

                                      -

                                      UiObject.expand()#

                                      +

                                      UiObject.expand()#

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

                                      -

                                      UiObject.show()#

                                      +

                                      UiObject.show()#

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

                                      -

                                      UiObject.scrollUp()#

                                      +

                                      UiObject.scrollUp()#

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

                                      -

                                      UiObject.scrollDown()#

                                      +

                                      UiObject.scrollDown()#

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

                                      -

                                      UiObject.scrollLeft()#

                                      +

                                      UiObject.scrollLeft()#

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

                                      -

                                      UiObject.scrollRight()#

                                      -

                                      children()#

                                      +

                                      UiObject.scrollRight()#

                                      +

                                      children()#

                                      @@ -878,12 +879,12 @@

                                      children()

                                      childCount()#

                                      +

                                      childCount()#

                                      返回子控件数目。

                                      -

                                      child(i)#

                                      +

                                      child(i)#

                                      • i <number> 子控件索引
                                      • 返回 <UiObject>
                                      • @@ -896,12 +897,12 @@

                                        child(i)#

                                        +

                                        parent()#

                                        • 返回 <UiObject>

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

                                        -

                                        bounds()#

                                        +

                                        bounds()#

                                        @@ -913,34 +914,34 @@

                                        bounds()#

                                        +

                                        boundsInParent()#

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

                                        -

                                        drawingOrder()#

                                        +

                                        drawingOrder()#

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

                                        -

                                        id()#

                                        +

                                        id()#

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

                                        -

                                        text()#

                                        +

                                        text()#

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

                                        -

                                        findByText(str)#

                                        +

                                        findByText(str)#

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

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

                                        -

                                        findOne(selector)#

                                        +

                                        findOne(selector)#

                                        • selector UiSelector
                                        • 返回 UiOobject
                                        • @@ -965,13 +966,13 @@

                                          findOne(selector)#

                                          +

                                          find(selector)#

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

                                          -

                                          UiCollection#

                                          +

                                          UiCollection#

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

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

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

                                          @@ -992,36 +993,36 @@

                                          UiCollection#

                                          +

                                          UiCollection.size()#

                                          返回集合中的控件数。

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

                                          -

                                          UiCollection.get(i)#

                                          +

                                          UiCollection.get(i)#

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

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

                                          -

                                          UiCollection.each(func)#

                                          +

                                          UiCollection.each(func)#

                                          • func <Function> 遍历函数,参数为UiObject。

                                          遍历集合。

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

                                          -

                                          empty()#

                                          +

                                          empty()#

                                          返回控件集合是否为空。

                                          -

                                          nonEmpty()#

                                          +

                                          nonEmpty()#

                                          返回控件集合是否非空。

                                          -

                                          UiCollection.find(selector)#

                                          +

                                          UiCollection.find(selector)#

                                          • selector UiSelector
                                          • 返回 UiCollection
                                          • @@ -1032,65 +1033,65 @@

                                            UiCollection.find(selector)#

                                            +

                                            UiCollection.findOne(selector)#

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

                                            -

                                            Rect#

                                            +

                                            Rect#

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

                                            -

                                            Rect.left#

                                            +

                                            Rect.left#

                                            长方形左边界的x坐标、

                                            -

                                            Rect.right#

                                            +

                                            Rect.right#

                                            长方形右边界的x坐标、

                                            -

                                            Rect.top#

                                            +

                                            Rect.top#

                                            长方形上边界的y坐标、

                                            -

                                            Rect.bottom#

                                            +

                                            Rect.bottom#

                                            长方形下边界的y坐标、

                                            -

                                            Rect.centerX()#

                                            +

                                            Rect.centerX()#

                                            长方形中点x坐标。

                                            -

                                            Rect.centerY()#

                                            +

                                            Rect.centerY()#

                                            长方形中点y坐标。

                                            -

                                            Rect.width()#

                                            +

                                            Rect.width()#

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

                                            -

                                            Rect.height()#

                                            +

                                            Rect.height()#

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

                                            -

                                            Rect.contains(r)#

                                            +

                                            Rect.contains(r)#

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

                                            -

                                            Rect.intersect(r)#

                                            +

                                            Rect.intersect(r)#

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

                                            -

                                            UiSelector进阶#

                                            +

                                            UiSelector进阶#

                                            未完待续。

                                          diff --git a/generator/auto_generate.py b/generator/auto_generate.py index 28b5025..660643d 100644 --- a/generator/auto_generate.py +++ b/generator/auto_generate.py @@ -6,7 +6,7 @@ out_dir = os.path.join('..', 'docs') json_out_dir = os.path.join('..', 'json') template = os.path.join('..', 'template.html') -version = 'Pro-8.1.0' +version = 'Pro-8.2.0' def process(in_file, out_file, format="html"): diff --git a/generator/generate.js b/generator/generate.js index e14a014..ef921d4 100644 --- a/generator/generate.js +++ b/generator/generate.js @@ -27,13 +27,15 @@ const fs = require('fs'); // parse the args. // Don't use nopt or whatever for this. It's simple enough. +const path = require('path'); + const args = process.argv.slice(2); let format = 'html'; -let template = '..\\template.html'; -let inputFile = '..\\api\\all.md'; +let template = path.join('..', 'template.html'); +let inputFile = path.join('..', 'api', 'all.md'); let nodeVersion = null; let analytics = null; -let out = '..\\docs\\all.html'; +let out = path.join('..', 'docs', 'all.html'); args.forEach(function(arg) { if (!arg.startsWith('--')) { diff --git a/json/_404.json b/json/_404.json new file mode 100644 index 0000000..b10bd26 --- /dev/null +++ b/json/_404.json @@ -0,0 +1,11 @@ +{ + "source": "../api/_404.md", + "modules": [ + { + "textRaw": "页面找不到了o(╥﹏╥)o", + "name": "页面找不到了o(╥﹏╥)o", + "type": "module", + "displayName": "页面找不到了o(╥﹏╥)o" + } + ] +} \ No newline at end of file diff --git a/json/_coverpage.json b/json/_coverpage.json new file mode 100644 index 0000000..ba51bc2 --- /dev/null +++ b/json/_coverpage.json @@ -0,0 +1,20 @@ +{ + "source": "../api/_coverpage.md", + "desc": [ + { + "type": "paragraph", + "text": "![logo](images/logo.png)" + }, + { + "type": "space" + } + ], + "properties": [ + { + "textRaw": "Auto.js", + "type": "property", + "name": "js", + "desc": "

                                          不需要Root权限的JavaScript自动化软件

                                          \n

                                          GitHub\n软件下载\n阅读文档

                                          \n" + } + ] +} \ No newline at end of file diff --git a/json/_sidebar.json b/json/_sidebar.json new file mode 100644 index 0000000..506fe2d --- /dev/null +++ b/json/_sidebar.json @@ -0,0 +1,293 @@ +{ + "source": "../api/_sidebar.md", + "desc": [ + { + "type": "list_start", + "ordered": false, + "start": "" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[首页](/)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[Q&A](/qa)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[如何阅读本文档 - Documentation](/documentation)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[应用 - App](/app)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[一般全局函数 - Globals](/globals)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[控制台 - Console](/console)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[基于坐标的操作 - CoordinatesBasedAutomation](/coordinatesBasedAutomation)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[加解密与消息摘要 - Crypto](/crypto)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[设备 - Device](/device)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[对话框 - Dialogs](/dialogs)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[脚本引擎 - Engines](/engines)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[事件与监听 - Events](/events)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[悬浮窗 - Floaty](/floaty)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[文件系统 - Files](/files)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[HTTP - Http](/http)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[图片与颜色 - Images](/images)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[画布 - Canvas](/canvas)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[按键模拟 - Keys](/keys)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[多媒体 - Media](/media)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[模块 - Modules](/modules)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[基于控件的操作 - WidgetsBaseAutomation](/widgetsBasedAutomation)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[传感器 - Sensor](/sensors)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[Shell - Shell](/shell)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[本地储存 - Storages](/storages)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[多线程 - Threads](/threads)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[定时器 - Timers](/timers)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[用户界面 - UI](/ui)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, + { + "type": "text", + "text": "[调用Java - Work with Java](https://developer.mozilla.org/zh-CN/docs/Mozilla/Projects/Rhino/Scripting_Java)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_end" + } + ] +} \ No newline at end of file diff --git a/json/_toc.json b/json/_toc.json index 29c5185..ac546f0 100644 --- a/json/_toc.json +++ b/json/_toc.json @@ -88,6 +88,16 @@ { "type": "list_item_start" }, + { + "type": "text", + "text": "[Canvas - 画布](canvas.html)" + }, + { + "type": "list_item_end" + }, + { + "type": "list_item_start" + }, { "type": "text", "text": "[Console - 控制台](console.html)" @@ -100,7 +110,7 @@ }, { "type": "text", - "text": "[CoordinatesBasedAutomation - 基于坐标的操作](coordinates-based-automation.html)" + "text": "[CoordinatesBasedAutomation - 基于坐标的操作](coordinatesBasedAutomation.html)" }, { "type": "list_item_end" @@ -240,7 +250,7 @@ }, { "type": "text", - "text": "[WidgetsBasedAutomation - 基于控件的操作](widgets-based-automation.html)" + "text": "[Sensors - 传感器](sensors.html)" }, { "type": "list_item_end" @@ -250,7 +260,7 @@ }, { "type": "text", - "text": "[Sensors - 传感器](sensors.html)" + "text": "[Shell - Shell命令](shell.html)" }, { "type": "list_item_end" @@ -260,7 +270,7 @@ }, { "type": "text", - "text": "[Shell - Shell命令](shell.html)" + "text": "[Storages - 本地存储](storages.html)" }, { "type": "list_item_end" @@ -270,7 +280,7 @@ }, { "type": "text", - "text": "[Storages - 本地存储](storages.html)" + "text": "[Threads - 多线程](threads.html)" }, { "type": "list_item_end" @@ -280,7 +290,7 @@ }, { "type": "text", - "text": "[Threads - 多线程](threads.html)" + "text": "[Timers - 定时器](timers.html)" }, { "type": "list_item_end" @@ -290,7 +300,7 @@ }, { "type": "text", - "text": "[Timers - 定时器](timers.html)" + "text": "[UI - 用户界面](ui.html)" }, { "type": "list_item_end" @@ -300,7 +310,7 @@ }, { "type": "text", - "text": "[UI - 用户界面](ui.html)" + "text": "[WidgetsBasedAutomation - 基于控件的操作](widgetsBasedAutomation.html)" }, { "type": "list_item_end" diff --git a/json/canvas.json b/json/canvas.json index 326ef53..1c0e71e 100644 --- a/json/canvas.json +++ b/json/canvas.json @@ -4,13 +4,124 @@ { "textRaw": "Canvas", "name": "canvas", - "desc": "

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

                                          \n

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

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

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

                                          \n
                                          var paint = new Paint();\n//设置画笔为填充,则绘制出来的图形都是实心的\npaint.setStyle(Paint.STYLE.FILL);\n//设置画笔颜色为红色\npaint.setColor(colors.RED);\n//绘制一个从坐标(0, 0)到坐标(100, 100)的正方形\ncanvas.drawRect(0, 0, 100, 100, paint);\n

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

                                          \n
                                          var paint = new Paint();\n//设置画笔为描边,则绘制出来的图形都是轮廓\npaint.setStyle(Paint.STYLE.STROKE);\n//设置画笔颜色为红色\npaint.setColor(colors.RED);\n//绘制一个从坐标(0, 0)到坐标(100, 100)的正方形\ncanvas.drawRect(0, 0, 100, 100, paint);\n

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

                                          \n", + "desc": "

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

                                          \n

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

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

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

                                          \n
                                          var paint = new Paint();\n//设置画笔为填充,则绘制出来的图形都是实心的\npaint.setStyle(Paint.STYLE.FILL);\n//设置画笔颜色为红色\npaint.setColor(colors.RED);\n//绘制一个从坐标(0, 0)到坐标(100, 100)的正方形\ncanvas.drawRect(0, 0, 100, 100, paint);\n

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

                                          \n
                                          var paint = new Paint();\n//设置画笔为描边,则绘制出来的图形都是轮廓\npaint.setStyle(Paint.STYLE.STROKE);\n//设置画笔颜色为红色\npaint.setColor(colors.RED);\n//绘制一个从坐标(0, 0)到坐标(100, 100)的正方形\ncanvas.drawRect(0, 0, 100, 100, paint);\n

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

                                          \n", "methods": [ + { + "textRaw": "canvas.getWidth()", + "type": "method", + "name": "getWidth", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {number} ", + "name": "返回", + "type": "number" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          返回画布当前图层的宽度。

                                          \n" + }, + { + "textRaw": "canvas.getHeight()", + "type": "method", + "name": "getHeight", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {number} ", + "name": "返回", + "type": "number" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          返回画布当前图层的高度。

                                          \n" + }, + { + "textRaw": "canvas.drawRGB(r, int g, int b)", + "type": "method", + "name": "drawRGB", + "signatures": [ + { + "params": [ + { + "textRaw": "`r` {number} 红色通道值 ", + "name": "r", + "type": "number", + "desc": "红色通道值" + }, + { + "textRaw": "`g` {number} 绿色通道值 ", + "name": "g", + "type": "number", + "desc": "绿色通道值" + }, + { + "textRaw": "`b` {number} 蓝色通道值 ", + "name": "b", + "type": "number", + "desc": "蓝色通道值" + } + ] + }, + { + "params": [ + { + "name": "r" + }, + { + "name": "int g" + }, + { + "name": "int b" + } + ] + } + ], + "desc": "

                                          将整个可绘制区域填充为r、g、b指定的颜色。相当于 canvas.drawColor(colors.rgb(r, g, b))

                                          \n" + }, { "textRaw": "canvas.drawARGB(a, r, g, b)", "type": "method", "name": "drawARGB", "signatures": [ + { + "params": [ + { + "textRaw": "`a` {number} 透明通道值 ", + "name": "a", + "type": "number", + "desc": "透明通道值" + }, + { + "textRaw": "`r` {number} 红色通道值 ", + "name": "r", + "type": "number", + "desc": "红色通道值" + }, + { + "textRaw": "`g` {number} 绿色通道值 ", + "name": "g", + "type": "number", + "desc": "绿色通道值" + }, + { + "textRaw": "`b` {number} 蓝色通道值 ", + "name": "b", + "type": "number", + "desc": "蓝色通道值" + } + ] + }, { "params": [ { @@ -27,17 +138,509 @@ } ] } + ], + "desc": "

                                          将整个可绘制区域填充为a、r、g、b指定的颜色。相当于 canvas.drawColor(colors.argb(a, r, g, b))

                                          \n" + }, + { + "textRaw": "canvas.drawColor(color)", + "type": "method", + "name": "drawColor", + "signatures": [ + { + "params": [ + { + "textRaw": "`color` {number} 颜色值 ", + "name": "color", + "type": "number", + "desc": "颜色值" + } + ] + }, + { + "params": [ + { + "name": "color" + } + ] + } + ], + "desc": "

                                          将整个可绘制区域填充为color指定的颜色。

                                          \n" + }, + { + "textRaw": "canvas.drawColor(color, mode)", + "type": "method", + "name": "drawColor", + "signatures": [ + { + "params": [ + { + "textRaw": "`color` {number} 颜色值 ", + "name": "color", + "type": "number", + "desc": "颜色值" + }, + { + "textRaw": "`mode` {PorterDuff.Mode} Porter-Duff操作 ", + "name": "mode", + "type": "PorterDuff.Mode", + "desc": "Porter-Duff操作" + } + ] + }, + { + "params": [ + { + "name": "color" + }, + { + "name": "mode" + } + ] + } + ], + "desc": "

                                          将整个可绘制区域填充为color指定的颜色。

                                          \n" + }, + { + "textRaw": "canvas.drawPaint(paint)", + "type": "method", + "name": "drawPaint", + "signatures": [ + { + "params": [ + { + "textRaw": "`paint` {Paint} 画笔 ", + "name": "paint", + "type": "Paint", + "desc": "画笔" + } + ] + }, + { + "params": [ + { + "name": "paint" + } + ] + } + ], + "desc": "

                                          将整个可绘制区域用paint指定的画笔填充。相当于绘制一个无限大的矩形,但是更快。\n通过该方法可以绘制一个指定的着色器的图案。

                                          \n" + }, + { + "textRaw": "canvas.drawPoint(x, y, paint)", + "type": "method", + "name": "drawPoint", + "signatures": [ + { + "params": [ + { + "textRaw": "`x` {number} x坐标 ", + "name": "x", + "type": "number", + "desc": "x坐标" + }, + { + "textRaw": "`y` {number} y坐标 ", + "name": "y", + "type": "number", + "desc": "y坐标" + }, + { + "textRaw": "`paint` {Paint} 画笔 ", + "name": "paint", + "type": "Paint", + "desc": "画笔" + } + ] + }, + { + "params": [ + { + "name": "x" + }, + { + "name": "y" + }, + { + "name": "paint" + } + ] + } + ], + "desc": "

                                          在可绘制区域绘制由坐标(x, y)指定的点。\n点的形状由画笔的线帽决定(参见paint.setStrokeCap(cap))。\n点的大小由画笔的宽度决定(参见paint.setStrokeWidth(width))。

                                          \n
                                          \n

                                          如果画笔宽度为0,则也会绘制1个像素至4个(若抗锯齿启用)。

                                          \n
                                          \n

                                          相当于 canvas.drawPoints([x, y], paint)

                                          \n" + }, + { + "textRaw": "canvas.drawPoints(pts, paint)", + "type": "method", + "name": "drawPoints", + "signatures": [ + { + "params": [ + { + "textRaw": "`pts` {Array} 点坐标数组 [x0, y0, x1, y1, x2, y2, ...] ", + "name": "pts", + "type": "Array", + "desc": "点坐标数组 [x0, y0, x1, y1, x2, y2, ...]" + }, + { + "textRaw": "`paint` {Paint} 画笔 ", + "name": "paint", + "type": "Paint", + "desc": "画笔" + } + ] + }, + { + "params": [ + { + "name": "pts" + }, + { + "name": "paint" + } + ] + } + ], + "desc": "

                                          在可绘制区域绘制由坐标数组指定的多个点。

                                          \n" + }, + { + "textRaw": "canvas.drawLine(startX, startY, stopX, stopY, paint)", + "type": "method", + "name": "drawLine", + "signatures": [ + { + "params": [ + { + "textRaw": "`startX` {number} 起点x坐标 ", + "name": "startX", + "type": "number", + "desc": "起点x坐标" + }, + { + "textRaw": "`startY` {number} 起点y坐标 ", + "name": "startY", + "type": "number", + "desc": "起点y坐标" + }, + { + "textRaw": "`endX` {number} 终点x坐标 ", + "name": "endX", + "type": "number", + "desc": "终点x坐标" + }, + { + "textRaw": "`endY` {number} 终点y坐标 ", + "name": "endY", + "type": "number", + "desc": "终点y坐标" + }, + { + "textRaw": "`paint` {Paint} 画笔 ", + "name": "paint", + "type": "Paint", + "desc": "画笔" + } + ] + }, + { + "params": [ + { + "name": "startX" + }, + { + "name": "startY" + }, + { + "name": "stopX" + }, + { + "name": "stopY" + }, + { + "name": "paint" + } + ] + } + ], + "desc": "

                                          在可绘制区域绘制由起点坐标(startX, startY)和终点坐标(endX, endY)指定的线。\n绘制时会忽略画笔的样式(Style)。也就是说,即使样式设为“仅填充(FILL)”也会绘制。\n退化为点的线(长度为0)不会被绘制。

                                          \n" + }, + { + "textRaw": "canvas.drawRect(r, paint)", + "type": "method", + "name": "drawRect", + "signatures": [ + { + "params": [ + { + "textRaw": "`r` {Rect} 矩形边界 ", + "name": "r", + "type": "Rect", + "desc": "矩形边界" + }, + { + "textRaw": "`paint` {Paint} 画笔 ", + "name": "paint", + "type": "Paint", + "desc": "画笔" + } + ] + }, + { + "params": [ + { + "name": "r" + }, + { + "name": "paint" + } + ] + } + ], + "desc": "

                                          在可绘制区域绘制由矩形边界r指定的矩形。\n绘制时画笔的样式(Style)决定了是否绘制矩形界线和填充矩形。

                                          \n" + }, + { + "textRaw": "canvas.drawRect(left, top, right, bottom, android.graphics.Paint paint)", + "type": "method", + "name": "drawRect", + "signatures": [ + { + "params": [ + { + "textRaw": "`left` {number} 矩形左边界x坐标 ", + "name": "left", + "type": "number", + "desc": "矩形左边界x坐标" + }, + { + "textRaw": "`top` {number} 矩形上边界y坐标 ", + "name": "top", + "type": "number", + "desc": "矩形上边界y坐标" + }, + { + "textRaw": "`right` {number} 矩形右边界x坐标 ", + "name": "right", + "type": "number", + "desc": "矩形右边界x坐标" + }, + { + "textRaw": "`bottom` {number} 矩形下边界y坐标 ", + "name": "bottom", + "type": "number", + "desc": "矩形下边界y坐标" + }, + { + "textRaw": "`paint` {Paint} 画笔 ", + "name": "paint", + "type": "Paint", + "desc": "画笔" + } + ] + }, + { + "params": [ + { + "name": "left" + }, + { + "name": "top" + }, + { + "name": "right" + }, + { + "name": "bottom" + }, + { + "name": "android.graphics.Paint paint" + } + ] + } + ], + "desc": "

                                          在可绘制区域绘制由矩形边界(left, top, right, bottom)指定的矩形。\n绘制时画笔的样式(Style)决定了是否绘制矩形界线和填充矩形。

                                          \n" + }, + { + "textRaw": "canvas.drawTextOnPath(java.lang.String text, android.graphics.Path path, float hOffset, float vOffset, android.graphics.Paint paint)", + "type": "method", + "name": "drawTextOnPath", + "signatures": [ + { + "params": [ + { + "textRaw": "`dx` {number} 向x轴正方向平移的距离,负数表示反方向平移 ", + "name": "dx", + "type": "number", + "desc": "向x轴正方向平移的距离,负数表示反方向平移" + }, + { + "textRaw": "`dy` {number} 向y轴正方向平移的距离,负数表示反方向平移 ", + "name": "dy", + "type": "number", + "desc": "向y轴正方向平移的距离,负数表示反方向平移" + } + ] + }, + { + "params": [ + { + "name": "dx" + }, + { + "name": "dy" + } + ] + }, + { + "params": [ + { + "name": "java.lang.String text" + }, + { + "name": "android.graphics.Path path" + }, + { + "name": "float hOffset" + }, + { + "name": "float vOffset" + }, + { + "name": "android.graphics.Paint paint" + } + ] + } + ], + "desc": "

                                          平移指定距离。

                                          \n" + }, + { + "textRaw": "canvas.translate(dx, dy)", + "type": "method", + "name": "translate", + "signatures": [ + { + "params": [ + { + "textRaw": "`dx` {number} 向x轴正方向平移的距离,负数表示反方向平移 ", + "name": "dx", + "type": "number", + "desc": "向x轴正方向平移的距离,负数表示反方向平移" + }, + { + "textRaw": "`dy` {number} 向y轴正方向平移的距离,负数表示反方向平移 ", + "name": "dy", + "type": "number", + "desc": "向y轴正方向平移的距离,负数表示反方向平移" + } + ] + }, + { + "params": [ + { + "name": "dx" + }, + { + "name": "dy" + } + ] + } + ], + "desc": "

                                          平移指定距离。

                                          \n" + }, + { + "textRaw": "canvas.scale(sx, sy)", + "type": "method", + "name": "scale", + "signatures": [ + { + "params": [ + { + "textRaw": "`sx` {number} 向x轴正方向平移的距离,负数表示反方向平移 ", + "name": "sx", + "type": "number", + "desc": "向x轴正方向平移的距离,负数表示反方向平移" + }, + { + "textRaw": "`sy` {number} 向y轴正方向平移的距离,负数表示反方向平移 ", + "name": "sy", + "type": "number", + "desc": "向y轴正方向平移的距离,负数表示反方向平移" + } + ] + }, + { + "params": [ + { + "name": "sx" + }, + { + "name": "sy" + } + ] + } + ], + "desc": "

                                          以原点为中心,将坐标系平移缩放指定倍数。

                                          \n" + }, + { + "textRaw": "canvas.rotate(float degrees, float px, float py)", + "type": "method", + "name": "rotate", + "signatures": [ + { + "params": [ + { + "name": "float sx" + }, + { + "name": "float sy" + } + ] + }, + { + "params": [ + { + "name": "float degrees" + }, + { + "name": "float px" + }, + { + "name": "float py" + } + ] + } ] - } - ], - "properties": [ + }, { - "textRaw": "canvas.draw", - "name": "draw" + "textRaw": "canvas.skew(float sx, float sy)", + "type": "method", + "name": "skew", + "signatures": [ + { + "params": [ + { + "name": "float sx" + }, + { + "name": "float sy" + } + ] + } + ] } ], "type": "module", "displayName": "Canvas" + }, + { + "textRaw": "路径特效", + "name": "路径特效", + "type": "module", + "displayName": "区域" + }, + { + "textRaw": "区域", + "name": "区域", + "type": "module", + "displayName": "区域" } ] } \ No newline at end of file diff --git a/json/coordinatesBasedAutomation.json b/json/coordinatesBasedAutomation.json new file mode 100644 index 0000000..8003d06 --- /dev/null +++ b/json/coordinatesBasedAutomation.json @@ -0,0 +1,814 @@ +{ + "source": "../api/coordinatesBasedAutomation.md", + "modules": [ + { + "textRaw": "基于坐标的触摸模拟", + "name": "基于坐标的触摸模拟", + "stability": 2, + "stabilityText": "Stable", + "desc": "

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

                                          \n

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

                                          \n

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

                                          \n

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

                                          \n
                                          //获取这个控件\nvar widget = id("xxx").findOne();\n//获取其中心位置并点击\nclick(widget.bounds().centerX(), widget.bounds().centerY());\n//如果用root权限则用Tap\n
                                          ", + "methods": [ + { + "textRaw": "setScreenMetrics(width, height)", + "type": "method", + "name": "setScreenMetrics", + "signatures": [ + { + "params": [ + { + "textRaw": "width {number} 屏幕宽度,单位像素 ", + "name": "width", + "type": "number", + "desc": "屏幕宽度,单位像素" + }, + { + "textRaw": "height {number} 屏幕高度,单位像素 ", + "name": "height", + "type": "number", + "desc": "屏幕高度,单位像素" + } + ] + }, + { + "params": [ + { + "name": "width" + }, + { + "name": "height" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n
                                          setScreenMetrics(1080, 1920);\nclick(800, 200);\nlongClick(300, 500);\n

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

                                          \n" + } + ], + "type": "module", + "displayName": "基于坐标的触摸模拟" + }, + { + "textRaw": "RootAutomator", + "name": "rootautomator", + "stability": 2, + "stabilityText": "Stable", + "desc": "

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

                                          \n

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

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

                                          注意以下命令需要root权限

                                          \n", + "methods": [ + { + "textRaw": "RootAutomator.tap(x, y[, id])", + "type": "method", + "name": "tap", + "signatures": [ + { + "params": [ + { + "textRaw": "`x` {number} 横坐标 ", + "name": "x", + "type": "number", + "desc": "横坐标" + }, + { + "textRaw": "`y` {number} 纵坐标 ", + "name": "y", + "type": "number", + "desc": "纵坐标" + }, + { + "textRaw": "`id` {number} 多点触摸id,可选,默认为1,可以通过setDefaultId指定。 ", + "name": "id", + "type": "number", + "desc": "多点触摸id,可选,默认为1,可以通过setDefaultId指定。", + "optional": true + } + ] + }, + { + "params": [ + { + "name": "x" + }, + { + "name": "y" + }, + { + "name": "id", + "optional": true + } + ] + } + ], + "desc": "

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

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

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "RootAutomator.swipe(x1, x2, y1, y2[, duration, id])", + "type": "method", + "name": "swipe", + "signatures": [ + { + "params": [ + { + "textRaw": "`x1` {number} 滑动起点横坐标 ", + "name": "x1", + "type": "number", + "desc": "滑动起点横坐标" + }, + { + "textRaw": "`y1` {number} 滑动起点纵坐标 ", + "name": "y1", + "type": "number", + "desc": "滑动起点纵坐标" + }, + { + "textRaw": "`x2` {number} 滑动终点横坐标 ", + "name": "x2", + "type": "number", + "desc": "滑动终点横坐标" + }, + { + "textRaw": "`y2` {number} 滑动终点纵坐标 ", + "name": "y2", + "type": "number", + "desc": "滑动终点纵坐标" + }, + { + "textRaw": "`duration` {number} 滑动时长,单位毫秒,默认值为300 ", + "name": "duration", + "type": "number", + "desc": "滑动时长,单位毫秒,默认值为300", + "optional": true + }, + { + "textRaw": "`id` {number} 多点触摸id,可选,默认为1 ", + "name": "id", + "type": "number", + "desc": "多点触摸id,可选,默认为1", + "optional": true + } + ] + }, + { + "params": [ + { + "name": "x1" + }, + { + "name": "x2" + }, + { + "name": "y1" + }, + { + "name": "y2" + }, + { + "name": "duration", + "optional": true + }, + { + "name": "id", + "optional": true + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "RootAutomator.press(x, y, duration[, id])", + "type": "method", + "name": "press", + "signatures": [ + { + "params": [ + { + "textRaw": "`x` {number} 横坐标 ", + "name": "x", + "type": "number", + "desc": "横坐标" + }, + { + "textRaw": "`y` {number} 纵坐标 ", + "name": "y", + "type": "number", + "desc": "纵坐标" + }, + { + "textRaw": "`duration` {number} 按下时长 ", + "name": "duration", + "type": "number", + "desc": "按下时长" + }, + { + "textRaw": "`id` {number} 多点触摸id,可选,默认为1 ", + "name": "id", + "type": "number", + "desc": "多点触摸id,可选,默认为1", + "optional": true + } + ] + }, + { + "params": [ + { + "name": "x" + }, + { + "name": "y" + }, + { + "name": "duration" + }, + { + "name": "id", + "optional": true + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "RootAutomator.longPress(x, y[\\, id\\])", + "type": "method", + "name": "longPress", + "signatures": [ + { + "params": [ + { + "textRaw": "`x` {number} 横坐标 ", + "name": "x", + "type": "number", + "desc": "横坐标" + }, + { + "textRaw": "`y` {number} 纵坐标 ", + "name": "y", + "type": "number", + "desc": "纵坐标" + }, + { + "textRaw": "`duration` {number} 按下时长 ", + "name": "duration", + "type": "number", + "desc": "按下时长" + }, + { + "textRaw": "`id` {number} 多点触摸id,可选,默认为1 ", + "name": "id", + "type": "number", + "desc": "多点触摸id,可选,默认为1" + } + ] + }, + { + "params": [ + { + "name": "x" + }, + { + "name": "y[\\" + }, + { + "name": "id\\" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "RootAutomator.touchDown(x, y[, id])", + "type": "method", + "name": "touchDown", + "signatures": [ + { + "params": [ + { + "textRaw": "`x` {number} 横坐标 ", + "name": "x", + "type": "number", + "desc": "横坐标" + }, + { + "textRaw": "`y` {number} 纵坐标 ", + "name": "y", + "type": "number", + "desc": "纵坐标" + }, + { + "textRaw": "`id` {number} 多点触摸id,可选,默认为1 ", + "name": "id", + "type": "number", + "desc": "多点触摸id,可选,默认为1", + "optional": true + } + ] + }, + { + "params": [ + { + "name": "x" + }, + { + "name": "y" + }, + { + "name": "id", + "optional": true + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "RootAutomator.touchMove(x, y[, id])", + "type": "method", + "name": "touchMove", + "signatures": [ + { + "params": [ + { + "textRaw": "`x` {number} 横坐标 ", + "name": "x", + "type": "number", + "desc": "横坐标" + }, + { + "textRaw": "`y` {number} 纵坐标 ", + "name": "y", + "type": "number", + "desc": "纵坐标" + }, + { + "textRaw": "`id` {number} 多点触摸id,可选,默认为1 ", + "name": "id", + "type": "number", + "desc": "多点触摸id,可选,默认为1", + "optional": true + } + ] + }, + { + "params": [ + { + "name": "x" + }, + { + "name": "y" + }, + { + "name": "id", + "optional": true + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "RootAutomator.touchUp([id])", + "type": "method", + "name": "touchUp", + "signatures": [ + { + "params": [ + { + "textRaw": "`id` {number} 多点触摸id,可选,默认为1 ", + "name": "id", + "type": "number", + "desc": "多点触摸id,可选,默认为1", + "optional": true + } + ] + }, + { + "params": [ + { + "name": "id", + "optional": true + } + ] + } + ], + "desc": "

                                          模拟手指弹起。

                                          \n" + } + ], + "type": "module", + "displayName": "RootAutomator" + }, + { + "textRaw": "使用root权限点击和滑动的简单命令", + "name": "使用root权限点击和滑动的简单命令", + "stability": 1, + "stabilityText": "Experimental", + "desc": "

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

                                          \n

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

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

                                          例如:

                                          \n
                                          Tap(100, 100);\nsleep(500);\n

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

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

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

                                          \n
                                          for(var i = 0; i < 100; i++){\n  Tap(100, 100);\n  sleep(500);\n}\n
                                          ", + "methods": [ + { + "textRaw": "Tap(x, y)", + "type": "method", + "name": "Tap", + "signatures": [ + { + "params": [ + { + "textRaw": "x, y {number} 要点击的坐标。 ", + "name": "x,", + "desc": "y {number} 要点击的坐标。" + }, + { + "name": "y" + } + ] + }, + { + "params": [ + { + "name": "x" + }, + { + "name": "y" + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "Swipe(x1, y1, x2, y2, \\[duration\\])", + "type": "method", + "name": "Swipe", + "signatures": [ + { + "params": [ + { + "textRaw": "x1, y1 {number} 滑动起点的坐标 ", + "name": "x1,", + "desc": "y1 {number} 滑动起点的坐标" + }, + { + "textRaw": "x2, y2 {number} 滑动终点的坐标 ", + "name": "x2,", + "desc": "y2 {number} 滑动终点的坐标" + }, + { + "textRaw": "duration {number} 滑动动作所用的时间 ", + "name": "duration", + "type": "number", + "desc": "滑动动作所用的时间" + }, + { + "name": "y2" + }, + { + "name": "\\[duration\\" + } + ] + }, + { + "params": [ + { + "name": "x1" + }, + { + "name": "y1" + }, + { + "name": "x2" + }, + { + "name": "y2" + }, + { + "name": "\\[duration\\" + } + ] + } + ], + "desc": "

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

                                          \n" + } + ], + "type": "module", + "displayName": "使用root权限点击和滑动的简单命令" + } + ], + "properties": [ + { + "textRaw": "安卓7.0以上的触摸和手势模拟", + "type": "property", + "name": "0以上的触摸和手势模拟", + "stability": 2, + "stabilityText": "Stable", + "desc": "

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

                                          \n", + "methods": [ + { + "textRaw": "click(x, y)", + "type": "method", + "name": "click", + "signatures": [ + { + "params": [ + { + "textRaw": "`x` {number} 要点击的坐标的x值 ", + "name": "x", + "type": "number", + "desc": "要点击的坐标的x值" + }, + { + "textRaw": "`y` {number} 要点击的坐标的y值 ", + "name": "y", + "type": "number", + "desc": "要点击的坐标的y值" + } + ] + }, + { + "params": [ + { + "name": "x" + }, + { + "name": "y" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "longClick(x, y)", + "type": "method", + "name": "longClick", + "signatures": [ + { + "params": [ + { + "textRaw": "`x` {number} 要长按的坐标的x值 ", + "name": "x", + "type": "number", + "desc": "要长按的坐标的x值" + }, + { + "textRaw": "`y` {number} 要长按的坐标的y值 ", + "name": "y", + "type": "number", + "desc": "要长按的坐标的y值" + } + ] + }, + { + "params": [ + { + "name": "x" + }, + { + "name": "y" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "press(x, y, duration)", + "type": "method", + "name": "press", + "signatures": [ + { + "params": [ + { + "textRaw": "`x` {number} 要按住的坐标的x值 ", + "name": "x", + "type": "number", + "desc": "要按住的坐标的x值" + }, + { + "textRaw": "`y` {number} 要按住的坐标的y值 ", + "name": "y", + "type": "number", + "desc": "要按住的坐标的y值" + }, + { + "textRaw": "`duration` {number} 按住时长,单位毫秒 ", + "name": "duration", + "type": "number", + "desc": "按住时长,单位毫秒" + } + ] + }, + { + "params": [ + { + "name": "x" + }, + { + "name": "y" + }, + { + "name": "duration" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n

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

                                          \n

                                          一个连点器的例子如下:

                                          \n
                                          //循环100次\nfor(var i = 0; i < 100; i++){\n  //点击位置(500, 1000), 每次用时1毫秒\n  press(500, 1000, 1);\n}\n
                                          " + }, + { + "textRaw": "swipe(x1, y1, x2, y2, duration)", + "type": "method", + "name": "swipe", + "signatures": [ + { + "params": [ + { + "textRaw": "`x1` {number} 滑动的起始坐标的x值 ", + "name": "x1", + "type": "number", + "desc": "滑动的起始坐标的x值" + }, + { + "textRaw": "`y1` {number} 滑动的起始坐标的y值 ", + "name": "y1", + "type": "number", + "desc": "滑动的起始坐标的y值" + }, + { + "textRaw": "`x2` {number} 滑动的结束坐标的x值 ", + "name": "x2", + "type": "number", + "desc": "滑动的结束坐标的x值" + }, + { + "textRaw": "`y2` {number} 滑动的结束坐标的y值 ", + "name": "y2", + "type": "number", + "desc": "滑动的结束坐标的y值" + }, + { + "textRaw": "`duration` {number} 滑动时长,单位毫秒 ", + "name": "duration", + "type": "number", + "desc": "滑动时长,单位毫秒" + } + ] + }, + { + "params": [ + { + "name": "x1" + }, + { + "name": "y1" + }, + { + "name": "x2" + }, + { + "name": "y2" + }, + { + "name": "duration" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "gesture(duration, [x1, y1], [x2, y2], ...)", + "type": "method", + "name": "gesture", + "signatures": [ + { + "params": [ + { + "textRaw": "`duration` {number} 手势的时长 ", + "name": "duration", + "type": "number", + "desc": "手势的时长" + }, + { + "textRaw": "[x, y] ... 手势滑动路径的一系列坐标 ", + "name": "[x,", + "desc": "y] ... 手势滑动路径的一系列坐标", + "optional": true + }, + { + "name": "y1", + "optional": true + }, + { + "name": "x2", + "optional": true + }, + { + "name": "y2", + "optional": true + }, + { + "name": "..." + } + ] + }, + { + "params": [ + { + "name": "duration" + }, + { + "name": "x1", + "optional": true + }, + { + "name": "y1", + "optional": true + }, + { + "name": "x2", + "optional": true + }, + { + "name": "y2", + "optional": true + }, + { + "name": "..." + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "gestures([delay1, duration1, [x1, y1], [x2, y2], ...], [delay2, duration2, [x3, y3], [x4, y4], ...], ...)", + "type": "method", + "name": "gestures", + "desc": "

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

                                          \n

                                          例如手指捏合:

                                          \n
                                          gestures([0, 500, [800, 300], [500, 1000]],\n         [0, 500, [300, 1500], [500, 1000]]);\n
                                          ", + "signatures": [ + { + "params": [ + { + "name": "delay1", + "optional": true + }, + { + "name": "duration1", + "optional": true + }, + { + "name": "x1", + "optional": true + }, + { + "name": "y1", + "optional": true + }, + { + "name": "x2", + "optional": true + }, + { + "name": "y2", + "optional": true + }, + { + "name": "...", + "optional": true + }, + { + "name": "delay2", + "optional": true + }, + { + "name": "duration2", + "optional": true + }, + { + "name": "x3", + "optional": true + }, + { + "name": "y3", + "optional": true + }, + { + "name": "x4", + "optional": true + }, + { + "name": "y4", + "optional": true + }, + { + "name": "...", + "optional": true + }, + { + "name": "..." + } + ] + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/json/globals.json b/json/globals.json index 4350987..a3fb19f 100644 --- a/json/globals.json +++ b/json/globals.json @@ -29,7 +29,7 @@ ] } ], - "desc": "

                                          暂停运行n毫秒的时间。1秒等于1000毫秒。

                                          \n
                                          //暂停5毫秒\nsleep(5000);\n
                                          " + "desc": "

                                          暂停运行n毫秒的时间。1秒等于1000毫秒。

                                          \n
                                          //暂停5秒\nsleep(5000);\n
                                          " }, { "textRaw": "currentPackage()", diff --git a/json/overview.json b/json/overview.json index 40936f1..a326a6c 100644 --- a/json/overview.json +++ b/json/overview.json @@ -4,7 +4,7 @@ { "textRaw": "综述", "name": "综述", - "desc": "

                                          Auto.js使用JavaScript作为脚本语言,目前使用Rhino 1.7.7.2作为脚本引擎,支持ES5与部分ES6特性。

                                          \n
                                            \n
                                          • 因为Auto.js是基于JavaScript的,学习Auto.js的API之前建议先学习JavaScript的基本语法和内置对象,可以使用教程前面的两个JavaScript教程链接来学习。
                                          • \n
                                          • 如果您想要使用TypeScript来开发,目前已经有开发者公布了一个可以把使用TypeScript进行Auto.js开发的工具,参见Auto.js DevTools
                                          • \n
                                          • 如果想要在电脑而不是手机上开发Auto.js,可以使用VS Code以及相应的Auto.js插件使得在电脑上编辑的脚本能推送到手机运行,参见Auto.js-VSCode-Extension,Auto.js Pro用户则需要使用Auto.js-Pro-Ext
                                          • \n
                                          \n

                                          本文档的章节大致上是以模块来分的,总体上可以分成"自动操作"类模块(控件操作、触摸模拟、按键模拟等)和其他类模块(设备、应用、界面等)。

                                          \n

                                          "自动操作"的部分又可以大致分为基于控件和基于坐标的操作。基于坐标的操作是传统按键精灵、触摸精灵等脚本软件采用的方式,通过屏幕坐标来点击、长按指定位置模拟操作,从而到达目的。例如click(100, 200), press(100, 200, 500)等。这种方式在游戏类脚本中比较有可行性,结合找图找色、坐标放缩功能也能达到较好的兼容性。但是,这种方式对一般软件脚本却难以达到想要的效果,而且这种方式需要安卓7.0版本以上或者root权限才能执行。所以对于一般软件脚本(例如批量添加联系人、自动提取短信验证码等等),我们采用基于控件的模拟操作方式,结合通知事情、按键事情等达成更好的工作流。这些部分的文档参见基于控件的操作基于坐标的操作

                                          \n

                                          其他部分主要包括:

                                          \n
                                            \n
                                          • app: 应用。启动应用,卸载应用,使用应用查看、编辑文件、访问网页,发送应用间广播等。
                                          • \n
                                          • console: 控制台。记录运行的日志、错误、信息等。
                                          • \n
                                          • device: 设备。获取设备屏幕宽高、系统版本等信息,控制设备音量、亮度等。
                                          • \n
                                          • engines: 脚本引擎。用于启动其他脚本。
                                          • \n
                                          • events: 事件与监听。按键监听,通知监听,触摸监听等。
                                          • \n
                                          • floaty: 悬浮窗。用于显示自定义的悬浮窗。
                                          • \n
                                          • files: 文件系统。文件创建、获取信息、读写。
                                          • \n
                                          • http: HTTP。发送HTTP请求,例如GET, POST等。
                                          • \n
                                          • images, colors: 图片和图色处理。截图,剪切图片,找图找色,读取保存图片等。
                                          • \n
                                          • keys: 按键模拟。比如音量键、Home键模拟等。
                                          • \n
                                          • shell: Shell命令。
                                          • \n
                                          • threads: 多线程支持。
                                          • \n
                                          • ui: UI界面。用于显示自定义的UI界面,和用户交互。
                                          • \n
                                          \n

                                          除此之外,Auto.js内置了对Promise

                                          \n", + "desc": "

                                          Auto.js使用JavaScript作为脚本语言,目前使用Rhino 1.7.7.2作为脚本引擎,支持ES5与部分ES6特性。

                                          \n
                                            \n
                                          • 因为Auto.js是基于JavaScript的,学习Auto.js的API之前建议先学习JavaScript的基本语法和内置对象,可以使用教程前面的两个JavaScript教程链接来学习。
                                          • \n
                                          • 如果您想要使用TypeScript来开发,目前已经有开发者公布了一个可以把使用TypeScript进行Auto.js开发的工具,参见Auto.js DevTools
                                          • \n
                                          • 如果想要在电脑而不是手机上开发Auto.js,可以使用VS Code以及相应的Auto.js插件使得在电脑上编辑的脚本能推送到手机运行,参见Auto.js-VSCode-Extension,Auto.js Pro用户则需要使用Auto.js-Pro-Ext
                                          • \n
                                          • 支持Node.js已在计划中,预计2020年底在Auto.js Pro中支持
                                          • \n
                                          \n

                                          本文档的章节大致上是以模块来分的,总体上可以分成"自动操作"类模块(控件操作、触摸模拟、按键模拟等)和其他类模块(设备、应用、界面等)。

                                          \n

                                          "自动操作"的部分又可以大致分为基于控件和基于坐标的操作。基于坐标的操作是传统按键精灵、触摸精灵等脚本软件采用的方式,通过屏幕坐标来点击、长按指定位置模拟操作,从而到达目的。例如click(100, 200), press(100, 200, 500)等。这种方式在游戏类脚本中比较有可行性,结合找图找色、坐标放缩功能也能达到较好的兼容性。但是,这种方式对一般软件脚本却难以达到想要的效果,而且这种方式需要安卓7.0版本以上或者root权限才能执行。所以对于一般软件脚本(例如批量添加联系人、自动提取短信验证码等等),我们采用基于控件的模拟操作方式,结合通知事情、按键事情等达成更好的工作流。这些部分的文档参见基于控件的操作基于坐标的操作

                                          \n

                                          其他部分主要包括:

                                          \n
                                            \n
                                          • app: 应用。启动应用,卸载应用,使用应用查看、编辑文件、访问网页,发送应用间广播等。
                                          • \n
                                          • console: 控制台。记录运行的日志、错误、信息等。
                                          • \n
                                          • device: 设备。获取设备屏幕宽高、系统版本等信息,控制设备音量、亮度等。
                                          • \n
                                          • engines: 脚本引擎。用于启动其他脚本。
                                          • \n
                                          • events: 事件与监听。按键监听,通知监听,触摸监听等。
                                          • \n
                                          • floaty: 悬浮窗。用于显示自定义的悬浮窗。
                                          • \n
                                          • files: 文件系统。文件创建、获取信息、读写。
                                          • \n
                                          • http: HTTP。发送HTTP请求,例如GET, POST等。
                                          • \n
                                          • images, colors: 图片和图色处理。截图,剪切图片,找图找色,读取保存图片等。
                                          • \n
                                          • keys: 按键模拟。比如音量键、Home键模拟等。
                                          • \n
                                          • shell: Shell命令。
                                          • \n
                                          • threads: 多线程支持。
                                          • \n
                                          • ui: UI界面。用于显示自定义的UI界面,和用户交互。
                                          • \n
                                          \n

                                          除此之外,Auto.js内置了对Promise

                                          \n", "type": "module", "displayName": "综述" } diff --git a/json/widgetsBasedAutomation.json b/json/widgetsBasedAutomation.json new file mode 100644 index 0000000..f51c7f6 --- /dev/null +++ b/json/widgetsBasedAutomation.json @@ -0,0 +1,2631 @@ +{ + "source": "../api/widgetsBasedAutomation.md", + "modules": [ + { + "textRaw": "基于控件的操作", + "name": "基于控件的操作", + "desc": "

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

                                          \n

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

                                          \n

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

                                          \n", + "methods": [ + { + "textRaw": "auto([mode])", + "type": "method", + "name": "auto", + "signatures": [ + { + "params": [ + { + "textRaw": "`mode` {string} 模式 ", + "name": "mode", + "type": "string", + "desc": "模式", + "optional": true + } + ] + }, + { + "params": [ + { + "name": "mode", + "optional": true + } + ] + } + ], + "desc": "

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

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

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

                                          \n

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

                                          \n

                                          示例:

                                          \n
                                          auto("fast");\n

                                          示例2:

                                          \n
                                          auto();\n
                                          " + }, + { + "textRaw": "auto.waitFor()", + "type": "method", + "name": "waitFor", + "desc": "

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

                                          \n

                                          因为该函数是阻塞的,因此除非是有协程特性,否则不能在ui模式下运行该函数,建议在ui模式下使用auto()函数。

                                          \n", + "signatures": [ + { + "params": [] + } + ] + }, + { + "textRaw": "auto.setMode(mode)", + "type": "method", + "name": "setMode", + "signatures": [ + { + "params": [ + { + "textRaw": "`mode` {string} 模式 ", + "name": "mode", + "type": "string", + "desc": "模式" + } + ] + }, + { + "params": [ + { + "name": "mode" + } + ] + } + ], + "desc": "

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

                                          \n
                                            \n
                                          • fast 快速模式。该模式下会启用控件缓存,从而选择器获取屏幕控件更快。对于需要快速的控件查看和操作的脚本可以使用该模式,一般脚本则没有必要使用该函数。
                                          • \n
                                          • normal 正常模式,默认。
                                          • \n
                                          \n" + }, + { + "textRaw": "auto.setFlags(flags)", + "type": "method", + "name": "setFlags", + "desc": "

                                          [v4.1.0新增]

                                          \n
                                            \n
                                          • flags {string} | {Array} 一些标志,来启用和禁用某些特性,包括:
                                              \n
                                            • findOnUiThread 使用该特性后,选择器搜索时会在主进程进行。该特性用于解决线程安全问题导致的次生问题,不过目前貌似已知问题并不是线程安全问题。
                                            • \n
                                            • useUsageStats 使用该特性后,将会以"使用情况统计"服务的结果来检测当前正在运行的应用包名(需要授予"查看使用情况统计"权限)。如果觉得currentPackage()返回的结果不太准确,可以尝试该特性。
                                            • \n
                                            • useShell 使用该特性后,将使用shell命令获取当前正在运行的应用的包名、活动名称,但是需要root权限。
                                            • \n
                                            \n
                                          • \n
                                          \n

                                          启用有关automator的一些特性。例如:

                                          \n
                                          auto.setFlags(["findOnUiThread", "useShell"]);\n
                                          ", + "signatures": [ + { + "params": [ + { + "name": "flags" + } + ] + } + ] + }, + { + "textRaw": "auto.setWindowFilter(filter)", + "type": "method", + "name": "setWindowFilter", + "desc": "

                                          [v4.1.0新增]

                                          \n\n

                                          设置窗口过滤器。这个过滤器可以决定哪些窗口是目标窗口,并影响选择器的搜索。例如,如果想要选择器在所有窗口(包括状态栏、输入法等)中搜索,只需要使用以下代码:

                                          \n
                                          auto.setWindowFilter(function(window){\n    //不管是如何窗口,都返回true,表示在该窗口中搜索\n    return true;\n});\n

                                          又例如,当前使用了分屏功能,屏幕上有Auto.js和QQ两个应用,但我们只想选择器对QQ界面进行搜索,则:

                                          \n
                                          auto.setWindowFilter(function(window){\n    // 对于应用窗口,他的title属性就是应用的名称,因此可以通过title属性来判断一个应用\n    return window.title == "QQ";\n});\n

                                          选择器默认是在当前活跃的窗口中搜索,不会搜索诸如悬浮窗、状态栏之类的,使用WindowFilter则可以控制搜索的窗口。

                                          \n

                                          需要注意的是, 如果WindowFilter返回的结果均为false,则选择器的搜索结果将为空。

                                          \n

                                          另外setWindowFilter函数也会影响auto.windowRoots的结果。

                                          \n

                                          该函数需要Android 5.0以上才有效。

                                          \n", + "signatures": [ + { + "params": [ + { + "name": "filter" + } + ] + } + ] + } + ], + "properties": [ + { + "textRaw": "auto.serivce", + "name": "serivce", + "desc": "

                                          [v4.1.0新增]

                                          \n\n

                                          获取无障碍服务。如果无障碍服务没有启动,则返回null

                                          \n

                                          参见AccessibilityService

                                          \n" + }, + { + "textRaw": "auto.windows", + "name": "windows", + "desc": "

                                          [v4.1.0新增]

                                          \n
                                            \n
                                          • {Array}
                                          • \n
                                          \n

                                          当前所有窗口(AccessibilityWindowInfo)的数组,可能包括状态栏、输入法、当前应用窗口,弹出窗口、悬浮窗、分屏应用窗口等。可以分别获取每个窗口的布局信息。

                                          \n

                                          该函数需要Android 5.0以上才能运行。

                                          \n" + }, + { + "textRaw": "auto.root", + "name": "root", + "desc": "

                                          [v4.1.0新增]

                                          \n
                                            \n
                                          • {UiObject}
                                          • \n
                                          \n

                                          当前窗口的布局根元素。如果无障碍服务未启动或者WindowFilter均返回false,则会返回null

                                          \n

                                          如果不设置windowFilter,则当前窗口即为活跃的窗口(获取到焦点、正在触摸的窗口);如果设置了windowFilter,则获取的是过滤的窗口中的第一个窗口。

                                          \n

                                          如果系统是Android5.0以下,则始终返回当前活跃的窗口的布局根元素。

                                          \n" + }, + { + "textRaw": "auto.rootInActiveWindow", + "name": "rootInActiveWindow", + "desc": "

                                          [v4.1.0新增]

                                          \n
                                            \n
                                          • {UiObject}
                                          • \n
                                          \n

                                          当前活跃的窗口(获取到焦点、正在触摸的窗口)的布局根元素。如果无障碍服务未启动则为null

                                          \n" + }, + { + "textRaw": "auto.windowRoots", + "name": "windowRoots", + "desc": "

                                          [v4.1.0新增]

                                          \n
                                            \n
                                          • {Array}
                                          • \n
                                          \n

                                          返回当前被WindowFilter过滤的窗口的布局根元素组成的数组。

                                          \n

                                          如果系统是Android5.0以下,则始终返回当前活跃的窗口的布局根元素的数组。

                                          \n" + } + ], + "type": "module", + "displayName": "基于控件的操作" + }, + { + "textRaw": "SimpleActionAutomator", + "name": "simpleactionautomator", + "stability": 2, + "stabilityText": "Stable", + "desc": "

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

                                          \n", + "methods": [ + { + "textRaw": "click(text[, i])", + "type": "method", + "name": "click", + "signatures": [ + { + "params": [ + { + "textRaw": "`text` {string} 要点击的文本 ", + "name": "text", + "type": "string", + "desc": "要点击的文本" + }, + { + "textRaw": "`i` {number} 如果相同的文本在屏幕中出现多次,则i表示要点击第几个文本, i从0开始计算 ", + "name": "i", + "type": "number", + "desc": "如果相同的文本在屏幕中出现多次,则i表示要点击第几个文本, i从0开始计算", + "optional": true + } + ] + }, + { + "params": [ + { + "name": "text" + }, + { + "name": "i", + "optional": true + } + ] + } + ], + "desc": "

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

                                          \n

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

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

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

                                          \n

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

                                          \n
                                          \n

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

                                          \n
                                          \n" + }, + { + "textRaw": "click(left, top, bottom, right)", + "type": "method", + "name": "click", + "signatures": [ + { + "params": [ + { + "textRaw": "`left` {number} 要点击的长方形区域左边与屏幕左边的像素距离 ", + "name": "left", + "type": "number", + "desc": "要点击的长方形区域左边与屏幕左边的像素距离" + }, + { + "textRaw": "`top` {number} 要点击的长方形区域上边与屏幕上边的像素距离 ", + "name": "top", + "type": "number", + "desc": "要点击的长方形区域上边与屏幕上边的像素距离" + }, + { + "textRaw": "`bottom` {number} 要点击的长方形区域下边与屏幕下边的像素距离 ", + "name": "bottom", + "type": "number", + "desc": "要点击的长方形区域下边与屏幕下边的像素距离" + }, + { + "textRaw": "`right` {number} 要点击的长方形区域右边与屏幕右边的像素距离 ", + "name": "right", + "type": "number", + "desc": "要点击的长方形区域右边与屏幕右边的像素距离" + } + ] + }, + { + "params": [ + { + "name": "left" + }, + { + "name": "top" + }, + { + "name": "bottom" + }, + { + "name": "right" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n

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

                                          \n

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "scrollUp([i])", + "type": "method", + "name": "scrollUp", + "signatures": [ + { + "params": [ + { + "textRaw": "`i` {number} 要滑动的控件序号 ", + "name": "i", + "type": "number", + "desc": "要滑动的控件序号", + "optional": true + } + ] + }, + { + "params": [ + { + "name": "i", + "optional": true + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "scrollDown([i])", + "type": "method", + "name": "scrollDown", + "signatures": [ + { + "params": [ + { + "textRaw": "`i` {number} 要滑动的控件序号 ", + "name": "i", + "type": "number", + "desc": "要滑动的控件序号", + "optional": true + } + ] + }, + { + "params": [ + { + "name": "i", + "optional": true + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "setText([i, ]text)", + "type": "method", + "name": "setText", + "signatures": [ + { + "params": [ + { + "textRaw": "i {number} 表示要输入的为第i + 1个输入框 ", + "name": "i", + "type": "number", + "desc": "表示要输入的为第i + 1个输入框", + "optional": true + }, + { + "textRaw": "text {string} 要输入的文本 ", + "name": "text", + "type": "string", + "desc": "要输入的文本" + } + ] + }, + { + "params": [ + { + "name": "i", + "optional": true + }, + { + "name": "text" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "input([i, ]text)", + "type": "method", + "name": "input", + "signatures": [ + { + "params": [ + { + "textRaw": "i {number} 表示要输入的为第i + 1个输入框 ", + "name": "i", + "type": "number", + "desc": "表示要输入的为第i + 1个输入框", + "optional": true + }, + { + "textRaw": "text {string} 要输入的文本 ", + "name": "text", + "type": "string", + "desc": "要输入的文本" + } + ] + }, + { + "params": [ + { + "name": "i", + "optional": true + }, + { + "name": "text" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n" + } + ], + "modules": [ + { + "textRaw": "longClick(text[, i]))", + "name": "longclick(text[,_i]))", + "desc": "
                                            \n
                                          • text {string} 要长按的文本
                                          • \n
                                          • i {number} 如果相同的文本在屏幕中出现多次,则i表示要长按第几个文本, i从0开始计算
                                          • \n
                                          \n

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

                                          \n

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

                                          \n", + "type": "module", + "displayName": "longClick(text[, i]))" + } + ], + "type": "module", + "displayName": "SimpleActionAutomator" + }, + { + "textRaw": "UiSelector", + "name": "uiselector", + "desc": "

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

                                          \n

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

                                          \n

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

                                          \n
                                          var sendButton = text("发送").findOne();\nsendButton.click();\n

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

                                          \n

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

                                          \n

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

                                          \n

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

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

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

                                          \n

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

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

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

                                          \n

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

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

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

                                          \n

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

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

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

                                          \n
                                          while(true){\n    className("EditText").findOne().setText("刷屏...");\n    text("发送").findOne().clicK();\n}\n

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

                                          \n
                                          while(true){\n    className("EditText").setText("刷屏...");\n    text("发送").clicK();\n}\n

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

                                          \n

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

                                          \n

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

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

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

                                          \n

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

                                          \n", + "methods": [ + { + "textRaw": "selector()", + "type": "method", + "name": "selector", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {UiSelector} ", + "name": "返回", + "type": "UiSelector" + } + ] + }, + { + "params": [] + } + ], + "desc": "

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

                                          \n

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "UiSelector.algorithm(algorithm)", + "type": "method", + "name": "algorithm", + "desc": "

                                          [v4.1.0新增]

                                          \n
                                            \n
                                          • algorithm {string} 搜索算法,可选的值有:
                                              \n
                                            • DFS 深度优先算法,选择器的默认算法
                                            • \n
                                            • BFS 广度优先算法
                                            • \n
                                            \n
                                          • \n
                                          \n

                                          指定选择器的搜索算法。例如:

                                          \n
                                          log(selector().text("文本").algorithm("BFS").find());\n

                                          广度优先在控件所在层次较低时,或者布局的层次不多时,通常能更快找到控件。

                                          \n", + "signatures": [ + { + "params": [ + { + "name": "algorithm" + } + ] + } + ] + }, + { + "textRaw": "UiSelector.text(str)", + "type": "method", + "name": "text", + "signatures": [ + { + "params": [ + { + "textRaw": "`str` {string} 控件文本 ", + "name": "str", + "type": "string", + "desc": "控件文本" + }, + { + "textRaw": "返回 {UiSelector} 返回选择器自身以便链式调用 ", + "name": "返回", + "type": "UiSelector", + "desc": "返回选择器自身以便链式调用" + } + ] + }, + { + "params": [ + { + "name": "str" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "UiSelector.textContains(str)", + "type": "method", + "name": "textContains", + "signatures": [ + { + "params": [ + { + "textRaw": "`str` {string} 要包含的字符串 ", + "name": "str", + "type": "string", + "desc": "要包含的字符串" + } + ] + }, + { + "params": [ + { + "name": "str" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "UiSelector.textStartsWith(prefix)", + "type": "method", + "name": "textStartsWith", + "signatures": [ + { + "params": [ + { + "textRaw": "`prefix` {string} 前缀 ", + "name": "prefix", + "type": "string", + "desc": "前缀" + } + ] + }, + { + "params": [ + { + "name": "prefix" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "UiSelector.textEndsWith(suffix)", + "type": "method", + "name": "textEndsWith", + "signatures": [ + { + "params": [ + { + "textRaw": "suffix {string} 后缀 ", + "name": "suffix", + "type": "string", + "desc": "后缀" + } + ] + }, + { + "params": [ + { + "name": "suffix" + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "UiSelector.textMatches(reg)", + "type": "method", + "name": "textMatches", + "signatures": [ + { + "params": [ + { + "textRaw": "`reg` {string} | {Regex} 要满足的正则表达式。 ", + "name": "reg", + "type": "string", + "desc": "| {Regex} 要满足的正则表达式。" + } + ] + }, + { + "params": [ + { + "name": "reg" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "UiSelector.desc(str)", + "type": "method", + "name": "desc", + "signatures": [ + { + "params": [ + { + "textRaw": "`str` {string} 控件文本 ", + "name": "str", + "type": "string", + "desc": "控件文本" + }, + { + "textRaw": "返回 {UiSelector} 返回选择器自身以便链式调用 ", + "name": "返回", + "type": "UiSelector", + "desc": "返回选择器自身以便链式调用" + } + ] + }, + { + "params": [ + { + "name": "str" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "UiSelector.descContains(str)", + "type": "method", + "name": "descContains", + "signatures": [ + { + "params": [ + { + "textRaw": "`str` {string} 要包含的字符串 ", + "name": "str", + "type": "string", + "desc": "要包含的字符串" + } + ] + }, + { + "params": [ + { + "name": "str" + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "UiSelector.descStartsWith(prefix)", + "type": "method", + "name": "descStartsWith", + "signatures": [ + { + "params": [ + { + "textRaw": "`prefix` {string} 前缀 ", + "name": "prefix", + "type": "string", + "desc": "前缀" + } + ] + }, + { + "params": [ + { + "name": "prefix" + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "UiSelector.descEndsWith(suffix)", + "type": "method", + "name": "descEndsWith", + "signatures": [ + { + "params": [ + { + "textRaw": "`suffix` {string} 后缀 ", + "name": "suffix", + "type": "string", + "desc": "后缀" + } + ] + }, + { + "params": [ + { + "name": "suffix" + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "UiSelector.descMatches(reg)", + "type": "method", + "name": "descMatches", + "signatures": [ + { + "params": [ + { + "textRaw": "`reg` {string} | {Regex} 要满足的正则表达式。 ", + "name": "reg", + "type": "string", + "desc": "| {Regex} 要满足的正则表达式。" + } + ] + }, + { + "params": [ + { + "name": "reg" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "UiSelector.id(resId)", + "type": "method", + "name": "id", + "signatures": [ + { + "params": [ + { + "textRaw": "`resId` {string} 控件的id,以\"包名:id/\"开头,例如\"com.tencent.mm:id/send_btn\"。**也可以不指定包名**,这时会以当前正在运行的应用的包名来补全id。例如id(\"send_btn\"),在QQ界面想当于id(\"com.tencent.mobileqq:id/send_btn\")。 ", + "name": "resId", + "type": "string", + "desc": "控件的id,以\"包名:id/\"开头,例如\"com.tencent.mm:id/send_btn\"。**也可以不指定包名**,这时会以当前正在运行的应用的包名来补全id。例如id(\"send_btn\"),在QQ界面想当于id(\"com.tencent.mobileqq:id/send_btn\")。" + } + ] + }, + { + "params": [ + { + "name": "resId" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "UiSelector.idContains(str)", + "type": "method", + "name": "idContains", + "signatures": [ + { + "params": [ + { + "textRaw": "`str` {string} id要包含的字符串 ", + "name": "str", + "type": "string", + "desc": "id要包含的字符串" + } + ] + }, + { + "params": [ + { + "name": "str" + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "UiSelector.idStartsWith(prefix)", + "type": "method", + "name": "idStartsWith", + "signatures": [ + { + "params": [ + { + "textRaw": "`prefix` {string} id前缀 ", + "name": "prefix", + "type": "string", + "desc": "id前缀" + } + ] + }, + { + "params": [ + { + "name": "prefix" + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "UiSelector.idEndsWith(suffix)", + "type": "method", + "name": "idEndsWith", + "signatures": [ + { + "params": [ + { + "textRaw": "`suffix` {string} id后缀 ", + "name": "suffix", + "type": "string", + "desc": "id后缀" + } + ] + }, + { + "params": [ + { + "name": "suffix" + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "UiSelector.idMatches(reg)", + "type": "method", + "name": "idMatches", + "signatures": [ + { + "params": [ + { + "textRaw": "reg {Regex} | {string} id要满足的正则表达式 ", + "name": "reg", + "type": "Regex", + "desc": "| {string} id要满足的正则表达式" + } + ] + }, + { + "params": [ + { + "name": "reg" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n
                                          idMatches("[a-zA-Z]+")\n
                                          " + }, + { + "textRaw": "UiSelector.className(str)", + "type": "method", + "name": "className", + "signatures": [ + { + "params": [ + { + "textRaw": "`str` {string} 控件文本 ", + "name": "str", + "type": "string", + "desc": "控件文本" + }, + { + "textRaw": "返回 {UiSelector} 返回选择器自身以便链式调用 ", + "name": "返回", + "type": "UiSelector", + "desc": "返回选择器自身以便链式调用" + } + ] + }, + { + "params": [ + { + "name": "str" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n

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

                                          \n

                                          常见控件的类名如下:

                                          \n
                                            \n
                                          • android.widget.TextView 文本控件
                                          • \n
                                          • android.widget.ImageView 图片控件
                                          • \n
                                          • android.widget.Button 按钮控件
                                          • \n
                                          • android.widget.EditText 输入框控件
                                          • \n
                                          • android.widget.AbsListView 列表控件
                                          • \n
                                          • android.widget.LinearLayout 线性布局
                                          • \n
                                          • android.widget.FrameLayout 帧布局
                                          • \n
                                          • android.widget.RelativeLayout 相对布局
                                          • \n
                                          • android.widget.RelativeLayout 相对布局
                                          • \n
                                          • android.support.v7.widget.RecyclerView 通常也是列表控件
                                          • \n
                                          \n" + }, + { + "textRaw": "UiSelector.classNameContains(str)", + "type": "method", + "name": "classNameContains", + "signatures": [ + { + "params": [ + { + "textRaw": "`str` {string} 要包含的字符串 ", + "name": "str", + "type": "string", + "desc": "要包含的字符串" + } + ] + }, + { + "params": [ + { + "name": "str" + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "UiSelector.classNameStartsWith(prefix)", + "type": "method", + "name": "classNameStartsWith", + "signatures": [ + { + "params": [ + { + "textRaw": "`prefix` {string} 前缀 ", + "name": "prefix", + "type": "string", + "desc": "前缀" + } + ] + }, + { + "params": [ + { + "name": "prefix" + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "UiSelector.classNameEndsWith(suffix)", + "type": "method", + "name": "classNameEndsWith", + "signatures": [ + { + "params": [ + { + "textRaw": "`suffix` {string} 后缀 ", + "name": "suffix", + "type": "string", + "desc": "后缀" + } + ] + }, + { + "params": [ + { + "name": "suffix" + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "UiSelector.classNameMatches(reg)", + "type": "method", + "name": "classNameMatches", + "signatures": [ + { + "params": [ + { + "textRaw": "`reg` {string} | {Regex} 要满足的正则表达式。 ", + "name": "reg", + "type": "string", + "desc": "| {Regex} 要满足的正则表达式。" + } + ] + }, + { + "params": [ + { + "name": "reg" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "UiSelector.packageName(str)", + "type": "method", + "name": "packageName", + "signatures": [ + { + "params": [ + { + "textRaw": "`str` {string} 控件文本 ", + "name": "str", + "type": "string", + "desc": "控件文本" + }, + { + "textRaw": "返回 {UiSelector} 返回选择器自身以便链式调用 ", + "name": "返回", + "type": "UiSelector", + "desc": "返回选择器自身以便链式调用" + } + ] + }, + { + "params": [ + { + "name": "str" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "UiSelector.packageNameContains(str)", + "type": "method", + "name": "packageNameContains", + "signatures": [ + { + "params": [ + { + "textRaw": "`str` {string} 要包含的字符串 ", + "name": "str", + "type": "string", + "desc": "要包含的字符串" + } + ] + }, + { + "params": [ + { + "name": "str" + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "UiSelector.packageNameStartsWith(prefix)", + "type": "method", + "name": "packageNameStartsWith", + "signatures": [ + { + "params": [ + { + "textRaw": "`prefix` {string} 前缀 ", + "name": "prefix", + "type": "string", + "desc": "前缀" + } + ] + }, + { + "params": [ + { + "name": "prefix" + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "UiSelector.packageNameEndsWith(suffix)", + "type": "method", + "name": "packageNameEndsWith", + "signatures": [ + { + "params": [ + { + "textRaw": "`suffix` {string} 后缀 ", + "name": "suffix", + "type": "string", + "desc": "后缀" + } + ] + }, + { + "params": [ + { + "name": "suffix" + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "UiSelector.packageNameMatches(reg)", + "type": "method", + "name": "packageNameMatches", + "signatures": [ + { + "params": [ + { + "textRaw": "`reg` {string} | {Regex} 要满足的正则表达式。 ", + "name": "reg", + "type": "string", + "desc": "| {Regex} 要满足的正则表达式。" + } + ] + }, + { + "params": [ + { + "name": "reg" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "UiSelector.bounds(left, top, right, buttom)", + "type": "method", + "name": "bounds", + "signatures": [ + { + "params": [ + { + "textRaw": "`left` {number} 控件左边缘与屏幕左边的距离 ", + "name": "left", + "type": "number", + "desc": "控件左边缘与屏幕左边的距离" + }, + { + "textRaw": "`top` {number} 控件上边缘与屏幕上边的距离 ", + "name": "top", + "type": "number", + "desc": "控件上边缘与屏幕上边的距离" + }, + { + "textRaw": "`right` {number} 控件右边缘与屏幕左边的距离 ", + "name": "right", + "type": "number", + "desc": "控件右边缘与屏幕左边的距离" + }, + { + "textRaw": "`bottom` {number} 控件下边缘与屏幕上边的距离 ", + "name": "bottom", + "type": "number", + "desc": "控件下边缘与屏幕上边的距离" + } + ] + }, + { + "params": [ + { + "name": "left" + }, + { + "name": "top" + }, + { + "name": "right" + }, + { + "name": "buttom" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "UiSelector.boundsInside(left, top, right, buttom)", + "type": "method", + "name": "boundsInside", + "signatures": [ + { + "params": [ + { + "textRaw": "`left` {number} 范围左边缘与屏幕左边的距离 ", + "name": "left", + "type": "number", + "desc": "范围左边缘与屏幕左边的距离" + }, + { + "textRaw": "`top` {number} 范围上边缘与屏幕上边的距离 ", + "name": "top", + "type": "number", + "desc": "范围上边缘与屏幕上边的距离" + }, + { + "textRaw": "`right` {number} 范围右边缘与屏幕左边的距离 ", + "name": "right", + "type": "number", + "desc": "范围右边缘与屏幕左边的距离" + }, + { + "textRaw": "`bottom` {number} 范围下边缘与屏幕上边的距离 ", + "name": "bottom", + "type": "number", + "desc": "范围下边缘与屏幕上边的距离" + } + ] + }, + { + "params": [ + { + "name": "left" + }, + { + "name": "top" + }, + { + "name": "right" + }, + { + "name": "buttom" + } + ] + } + ], + "desc": "

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

                                          \n

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

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

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

                                          \n" + }, + { + "textRaw": "UiSelector.boundsContains(left, top, right, buttom)", + "type": "method", + "name": "boundsContains", + "signatures": [ + { + "params": [ + { + "textRaw": "`left` {number} 范围左边缘与屏幕左边的距离 ", + "name": "left", + "type": "number", + "desc": "范围左边缘与屏幕左边的距离" + }, + { + "textRaw": "`top` {number} 范围上边缘与屏幕上边的距离 ", + "name": "top", + "type": "number", + "desc": "范围上边缘与屏幕上边的距离" + }, + { + "textRaw": "`right` {number} 范围右边缘与屏幕左边的距离 ", + "name": "right", + "type": "number", + "desc": "范围右边缘与屏幕左边的距离" + }, + { + "textRaw": "`bottom` {number} 范围下边缘与屏幕上边的距离 ", + "name": "bottom", + "type": "number", + "desc": "范围下边缘与屏幕上边的距离" + } + ] + }, + { + "params": [ + { + "name": "left" + }, + { + "name": "top" + }, + { + "name": "right" + }, + { + "name": "buttom" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n
                                          var w = boundsContains(500, 300, 500, 300).clickable().findOne();\nw.click();\n
                                          " + }, + { + "textRaw": "UiSelector.drawingOrder(order)", + "type": "method", + "name": "drawingOrder", + "signatures": [ + { + "params": [ + { + "textRaw": "order {number} 控件在父视图中的绘制顺序 ", + "name": "order", + "type": "number", + "desc": "控件在父视图中的绘制顺序" + } + ] + }, + { + "params": [ + { + "name": "order" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "UiSelector.clickable([b = true])", + "type": "method", + "name": "clickable", + "signatures": [ + { + "params": [ + { + "textRaw": "`b` {Boolean} 表示控件是否可点击 ", + "name": "b", + "type": "Boolean", + "desc": "表示控件是否可点击", + "optional": true, + "default": " true" + } + ] + }, + { + "params": [ + { + "name": "b ", + "optional": true, + "default": " true" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "UiSelector.longClickable([b = true])", + "type": "method", + "name": "longClickable", + "signatures": [ + { + "params": [ + { + "textRaw": "`b` {Boolean} 表示控件是否可长按 ", + "name": "b", + "type": "Boolean", + "desc": "表示控件是否可长按", + "optional": true, + "default": " true" + } + ] + }, + { + "params": [ + { + "name": "b ", + "optional": true, + "default": " true" + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "UiSelector.checkable([b = true])", + "type": "method", + "name": "checkable", + "signatures": [ + { + "params": [ + { + "textRaw": "`b` {Boolean} 表示控件是否可勾选 ", + "name": "b", + "type": "Boolean", + "desc": "表示控件是否可勾选", + "optional": true, + "default": " true" + } + ] + }, + { + "params": [ + { + "name": "b ", + "optional": true, + "default": " true" + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "UiSelector.selected([b = true])", + "type": "method", + "name": "selected", + "signatures": [ + { + "params": [ + { + "textRaw": "`b` {Boolean} 表示控件是否被选 ", + "name": "b", + "type": "Boolean", + "desc": "表示控件是否被选", + "optional": true, + "default": " true" + } + ] + }, + { + "params": [ + { + "name": "b ", + "optional": true, + "default": " true" + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "UiSelector.enabled([b = true])", + "type": "method", + "name": "enabled", + "signatures": [ + { + "params": [ + { + "textRaw": "`b` {Boolean} 表示控件是否已启用 ", + "name": "b", + "type": "Boolean", + "desc": "表示控件是否已启用", + "optional": true, + "default": " true" + } + ] + }, + { + "params": [ + { + "name": "b ", + "optional": true, + "default": " true" + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "UiSelector.scrollable([b = true])", + "type": "method", + "name": "scrollable", + "signatures": [ + { + "params": [ + { + "textRaw": "`b` {Boolean} 表示控件是否可滑动 ", + "name": "b", + "type": "Boolean", + "desc": "表示控件是否可滑动", + "optional": true, + "default": " true" + } + ] + }, + { + "params": [ + { + "name": "b ", + "optional": true, + "default": " true" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n
                                          className("android.support.v7.widget.RecyclerView").scrollable().findOne().scrollForward();\n//或者classNameEndsWith("RecyclerView").scrollable().findOne().scrollForward();\n
                                          " + }, + { + "textRaw": "UiSelector.editable([b = true])", + "type": "method", + "name": "editable", + "signatures": [ + { + "params": [ + { + "textRaw": "`b` {Boolean} 表示控件是否可编辑 ", + "name": "b", + "type": "Boolean", + "desc": "表示控件是否可编辑", + "optional": true, + "default": " true" + } + ] + }, + { + "params": [ + { + "name": "b ", + "optional": true, + "default": " true" + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "UiSelector.multiLine([b = true])", + "type": "method", + "name": "multiLine", + "signatures": [ + { + "params": [ + { + "textRaw": "b {Boolean} 表示文本或输入框控件是否是多行显示的 ", + "name": "b", + "type": "Boolean", + "desc": "表示文本或输入框控件是否是多行显示的", + "optional": true, + "default": " true" + } + ] + }, + { + "params": [ + { + "name": "b ", + "optional": true, + "default": " true" + } + ] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "UiSelector.findOne()", + "type": "method", + "name": "findOne", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 [UiObject](#widgets_based_automation_uiobject) ", + "name": "返回", + "desc": "[UiObject](#widgets_based_automation_uiobject)" + } + ] + }, + { + "params": [] + } + ], + "desc": "

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

                                          \n

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

                                          \n

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "UiSelector.findOne(timeout)", + "type": "method", + "name": "findOne", + "signatures": [ + { + "params": [ + { + "textRaw": "`timeout` {number} 搜索的超时时间,单位毫秒 ", + "name": "timeout", + "type": "number", + "desc": "搜索的超时时间,单位毫秒" + }, + { + "textRaw": "返回 [UiObject](#widgets_based_automation_uiobject) ", + "name": "返回", + "desc": "[UiObject](#widgets_based_automation_uiobject)" + } + ] + }, + { + "params": [ + { + "name": "timeout" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n

                                          示例:

                                          \n
                                          //启动Auto.js\nlaunchApp("Auto.js");\n//在6秒内找出日志图标的控件\nvar w = id("action_log").findOne(6000);\n//如果找到控件则点击\nif(w != null){\n    w.click();\n}else{\n    //否则提示没有找到\n    toast("没有找到日志图标");\n}\n
                                          " + }, + { + "textRaw": "UiSelector.findOnce()", + "type": "method", + "name": "findOnce", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 [UiObject](#widgets_based_automation_uiobject) ", + "name": "返回", + "desc": "[UiObject](#widgets_based_automation_uiobject)" + } + ] + }, + { + "params": [] + } + ], + "desc": "

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

                                          \n" + }, + { + "textRaw": "UiSelector.findOnce(i)", + "type": "method", + "name": "findOnce", + "signatures": [ + { + "params": [ + { + "textRaw": "`i` {number} 索引 ", + "name": "i", + "type": "number", + "desc": "索引" + } + ] + }, + { + "params": [ + { + "name": "i" + } + ] + } + ], + "desc": "

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

                                          \n

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

                                          \n" + }, + { + "textRaw": "UiSelector.find()", + "type": "method", + "name": "find", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 [UiCollection](#widgets_based_automation_uicollection) ", + "name": "返回", + "desc": "[UiCollection](#widgets_based_automation_uicollection)" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          根据当前的选择器所确定的筛选条件,对屏幕上的控件进行搜索,找到所有满足条件的控件集合并返回。这个搜索只进行一次,并不保证一定会找到,因而会出现返回的控件集合为空的情况。

                                          \n

                                          不同于findOne()或者findOnce()只找到一个控件并返回一个控件,find()函数会找出所有满足条件的控件并返回一个控件集合。之后可以对控件集合进行操作。

                                          \n

                                          可以通过empty()函数判断找到的是否为空。例如:

                                          \n
                                          var c = className("AbsListView").find();\nif(c.empty()){\n    toast("找到啦");\n}else{\n    toast("没找到╭(╯^╰)╮");\n}\n
                                          " + }, + { + "textRaw": "UiSelector.untilFind()", + "type": "method", + "name": "untilFind", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 [UiCollection](#widgets_based_automation_uicollection) ", + "name": "返回", + "desc": "[UiCollection](#widgets_based_automation_uicollection)" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          根据当前的选择器所确定的筛选条件,对屏幕上的控件进行搜索,直到找到至少一个满足条件的控件为止,并返回所有满足条件的控件集合。

                                          \n

                                          该函数与find()函数的区别在于,该函数永远不会返回空集合;但是,如果屏幕上一直没有出现满足条件的控件,则该函数会保持阻塞。

                                          \n" + }, + { + "textRaw": "UiSelector.exists()", + "type": "method", + "name": "exists", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {Boolean} ", + "name": "返回", + "type": "Boolean" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          判断屏幕上是否存在控件符合选择器所确定的条件。例如要判断某个文本出现就执行某个动作,可以用:

                                          \n
                                          if(text("某个文本").exists()){\n    //要支持的动作\n}\n
                                          " + }, + { + "textRaw": "UiSelector.waitFor()", + "type": "method", + "name": "waitFor", + "desc": "

                                          等待屏幕上出现符合条件的控件;在满足该条件的控件出现之前,该函数会一直保持阻塞。

                                          \n

                                          例如要等待包含"哈哈哈"的文本控件出现的代码为:

                                          \n
                                          textContains("哈哈哈").waitFor();\n
                                          ", + "signatures": [ + { + "params": [] + } + ] + }, + { + "textRaw": "UiSelector.filter(f)", + "type": "method", + "name": "filter", + "signatures": [ + { + "params": [ + { + "textRaw": "`f` {Function} 过滤函数,参数为UiObject,返回值为boolean ", + "name": "f", + "type": "Function", + "desc": "过滤函数,参数为UiObject,返回值为boolean" + } + ] + }, + { + "params": [ + { + "name": "f" + } + ] + } + ], + "desc": "

                                          为当前选择器附加自定义的过滤条件。

                                          \n

                                          例如,要找出屏幕上所有文本长度为10的文本控件的代码为:

                                          \n
                                          var uc = className("TextView").filter(function(w){\n    return w.text().length == 10;\n});\n
                                          " + } + ], + "type": "module", + "displayName": "UiSelector" + }, + { + "textRaw": "UiObject", + "name": "uiobject", + "desc": "

                                          UiObject表示一个控件,可以通过这个对象获取到控件的属性,也可以对控件进行点击、长按等操作。

                                          \n

                                          获取一个UiObject通常通过选择器的findOne(), findOnce()等函数,也可以通过UiCollection来获取,或者通过UiObject.child(), UiObject.parent()等函数来获取一个控件的子控件或父控件。

                                          \n", + "methods": [ + { + "textRaw": "UiObject.click()", + "type": "method", + "name": "click", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {Boolean} ", + "name": "返回", + "type": "Boolean" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          点击该控件,并返回是否点击成功。

                                          \n

                                          如果该函数返回false,可能是该控件不可点击(clickable为false),当前界面无法响应该点击等。

                                          \n" + }, + { + "textRaw": "UiObject.longClick()", + "type": "method", + "name": "longClick", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {Boolean} ", + "name": "返回", + "type": "Boolean" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          长按该控件,并返回是否点击成功。

                                          \n

                                          如果该函数返回false,可能是该控件不可点击(longClickable为false),当前界面无法响应该点击等。

                                          \n" + }, + { + "textRaw": "UiObject.setText(text)", + "type": "method", + "name": "setText", + "signatures": [ + { + "params": [ + { + "textRaw": "`text` {string} 文本 ", + "name": "text", + "type": "string", + "desc": "文本" + }, + { + "textRaw": "返回 {Boolean} ", + "name": "返回", + "type": "Boolean" + } + ] + }, + { + "params": [ + { + "name": "text" + } + ] + } + ], + "desc": "

                                          设置输入框控件的文本内容,并返回是否设置成功。

                                          \n

                                          该函数只对可编辑的输入框(editable为true)有效。

                                          \n" + }, + { + "textRaw": "UiObject.copy()", + "type": "method", + "name": "copy", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {Boolean} ", + "name": "返回", + "type": "Boolean" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          对输入框文本的选中内容进行复制,并返回是否操作成功。

                                          \n

                                          该函数只能用于输入框控件,并且当前输入框控件有选中的文本。可以通过setSelection()函数来设置输入框选中的内容。

                                          \n
                                          var et = className("EditText").findOne();\n//选中前两个字\net.setSelection(0, 2);\n//对选中内容进行复制\nif(et.copy()){\n    toast("复制成功");\n}else{\n    toast("复制失败");\n}\n
                                          " + }, + { + "textRaw": "UiObject.cut()", + "type": "method", + "name": "cut", + "desc": "

                                          对输入框文本的选中内容进行剪切,并返回是否操作成功。

                                          \n

                                          该函数只能用于输入框控件,并且当前输入框控件有选中的文本。可以通过setSelection()函数来设置输入框选中的内容。

                                          \n", + "signatures": [ + { + "params": [] + } + ] + }, + { + "textRaw": "UiObject.paste()", + "type": "method", + "name": "paste", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {Boolean} ", + "name": "返回", + "type": "Boolean" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          对输入框控件进行粘贴操作,把剪贴板内容粘贴到输入框中,并返回是否操作成功。

                                          \n
                                          //设置剪贴板内容为“你好”\nsetClip("你好");\nvar et = className("EditText").findOne();\net.paste();\n
                                          " + }, + { + "textRaw": "UiObject.setSelection(start, end)", + "type": "method", + "name": "setSelection", + "signatures": [ + { + "params": [ + { + "textRaw": "`start` {number} 选中内容起始位置 ", + "name": "start", + "type": "number", + "desc": "选中内容起始位置" + }, + { + "textRaw": "`end` {number} 选中内容结束位置(不包括) ", + "name": "end", + "type": "number", + "desc": "选中内容结束位置(不包括)" + }, + { + "textRaw": "返回 {Boolean} ", + "name": "返回", + "type": "Boolean" + } + ] + }, + { + "params": [ + { + "name": "start" + }, + { + "name": "end" + } + ] + } + ], + "desc": "

                                          对输入框控件设置选中的文字内容,并返回是否操作成功。

                                          \n

                                          索引是从0开始计算的;并且,选中内容不包含end位置的字符。例如,如果一个输入框内容为"123456789",要选中"4567"的文字的代码为et.setSelection(3, 7)

                                          \n

                                          该函数也可以用来设置光标位置,只要参数的end等于start,即可把输入框光标设置在start的位置。例如et.setSelection(1, 1)会把光标设置在第一个字符的后面。

                                          \n" + }, + { + "textRaw": "UiObject.scrollForward()", + "type": "method", + "name": "scrollForward", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {Boolean} ", + "name": "返回", + "type": "Boolean" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          对控件执行向前滑动的操作,并返回是否操作成功。

                                          \n

                                          向前滑动包括了向右和向下滑动。如果一个控件既可以向右滑动和向下滑动,那么执行scrollForward()的行为是未知的(这是因为Android文档没有指出这一点,同时也没有充分的测试可供参考)。

                                          \n" + }, + { + "textRaw": "UiObject.scrollBackward()", + "type": "method", + "name": "scrollBackward", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {Boolean} ", + "name": "返回", + "type": "Boolean" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          对控件执行向后滑动的操作,并返回是否操作成功。

                                          \n

                                          向后滑动包括了向右和向下滑动。如果一个控件既可以向右滑动和向下滑动,那么执行scrollForward()的行为是未知的(这是因为Android文档没有指出这一点,同时也没有充分的测试可供参考)。

                                          \n" + }, + { + "textRaw": "UiObject.select()", + "type": "method", + "name": "select", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {Boolean} ", + "name": "返回", + "type": "Boolean" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          对控件执行"选中"操作,并返回是否操作成功。"选中"和isSelected()的属性相关,但该操作十分少用。

                                          \n" + }, + { + "textRaw": "UiObject.collapse()", + "type": "method", + "name": "collapse", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {Boolean} ", + "name": "返回", + "type": "Boolean" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          对控件执行折叠操作,并返回是否操作成功。

                                          \n" + }, + { + "textRaw": "UiObject.expand()", + "type": "method", + "name": "expand", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {Boolean} ", + "name": "返回", + "type": "Boolean" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          对控件执行操作,并返回是否操作成功。

                                          \n" + }, + { + "textRaw": "UiObject.show()", + "type": "method", + "name": "show", + "desc": "

                                          对集合中所有控件执行显示操作,并返回是否全部操作成功。

                                          \n", + "signatures": [ + { + "params": [] + } + ] + }, + { + "textRaw": "UiObject.scrollUp()", + "type": "method", + "name": "scrollUp", + "desc": "

                                          对集合中所有控件执行向上滑的操作,并返回是否全部操作成功。

                                          \n", + "signatures": [ + { + "params": [] + } + ] + }, + { + "textRaw": "UiObject.scrollDown()", + "type": "method", + "name": "scrollDown", + "desc": "

                                          对集合中所有控件执行向下滑的操作,并返回是否全部操作成功。

                                          \n", + "signatures": [ + { + "params": [] + } + ] + }, + { + "textRaw": "UiObject.scrollLeft()", + "type": "method", + "name": "scrollLeft", + "desc": "

                                          对集合中所有控件执行向左滑的操作,并返回是否全部操作成功。

                                          \n", + "signatures": [ + { + "params": [] + } + ] + }, + { + "textRaw": "UiObject.scrollRight()", + "type": "method", + "name": "scrollRight", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 [UiCollection](#widgets_based_automation_uicollection) ", + "name": "返回", + "desc": "[UiCollection](#widgets_based_automation_uicollection)" + } + ] + }, + { + "params": [] + }, + { + "params": [] + } + ], + "desc": "

                                          返回该控件的所有子控件组成的控件集合。可以用于遍历一个控件的子控件,例如:

                                          \n
                                          className("AbsListView").findOne().children()\n    .forEach(function(child){\n        log(child.className());\n    });\n
                                          " + }, + { + "textRaw": "children()", + "type": "method", + "name": "children", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 [UiCollection](#widgets_based_automation_uicollection) ", + "name": "返回", + "desc": "[UiCollection](#widgets_based_automation_uicollection)" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          返回该控件的所有子控件组成的控件集合。可以用于遍历一个控件的子控件,例如:

                                          \n
                                          className("AbsListView").findOne().children()\n    .forEach(function(child){\n        log(child.className());\n    });\n
                                          " + }, + { + "textRaw": "childCount()", + "type": "method", + "name": "childCount", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {number} ", + "name": "返回", + "type": "number" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          返回子控件数目。

                                          \n" + }, + { + "textRaw": "child(i)", + "type": "method", + "name": "child", + "signatures": [ + { + "params": [ + { + "textRaw": "i {number} 子控件索引 ", + "name": "i", + "type": "number", + "desc": "子控件索引" + }, + { + "textRaw": "返回 {UiObject} ", + "name": "返回", + "type": "UiObject" + } + ] + }, + { + "params": [ + { + "name": "i" + } + ] + } + ], + "desc": "

                                          返回第i+1个子控件。如果i>=控件数目或者小于0,则抛出异常。

                                          \n

                                          需要注意的是,由于布局捕捉的问题,该函数可能返回null,也就是可能获取不到某个子控件。

                                          \n

                                          遍历子控件的示例:

                                          \n
                                          var list = className("AbsListView").findOne();\nfor(var i = 0; i < list.childCount(); i++){\n    var child = list.child(i);\n    log(child.className());\n}\n
                                          " + }, + { + "textRaw": "parent()", + "type": "method", + "name": "parent", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {UiObject} ", + "name": "返回", + "type": "UiObject" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          返回该控件的父控件。如果该控件没有父控件,返回null

                                          \n" + }, + { + "textRaw": "bounds()", + "type": "method", + "name": "bounds", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 [Rect](https://hyb1996.github.io/AutoJs-Docs/widgets-based-automation.html#widgets_based_automation_rect) ", + "name": "返回", + "desc": "[Rect](https://hyb1996.github.io/AutoJs-Docs/widgets-based-automation.html#widgets_based_automation_rect)" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          返回控件在屏幕上的范围,其值是一个Rect对象。

                                          \n

                                          示例:

                                          \n
                                          var b = text("Auto.js").findOne().bounds();\ntoast("控件在屏幕上的范围为" + b);\n

                                          如果一个控件本身无法通过click()点击,那么我们可以利用bounds()函数获取其坐标,再利用坐标点击。例如:

                                          \n
                                          var b = desc("打开侧拉菜单").findOne().bounds();\nclick(b.centerX(), b.centerY());\n//如果使用root权限,则用 Tap(b.centerX(), b.centerY());\n
                                          " + }, + { + "textRaw": "boundsInParent()", + "type": "method", + "name": "boundsInParent", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 [Rect](https://hyb1996.github.io/AutoJs-Docs/widgets-based-automation.html#widgets_based_automation_rect) ", + "name": "返回", + "desc": "[Rect](https://hyb1996.github.io/AutoJs-Docs/widgets-based-automation.html#widgets_based_automation_rect)" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          返回控件在父控件中的范围,其值是一个Rect对象。

                                          \n" + }, + { + "textRaw": "drawingOrder()", + "type": "method", + "name": "drawingOrder", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {number} ", + "name": "返回", + "type": "number" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          返回控件在父控件中的绘制次序。该函数在安卓7.0及以上才有效,7.0以下版本调用会返回0。

                                          \n" + }, + { + "textRaw": "id()", + "type": "method", + "name": "id", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {string} ", + "name": "返回", + "type": "string" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          获取控件的id,如果一个控件没有id,则返回null

                                          \n" + }, + { + "textRaw": "text()", + "type": "method", + "name": "text", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {string} ", + "name": "返回", + "type": "string" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          获取控件的文本,如果控件没有文本,返回""

                                          \n" + }, + { + "textRaw": "findByText(str)", + "type": "method", + "name": "findByText", + "signatures": [ + { + "params": [ + { + "textRaw": "`str` {string} 文本 ", + "name": "str", + "type": "string", + "desc": "文本" + }, + { + "textRaw": "返回 [UiCollection](#widgets_based_automation_uicollection) ", + "name": "返回", + "desc": "[UiCollection](#widgets_based_automation_uicollection)" + } + ] + }, + { + "params": [ + { + "name": "str" + } + ] + } + ], + "desc": "

                                          根据文本text在子控件中递归地寻找并返回文本或描述(desc)包含这段文本str的控件,返回它们组成的集合。

                                          \n

                                          该函数会在当前控件的子控件,孙控件,曾孙控件...中搜索text或desc包含str的控件,并返回它们组合的集合。

                                          \n" + }, + { + "textRaw": "findOne(selector)", + "type": "method", + "name": "findOne", + "signatures": [ + { + "params": [ + { + "textRaw": "`selector` [UiSelector](#widgets_based_automation_uiselector) ", + "name": "selector", + "desc": "[UiSelector](#widgets_based_automation_uiselector)" + }, + { + "textRaw": "返回 [UiOobject](#widgets_based_automation_uiobject) ", + "name": "返回", + "desc": "[UiOobject](#widgets_based_automation_uiobject)" + } + ] + }, + { + "params": [ + { + "name": "selector" + } + ] + } + ], + "desc": "

                                          根据选择器selector在该控件的子控件、孙控件...中搜索符合该选择器条件的控件,并返回找到的第一个控件;如果没有找到符合条件的控件则返回null

                                          \n

                                          例如,对于酷安动态列表,我们可以遍历他的子控件(每个动态列表项),并在每个子控件中依次寻找点赞数量和图标,对于点赞数量小于10的点赞:

                                          \n
                                          //找出动态列表\nvar list = id("recycler_view").findOne();\n//遍历动态\nlist.children().forEach(function(child){\n    //找出点赞图标\n    var like = child.findOne(id("feed_action_view_like"));\n    //找出点赞数量\n    var likeCount = child.findOne(id("text_view"));\n    //如果这两个控件没有找到就不继续了\n    if(like == null || likeCount == null){\n        return;\n    }\n    //判断点赞数量是否小于10\n    if(parseInt(likeCount.text()) < 10){\n        //点赞\n        like.click();\n    }\n});\n
                                          " + }, + { + "textRaw": "find(selector)", + "type": "method", + "name": "find", + "signatures": [ + { + "params": [ + { + "textRaw": "`selector` [UiSelector](#widgets_based_automation_uiselector) ", + "name": "selector", + "desc": "[UiSelector](#widgets_based_automation_uiselector)" + }, + { + "textRaw": "返回 [UiCollection](#widgets_based_automation_uicollection) ", + "name": "返回", + "desc": "[UiCollection](#widgets_based_automation_uicollection)" + } + ] + }, + { + "params": [ + { + "name": "selector" + } + ] + } + ], + "desc": "

                                          根据选择器selector在该控件的子控件、孙控件...中搜索符合该选择器条件的控件,并返回它们组合的集合。

                                          \n" + } + ], + "type": "module", + "displayName": "UiObject" + }, + { + "textRaw": "UiCollection", + "name": "uicollection", + "desc": "

                                          UiCollection, 控件集合, 通过选择器的find(), untilFind()方法返回的对象。

                                          \n

                                          UiCollection"继承"于数组,实际上是一个UiObject的数组,因此可以使用数组的函数和属性,例如使用length属性获取UiCollection的大小,使用forEach函数来遍历UiCollection。

                                          \n

                                          例如,采用forEach遍历屏幕上所有的文本控件并打印出文本内容的代码为:

                                          \n
                                          console.show();\nclassName("TextView").find().forEach(function(tv){\n    if(tv.text() != ""){\n        log(tv.text());\n    }\n});\n

                                          也可以使用传统的数组遍历方式:

                                          \n
                                          console.show();\nvar uc = className("TextView").find();\nfor(var i = 0; i < uc.length; i++){\n    var tv = uc[i];\n    if(tv.text() != ""){\n        log(tv.text());\n    }\n}\n

                                          UiCollection的每一个元素都是UiObject,我们可以取出他的元素进行操作,例如取出第一个UiObject并点击的代码为ui[0].click()。如果想要对该集合的所有元素进行操作,可以直接在集合上调用相应的函数,例如uc.click(),该代码会对集合上所有UiObject执行点击操作并返回是否全部点击成功。

                                          \n

                                          因此,UiCollection具有所有UiObject对控件操作的函数,包括click(), longClick(), scrollForward()等等,不再赘述。

                                          \n", + "methods": [ + { + "textRaw": "UiCollection.size()", + "type": "method", + "name": "size", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {number} ", + "name": "返回", + "type": "number" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          返回集合中的控件数。

                                          \n

                                          历史遗留函数,相当于属性length。

                                          \n" + }, + { + "textRaw": "UiCollection.get(i)", + "type": "method", + "name": "get", + "signatures": [ + { + "params": [ + { + "textRaw": "`i` {number} 索引 ", + "name": "i", + "type": "number", + "desc": "索引" + }, + { + "textRaw": "返回 [UiObject](#widgets_based_automation_uiobject) ", + "name": "返回", + "desc": "[UiObject](#widgets_based_automation_uiobject)" + } + ] + }, + { + "params": [ + { + "name": "i" + } + ] + } + ], + "desc": "

                                          返回集合中第i+1个控件(UiObject)。

                                          \n

                                          历史遗留函数,建议直接使用数组下标的方式访问元素。

                                          \n" + }, + { + "textRaw": "UiCollection.each(func)", + "type": "method", + "name": "each", + "signatures": [ + { + "params": [ + { + "textRaw": "`func` {Function} 遍历函数,参数为UiObject。 ", + "name": "func", + "type": "Function", + "desc": "遍历函数,参数为UiObject。" + } + ] + }, + { + "params": [ + { + "name": "func" + } + ] + } + ], + "desc": "

                                          遍历集合。

                                          \n

                                          历史遗留函数,相当于forEach。参考forEach

                                          \n" + }, + { + "textRaw": "empty()", + "type": "method", + "name": "empty", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {Boolean} ", + "name": "返回", + "type": "Boolean" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          返回控件集合是否为空。

                                          \n" + }, + { + "textRaw": "nonEmpty()", + "type": "method", + "name": "nonEmpty", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {Boolean} ", + "name": "返回", + "type": "Boolean" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          返回控件集合是否非空。

                                          \n" + }, + { + "textRaw": "UiCollection.find(selector)", + "type": "method", + "name": "find", + "signatures": [ + { + "params": [ + { + "textRaw": "`selector` [UiSelector](#widgets_based_automation_uiselector) ", + "name": "selector", + "desc": "[UiSelector](#widgets_based_automation_uiselector)" + }, + { + "textRaw": "返回 [UiCollection](#widgets_based_automation_uicollection) ", + "name": "返回", + "desc": "[UiCollection](#widgets_based_automation_uicollection)" + } + ] + }, + { + "params": [ + { + "name": "selector" + } + ] + } + ], + "desc": "

                                          根据selector所确定的条件在该控件集合的控件、子控件、孙控件...中找到所有符合条件的控件并返回找到的控件集合。

                                          \n

                                          注意这会递归地遍历控件集合里所有的控件以及他们的子控件。和数组的filter函数不同。

                                          \n

                                          例如:

                                          \n
                                          var names = id("name").find();\n//在集合\nvar clickableNames = names.find(clickable());\n
                                          " + }, + { + "textRaw": "UiCollection.findOne(selector)", + "type": "method", + "name": "findOne", + "signatures": [ + { + "params": [ + { + "textRaw": "`selector` [UiSelector](#widgets_based_automation_uiselector) ", + "name": "selector", + "desc": "[UiSelector](#widgets_based_automation_uiselector)" + }, + { + "textRaw": "返回 [UiOobject](#widgets_based_automation_uiobject) ", + "name": "返回", + "desc": "[UiOobject](#widgets_based_automation_uiobject)" + } + ] + }, + { + "params": [ + { + "name": "selector" + } + ] + } + ], + "desc": "

                                          根据选择器selector在该控件集合的控件的子控件、孙控件...中搜索符合该选择器条件的控件,并返回找到的第一个控件;如果没有找到符合条件的控件则返回null

                                          \n" + } + ], + "type": "module", + "displayName": "UiCollection" + }, + { + "textRaw": "Rect", + "name": "rect", + "desc": "

                                          UiObject.bounds(), UiObject.boundsInParent()返回的对象。表示一个长方形(范围)。

                                          \n", + "properties": [ + { + "textRaw": "`left` {number} ", + "type": "number", + "name": "left", + "desc": "

                                          长方形左边界的x坐标、

                                          \n" + }, + { + "textRaw": "`right` {number} ", + "type": "number", + "name": "right", + "desc": "

                                          长方形右边界的x坐标、

                                          \n" + }, + { + "textRaw": "`top` {number} ", + "type": "number", + "name": "top", + "desc": "

                                          长方形上边界的y坐标、

                                          \n" + }, + { + "textRaw": "`bottom` {number} ", + "type": "number", + "name": "bottom", + "desc": "

                                          长方形下边界的y坐标、

                                          \n" + } + ], + "methods": [ + { + "textRaw": "Rect.centerX()", + "type": "method", + "name": "centerX", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {number} ", + "name": "返回", + "type": "number" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          长方形中点x坐标。

                                          \n" + }, + { + "textRaw": "Rect.centerY()", + "type": "method", + "name": "centerY", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {number} ", + "name": "返回", + "type": "number" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          长方形中点y坐标。

                                          \n" + }, + { + "textRaw": "Rect.width()", + "type": "method", + "name": "width", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {number} ", + "name": "返回", + "type": "number" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          长方形宽度。通常可以作为控件宽度。

                                          \n" + }, + { + "textRaw": "Rect.height()", + "type": "method", + "name": "height", + "signatures": [ + { + "params": [ + { + "textRaw": "返回 {number} ", + "name": "返回", + "type": "number" + } + ] + }, + { + "params": [] + } + ], + "desc": "

                                          长方形高度。通常可以作为控件高度。

                                          \n" + }, + { + "textRaw": "Rect.contains(r)", + "type": "method", + "name": "contains", + "signatures": [ + { + "params": [ + { + "textRaw": "r [Rect](#widgets_based_automation_rect) ", + "name": "r", + "desc": "[Rect](#widgets_based_automation_rect)" + } + ] + }, + { + "params": [ + { + "name": "r" + } + ] + } + ], + "desc": "

                                          返回是否包含另一个长方形r。包含指的是,长方形r在该长方形的里面(包含边界重叠的情况)。

                                          \n" + }, + { + "textRaw": "Rect.intersect(r)", + "type": "method", + "name": "intersect", + "signatures": [ + { + "params": [ + { + "textRaw": "r [Rect](#widgets_based_automation_rect) ", + "name": "r", + "desc": "[Rect](#widgets_based_automation_rect)" + } + ] + }, + { + "params": [ + { + "name": "r" + } + ] + } + ], + "desc": "

                                          返回是否和另一个长方形相交。

                                          \n" + } + ], + "type": "module", + "displayName": "Rect" + }, + { + "textRaw": "UiSelector进阶", + "name": "uiselector进阶", + "desc": "

                                          未完待续。

                                          \n", + "type": "module", + "displayName": "UiSelector进阶" + } + ] +} \ No newline at end of file