1.前言
控件可以理解为html页面中的各种标签,Android开发过程中也会有各种标签。控件操作就是将Android中的标签解析出来进行操作,控件的特性几乎和html的标签一致。控件操作对于非游戏应用是非常有好的,可以直接点击识别和操作控件,准确率可以达到百分之百。有小伙伴可能问了,既然有控件操作了,那么图像处理很麻烦,而且很容易被干扰,都使用控件操作算了。
虽然我平时使用控件比较少,但是还是有些理解的。游戏脚本是直接加载的游戏引擎,整个游戏相当于一个控件,没法进行识别和操作了,进入游戏只能使用图像处理和模拟点击完成脚本。又有小伙伴说了,那么在非游戏应用中全使用控件操作吧,这种可以了吧?我可以告诉你,也是不行。比如有个需求是出现按钮立即点击,只使用控件操作的话,就是调用按钮控件查找函数等到出现立即点击,在开发阶段发现可以实现,效率也高,准确定也高。打包后,你就发现,直接报错。为什么呢?控件操作想不报错,就得保证控件已经存在,不能出现从无到有或者从有到无的过程检测,尤其是多层检测时。那么,我都已经通过图像识处理检测到了存在了,我直接通过无障碍屏幕点击实现就好了,为什么还要再使用控件?不得不说的是,控件的准确性是图像处理没法比的,同样如果有悬浮窗等干扰时,通过控件操作更容易。最好的解决方式就是,通过图片处理+控件点击的方式完成上述功能。
控件操作也有有个大优势就是有时候不受屏幕限制,比如手机主界面启动应用,因为应用名唯一,可以通过控件的点击方式启动。好像这种启动方式,屏幕在哪个地方无所谓,他会自动划过去启动应用。有小伙伴会说了,不是有启动应用的接口吗?为什么还用这种笨蛋方式。我想说的是,那种直接启动的方式会有内存残余,启动应用越多,切换次数越多越容易崩。不要问为什么?因为我被搞崩过,有其他可以快速启动应用的方式吗?我回答是有,而且不会有内存残余,但是需要root环境,因此这种控件启动方式才是最常用的。
除了上述缺点,控件使用没有别的需要注意的了吗?我回答还是有。如果有些小伙伴有多台电脑,然后在第一台电脑模拟器上能够成功运行,到了第二台电脑模拟器上运行到控件地方就报错,这是为什么呢?这个有可能是两台电脑模拟器选择的手机机型不一致,就是机型,就是这么奇特。不过也不用太担心,这种问题一般出现在控件操作父子控件联动比较多的情况出现,因为不同机型的父子控件关系有少许差距,最好解决方式要么统一机型、要么把所有的父子控件关系考虑到位、要么改用图像处理+无障碍点击方式实现。一般也不会选择不同的环境运行脚本,每个环境都代表需要大量代码来适配。
再提醒一下控件的问题,比如控件上显示不能点击,但是你调用点击函数也能操作成功。再比如空间上显示能点击,但是你调用点击函数不生效。你不用慌张,这个都是控件常规问题,可以在不影响点击区域的前提下,去点击它的父控件或者子控件,你发现就可以了,再不行就多嵌套几次,实在不行就用图像处理。也不用担心,这都是小概率出现的问题,我只是提醒下。
有些小伙伴已经觉得控件bug太多了,其实并没有,只是个别特殊情况会出现,又没有很好的社区进行搜索,我真是尽力将能想到的问题都考虑到了。
2.控件查看
1.范围分析
1.点击Autojs悬浮窗,再点击"布局分析"按钮。

2.选择"布局范围分析"。

3.选择需要查看信息的控件位置,会显示查看控件信息、在布局层次中查看、生成代码三个选项。

4.选择"查看控件信息"。可以查看详细的空间信息,这些信息就是对应控件选择器中的信息,点击信息位置会自动复制,后面介绍。


5.选择"在布局层次中查看",就是查看当前控件在整个布局层次中所处的位置和层次关系,主要是用于查找和操作周围控件时进行查看。


6.选择"生成代码",通过勾选能够快速生成代码。选择好对应功能后,点击复制弹窗,再次点击"复制"按钮,进行代码复制。



7.可以直接将代码复制,然后运行,会将空间信息打印,这里不介绍功能了,可以大体看下。
ini
let currentUiSelect = className("android.widget.TextView").text("按键精灵").findOne();
console.log(currentUiSelect);

2.层次分析
1.点击Autojs悬浮窗,再点击"层次分析"按钮。


2.能够通过点击右侧的下拉按钮,查看所有的层次信息。大部分情况只会通过范围分析找到控件,然后在查看此控件的层次信息,能够快速找到。像这样直接查看层次信息的情况比较少,除非查看到底有多少层时,才会使用。

3.控件选择器
1.概况
控件选择器类似于html的css选择器,主要是根据选择器约束的条件获取某个或某些控件信息。控件选择器都支持链式调用方式,相关的函数可以在整个选择器前面加"selector()."或者什么都不加直接链式调用。以上面查看按键精灵信息为例,两种方式都可以完成功能,但是我更推荐不加"selector()."的方式。
ini
let currentUiSelect = selector().className("android.widget.TextView").text("按键精灵").findOne();
console.log(currentUiSelect);
currentUiSelect = className("android.widget.TextView").text("按键精灵").findOne();
console.log(currentUiSelect);

控件选择器实际上是检索空间范围分析中看到的控件信息。更具体点说,根据这些信息的特点,调用合适的控件选择器,找到需要的某个或某些控件。

2.algorithm
控件搜索算法,参数类型为字符串,默认值为DFS。只可传递DFS和BFS两个参数,分别代表深度优先算法和广度优先算法。因为控件搜索速度非常快,一般不会使用此函数。除非控件结构非常复杂,并且默认深度优先算法查询十分缓慢时,可以考虑切换到深度优先算法。
vbscript
// 默认DFS情况
let startTime = new Date().getTime();
let currentUiSelect = className("android.widget.TextView").algorithm("DFS").text("按键精灵").findOne();
console.log(currentUiSelect);
console.log("查询时间:" + (new Date().getTime() - startTime));
// BFS情况
startTime = new Date().getTime();
currentUiSelect = className("android.widget.TextView").algorithm("BFS").text("按键精灵").findOne();
console.log(currentUiSelect);
console.log("查询时间:" + (new Date().getTime() - startTime));

说明:
根据上面情况,可以判断出来,两个算法搜索速度差距不大。一个7毫秒,一个8毫秒,差距才1毫秒,其实只要速度不超过1秒,很难看出来。有些小伙伴运行时,可能出现和我下面情况类似,比如第一个算法226毫秒,第二个算法18毫秒,虽然实际使用过程中很难发现差距,但是速度是接近十倍的差距。其实,这并不是算法搜索速度导致的,因为这是第一次执行控件搜索,里面包含一个控件代码初次加载的时间。把哪个算法放在第一个位置,初次加载都会慢一些,你可以重新执行一次看下,搜索时间差距几乎可以忽略。

3.text
对应控件信息中的text内容,参数类型为字符串,用于获取text字符串与参数字符串一致的控件。这里就不举例说明了,在algorithm函数中使用过了。被检索最多的位置应该是控件信息的text。

4.textContains
对应控件信息中的text内容,参数类型为字符串,用于获取text字符串中包含参数字符串的控件。以text内容为"按键精灵"的控件为例,可以通过查找"键精"来找到。
ini
let currentUiSelect = textContains("键精").findOne();
console.log(currentUiSelect);

5.textStartsWith
对应控件信息中的text内容,参数类型为字符串,用于获取text字符串以参数字符串开头的控件。以text内容为"按键精灵"的控件为例,可以通过查找"按键"来找到。
ini
let currentUiSelect = textStartsWith("按键").findOne();
console.log(currentUiSelect);

6.textEndsWith
对应控件信息中的text内容,参数类型为字符串,用于获取text字符串以参数字符串结束的控件。以text内容为"按键精灵"的控件为例,可以通过查找"精灵"来找到。
ini
let currentUiSelect = textEndsWith("精灵").findOne();
console.log(currentUiSelect);

7.textMatches
对应控件信息中的text内容,参数类型为字符串,用于获取text字符串匹配参数正则表达式的控件。正则表达式一般用于匹配规则非常复杂的字符串检索。在写脚本过程中,控件信息的text内容完全相同的情况很少,哪怕出现了,我们可以检索控件信息其他部分的内容,而不是写复杂的正则表达式。当然,如果有小伙伴确实想学习正则表达式,可以看我主页文章,有关于正则表达式内容介绍。如果能够成功理解正则表达式的内容,完全可以使用这个一个函数完成text内容的匹配,没有必要使用前面介绍的函数。比如:我通过正则表达式完成了与textStartsWith类似的查找方式。
ini
let currentUiSelect = textMatches(/^按键.*/).findOne();
console.log(currentUiSelect);

注意:
Autojs的正则表达式,是以"/"开头,以"/"结束的,不能加上后面修饰符,比如"g"。也就是说,只要将正则表达式和上面那样包裹即可。
8.desc、descContains、descStartsWith、descEndsWith与descMatches
这个五个函数与text相关的函数一一对应,作用和使用方式完全一致,只是对应的控件信息位置不一样。

9.id、idContains、idStartsWith、idEndsWith与idMatches
这个五个函数与text相关的函数一一对应,作用和使用方式完全一致,只是对应的控件信息位置不一样。

10.className、classNameContains、classNameStartsWith、classNameEndsWith与classNameMatches
这个五个函数与text相关的函数一一对应,作用和使用方式完全一致,只是对应的控件信息位置不一样。

11.packageName、packageNameContains、packageNameStartsWith、packageNameEndsWith与packageNameMatches
这个五个函数与text相关的函数一一对应,作用和使用方式完全一致,只是对应的控件信息位置不一样。

12.bounds
对应控件信息中的bounds内容,可以传递4个参数,分别代表控件左边缘距离屏幕左侧的距离、控件上边缘距离屏幕顶部的距离、控件右边缘距离屏幕右侧的距离与控件下边缘距离屏幕底部的距离,参数类型均为数字,用于获取bounds数据与四个参数数字一致的控件。

ini
let currentUiSelect = bounds(196,385,360,560).findOne();
console.log(currentUiSelect);

13.boundsInside
对应控件信息中的bounds内容,可以传递4个参数,分别代表控件左边缘距离屏幕左侧的距离、控件上边缘距离屏幕顶部的距离、控件右边缘距离屏幕左侧的距离与控件下边缘距离屏幕顶部的距离,参数类型均为数字,用于获取bounds数据组成范围在四个参数数字组成范围内的控件。
ini
let currentUiSelect = boundsInside(180,360,480,710).findOne();
console.log(currentUiSelect);

注意:
这个函数的四个参数代表的意义只有前两个和bounds的前两个参数一致,后两个参数不一致。虽然两个函数都属于bounds开头的函数,其实两者有不同的意义。bounds函数主要用于查找完全匹配bounds控件信息的控件;boundsInside函数更多用于范围查找控件,如果观察细致就可以发现,此函数的四个参数更像以左上角为原点的两个点的横纵坐标,这种点坐标与后面的图片处理和点点击等功能坐标一致。比如:控件信息出现冲突时,可以通过boundsInside函数匹配查找区域,更加快速准确地找到控件。
14.boundsContains
对应控件信息中的bounds内容,可以传递4个参数,分别代表控件左边缘距离屏幕左侧的距离、控件上边缘距离屏幕顶部的距离、控件右边缘距离屏幕左侧的距离与控件下边缘距离屏幕顶部的距离,参数类型均为数字,用于获取bounds数据组成范围包含四个参数数字组成范围的控件。
ini
let currentUiSelect = boundsContains(220,400,240,420).text("按键精灵").findOne();
console.log(currentUiSelect);

注意:
有细心的好伙伴已经发现,这次测试boundsContains函数功能为什么附加上text选择器,因为这个控件有可能出现重叠情况,我又值查找一个数据,所以加上text选择器是为了证明这个函数的作用。因为父控件一定包含子控件,在子控件的范围也一定在父控件的位置,也就是说找个位置,根节点控件都有可能被找到。因此,boundsContains函数最好和其他控件选择器配合使用。
15.drawingOrder
对应控件信息中的drawingOrder内容,参数类型为数字,用于获取drawingOrder数字与参数数字一致的控件。

16.clickable
对应控件信息中的clickable内容,参数类型为boolean,默认是true,用于获取clickable值与参数值一致的控件。大家不要被这个值误解了,不论是选择器还是判断控件是否能被点击都不会看这个值,因为选择器很少选择这种区分性不大的值来检索,同时容易出现显示false但是能点击,显示true但是无法点击的情况。因此,这个函数大家知道有就行了,一般不用使用。

ini
let currentUiSelect = text("按键精灵").clickable().findOne();
console.log(currentUiSelect);

17.longClickable
对应控件信息中的longClickable内容,参数类型为boolean,默认是true,用于获取longClickable值与参数值一致的控件。和clickable作用类似,很少被使用。由于这两个值有时候不准确,我们检索控件时可以不考虑,如果检索到的控件确实无法点击或者长按,再考虑点击或长按控件的父控件或者子控件,这种情况出现概率较小。

18.checkable与selected
checkable和selected分别对应控件信息中的checked和selected内容,参数类型均为boolean,默认均是true,用于获取控件信息的checked或selected值与对应参数值一致的控件。checkable函数一般用于判断多选框状态,selected一般用于判断下拉框状态,比如检索多选框选择了哪些值,哪些没有值没有选择。


ini
let currentUiSelect = text("按键精灵").checkable(false).selected(false).findOne();
console.log(currentUiSelect);

19.enabled、scrollable、editable与multiLine
这四个函数分别对应控件信息中同函数名的内容,参数类型均为boolean,默认值均为true。四个函数很少使用,其中multiLine属性很少出现。目前,我没有在控件信息中看到multiLine属性的相关内容,也可能我用控件比较少。enabled、scrollable、editable与multiLine分别用于判断控制是否有效、控件是否能滑动、是否能修改以及是否为多行显示。后面这些参数类型为boolean的函数,在需要的时候能够想起来,简单了解下即可。

20.findOne
查找一个控件,可以传递一个参数,或者不传参数。不传参数代表直到找到符合要求的控件才会停止,找不到控件会阻塞程序。传一个参数代表查找一定时间仍然没有找到控件就返回null,这个参数类型为数字,参数代表等待的毫秒数。
ini
// 查找5秒,如果没找到也返回
let currentUiSelect = text("按键精灵").findOne(5000);
console.log(currentUiSelect);
// 直到找到控件为止,否则一直阻塞
currentUiSelect = text("按键精灵").findOne();
console.log(currentUiSelect);

21.findOnce
立即对当前屏幕进行控件检索,找到就返回控件信息,找不到就返回null,可以传一个参数或者不传参数。不传参数时,和上述功能一致。传一个参数时代表获取第"参数+1"个符合条件的控件信息,这个参数为数字,参数代表控件数组索引。比如,想获取第4个满足条件的控件信息,因为第4个控件在控件数组的索引为3,因此需要传递3这个参数。
ini
let currentUiSelect = className("android.widget.TextView").findOnce();
console.log(currentUiSelect);
currentUiSelect = className("android.widget.TextView").findOnce(5);
console.log(currentUiSelect);

22.find
立即对当前屏幕进行控件检索,将所有符合条件的控件信息,以控件数组的方式返回,即使没有找到符合条件的控件也会返回。当没找到匹配的控件数组时,可以通过empty函数判断是否为空。
ini
let currentUiSelect = className("android.widget.TextView").find();
console.log(currentUiSelect[5]);
currentUiSelect = className("测试中").find();
console.log(currentUiSelect.empty());

注意:
返回的控件数组是否为空,只能通过控件empty函数判断,如果直接打印会出现以下这种情况。

23.untilFind
立即对当前屏幕进行控件检索,等待至少有一个检索的控件,然后将所有符合条件的控件信息,以控件数组的方式返回。如果没有找到符合条件的控件,会进行阻塞;如果有符合条件的控件,其实和find函数功能一致。
ini
let currentUiSelect = className("android.widget.TextView").untilFind();
console.log(currentUiSelect[5]);
currentUiSelect = className("测试中").untilFind();
console.log(currentUiSelect.empty());
console.log("结束检索");

24.exists
立即对当前屏幕进行控件检索,返回是否有满足这个条件的控件,返回值类型为boolean。
sql
console.log(className("android.widget.TextView").exists());
console.log("-----------");
console.log(className("测试中").exists());

25.waitFor
等待满足条件的控件出现,否则一直阻塞。这个函数谨慎使用,我最不喜欢这种函数。这种阻塞不是我们通过while等条件控制的阻塞,如果出现特殊情况可以提前结束,这种阻塞完全是无法控制的,除非强制停止脚本。我们脚本并一定完全按照我们的想法执行,有可能出现特殊情况,交给这种无法停止的阻塞是一种全靠"运气"的行为。
erlang
className("android.widget.TextView").waitFor();
console.log("初次检索存在");
className("测试中").waitFor();
console.log("二次检索存在");

26.filter
这个函数与2-19中的选择器类似,需要配合20-25具体需求使用。官方文档应该是忘了配合使用,导致输出结果感觉很怪。比如,我手搓了个和text("按键精灵")类似的函数,大家一看就能理解。Autojs留这个函数的目的让我们匹配一些上面函数不能完成的数据,但是我觉得这个函数只能返回一个控件,不能返回控件数组,还是有局限性的。如果有特殊匹配需求,我可以通过选择器+find找到基本满足条件的数组,然后调用数组的filter函数进行复杂需求匹配是不是效果更好?既能解决检索单控件的需求,多控件也能使用此方法。习惯ES6语法的小伙伴可能觉得下面过滤函数的写法太复杂了,这也是没办法,免费版Autojs只支持ES5,除了个别官方给的函数外,"箭头函数"不要使用,容易出现报错。
javascript
let currentUiSelect = className("android.widget.TextView").filter(function(w) {
return w.text() == "按键精灵";
}).findOne(2000);
console.log(currentUiSelect);

27.总结
控件选择器需要先后由两部分组成,第一部分是2-19以及第26的控件信息检索,第二部分是20-25的功能选择。第一部分通过链式的方式匹配多个条件,从海量控件中检索出满足需求的控件。第二部分从直到找到一个控件(无参findOne函数)、在一定时间内找一个控件(有参findeOne函数)、立即查找一个控件(无参findOnce函数)、立即查找数组某个索引的控件(有参findOnce函数)、查找并返回所有控件(find函数)、直到找到控件并返回所有控件(untilFind函数)、判断控件是否存在(exists函数)和等待控件出现(waitFor函数)功能中选择一个,然后严格按照第一部分在前,第二部分在后的方式查找,返回满足需求的控件信息。
4.控件信息和控件操作
1.click、clickCenter与longClick
这几个函数都是用于控件操作,模拟人手动点击,但是操作会直接作用于控件。click、clickCenter和longClick分别代表点击控件、点击控件中心和长按控件,均返回是否点击成功。一般点击用click即可,长按用longClick。由于操作直接作用于控件,点击控件中心和点击控件功能一样,clickCenter平时很少使用。
2.setText
此函数只用于输入框,参数类型为字符串,用参数字符串替换输入框中的内容。
ini
let currentUiSelect = className("android.widget.EditText").findOne();
currentUiSelect.setText("123456");

3.setSelection
用于输入框,选中输入框中某个区域的范围。需要传递两个参数,第一个参数为输入框内字符串起始位置的索引,第二参数为输入框内字符串结束位置的索引。
ini
let currentUiSelect = className("android.widget.EditText").findOne();
currentUiSelect.setSelection(1, 4);

注意:
1.字符串的索引代表实际位置减去1,也就是索引是从0开始的,简言之,第一个字符位置为0而不是1。
2.字符串选择时是包括起始位置索引的字符,但是不包括结束位置索引的字符。如果传递第一个参数为1,第二个参数为4,代表选择第二个到第4个字符。
4.copy、cut与paste
这几个函数都是用于输入框,通过名字能够看出几个函数的作用,copy、cut和paste分别代表复制、剪切和粘贴,均返回是否操作成功。这三个函数的功能我们也会在日常生活中使用,和大家理解的一样,先要通过序号3的setSelection函数进选择,然后进行复制和剪切,最后不论是获取到剪贴板中的内容,还是直接粘贴都可以。
ini
let currentUiSelect = className("android.widget.EditText").findOne();
// 设置输入框内容为123456
currentUiSelect.setText("123456");
// 选择复制
currentUiSelect.setSelection(2, 4);
let currentOperation = currentUiSelect.copy();
let currentClip = getClip();
console.log("复制操作: " + currentOperation);
console.log(currentClip);
console.log("---------------------");
// 选择剪切
currentUiSelect.setSelection(1, 3);
currentOperation = currentUiSelect.cut();
currentClip = getClip();
console.log("剪切操作: " + currentOperation);
console.log(currentClip);
console.log("---------------------");
// 输入框光标移动到最后
// 重新获取控件信息
currentUiSelect = className("android.widget.EditText").findOne();
let currentInputText = currentUiSelect.text();
let currentInputTextLength = currentInputText.length;
currentUiSelect.setSelection(currentInputTextLength, currentInputTextLength);
//粘贴
currentOperation = currentUiSelect.paste();
console.log("粘贴操作: " + currentOperation);
// 重新获取控件信息
currentUiSelect = className("android.widget.EditText").findOne();
console.log(currentUiSelect.text());


注意:
1.输入框选中内容后,复制后默认不会取消选中状态。
2.输入框选中内容后,进行剪切时,光标会自动切换到剪切位置。
3.复制和剪切都要注意光标问题,需要手动移动光标再进行后续输入框操作。
4.输入框内容改变后,需要重新获取控件信息。
总结:
1.上面我在粘贴之前使用了输入框光标移动到最后的方式,这个方式可以进行延伸,可以通过序号3的setSelection函数完成。setSelection两个参数需要相同的值,并且这个值代表需要移动到字符的位置,最后会移动这个位置字符的后面。需要特别注意是位置,而不是索引。
ini
let currentUiSelect = className("android.widget.EditText").findOne();
// 设置输入框内容为123456
currentUiSelect.setText("123456");
// 需要移动的位置
let targetPosition = 4;
// 进行光标移动
currentUiSelect.setSelection(targetPosition, targetPosition);

2.我们有时候并不是复制部分内容,而是进行全选,其实也能封装个万能全选代码。
ini
let currentUiSelect = className("android.widget.EditText").findOne();
// 设置输入框内容为123456
currentUiSelect.setText("123456");
// 获取输入框内容
let currentInputText = currentUiSelect.text();
// 全选
currentUiSelect.setSelection(0, currentInputText.length);

3.通过上面介绍,我们会发现输入框操作太复杂了。其实,这样操作完全是为了更全面地学习autojs内容罢了,为了在特殊情况下能够找到这个方向罢了。正常情况下,获取或者操作输入框时,几乎没人会使用这种方式。只需要记住,获取输入框的内容,只需要调用控件的text函数(这个函数后面会介绍)即可,一下获取输入框的所有内容,具体我们最后需要获取哪个部分字符,再通过js字符串函数操作呗,最起码比选中和移动光标方便多了;操作输入框内容时,只需要记住调用序号2的setText方式即可,我们通过js获取到最终字符串,然后通过这个函数赋值,会通过新值覆盖掉原来的值。
5.scrollForward与scrollBackward
scrollForward和scrollBackward均用于控件滑动,只是控件滑动方向不一样,均返回是否操作成功。scrollForward分别用于从下往上滑动 (类似手指上翻)和从右往左滑动 (类似手指左翻)。scrollBackward分别用于从上往下滑动 (类似手指下翻)和从左往右滑动 (类似手指右翻)。两个函数均控制两个方向的滑动,但是滑动方向是无法控制的。一般情况下控件只支持上下或者左右滑动,不会同时支持四个方向的滑动,所以只需要找到两个函数对应的方向即可。
scss
let currentUiSelectArray = scrollable(true).find();
// 如果不为空
if (!currentUiSelectArray.empty()) {
currentUiSelectArray.forEach(currentUiSelect => {
currentUiSelect.scrollForward();
sleep(400);
currentUiSelect.scrollForward();
sleep(400);
currentUiSelect.scrollForward();
sleep(400);
console.log(currentUiSelect);
});
}
注意:
滑动控件一般很难找到,通过范围分析和层次分析进行查找比较困难。但是,可滑动的控件相对较少,可以通过上面代码中scrollable(true).find()方式查找所有可以滑动的控件,然后打印每个的控件信息。对于多个可滑动控件的页面,也可以通过打印信息找到自己需要操作的控件,然后在完善选择器的信息,最后一次性找到需要的控件,这算是快速找到能够滑动控件的小技巧。
6.select、collapse、expand与show
select、collapse、expand和show分别用于控件选择、折叠、展开和显示操作,均返回是否操作成功。说实话,这四个函数几乎不会使用,在手机端一般情况下会通过点击操作完成这四个功能。控件上前三个功能对应属性一般都是false,对于功能显示功能几乎不使用。
7.scrollUp、scrollDown、scrollLeft与scrollRight
scrollUp、scrollDown、scrollLeft和scrollRight分别用于控件从上往下、从下往上、从左往右和从右往左滑动。这四个函数的功能其实和序号5中scrollForward和scrollBackward函数的功能类似,scrollForward函数对应scrollDown和scrollRight函数的功能,scrollBackward函数对应scrollUp和scrollLeft函数的功能。虽然这四个函数比scrollForward和scrollBackward函数功能更准确,但是真正使用比较多的函数仍然是scrollForward和scrollBackward两个函数。因为这四个函数有时候调用不会生效,兼容性比scrollForward和scrollBackward两个函数差远了。
8.children、childCount、child与parent
children、childCount和child函数用于获取当前控件的子控件信息,分别获取子控件所有信息、子控件数量和获取某个索引的控件。parent函数用于获取当前控件的父控件信息。由于控件是通过树形连接的,每个控件可能有多个子控件,但是最多只能有一个父控件,因此子控件函数比较多,父控件函数只有一个。children函数将当前控件所有控件信息通过数组的方式返回,其实这函数返回值和find函数类似,通过empty函数判断是否为空。
ini
let currentUiSelectArray = className("android.widget.TextView").find();
// 如果不为空
if (!currentUiSelectArray.empty()) {
currentUiSelectArray.forEach(currentUiSelect => {
if (currentUiSelect.childCount() == 0) {
console.log(currentUiSelect.text());
console.log(currentUiSelect.children().empty());
console.log("-------------------------------");
}
});
}

9.bounds、boundsInParent与drawingOrder
bounds、boundsInParent和drawingOrder分别用于获取控件在屏幕中的范围、控件在父组件的范围和绘制次序,这三个函数平时很少用,了解即可。同时需要注意,drawingOrder函数只有在Android7以后的系统才有效,在此版本之前的系统会一直返回0。
ini
let currentUiSelect = className("android.widget.TextView").text("按键精灵").findOne();
console.log(currentUiSelect.bounds());
console.log(currentUiSelect.boundsInParent());
console.log(currentUiSelect.drawingOrder());

10.id与text
id和text分别用于获取控件的id和text信息。
11.findByText
此函数谨慎使用,按照官方文档说明,会返回控件数组,实际上返回数据类型有问题,导致这个函数容易出现奇奇怪怪的问题。find函数会返回控件数据,正常来说findByText函数应该也会返回控件数组,因此两者返回类型应该是一致的。但是通过测试发现,两者返回类型不一致,同时调用控件数组常用判断数组是否为空的empty函数,直接报错。
ini
let currentUiSelectArray = className("android.widget.FrameLayout").find();
console.log(Object.prototype.toString.call(currentUiSelectArray));
console.log("------------------------");
if (!currentUiSelectArray.empty()) {
currentUiSelectArray.forEach(currentUiSelect => {
let currentUiSelectChildren = currentUiSelect.findByText("按键精灵");
console.log(Object.prototype.toString.call(currentUiSelectChildren));
currentUiSelectChildren.empty();
});
}

我以为是返回类型弄错了,返回的数据是控件信息,弄成了控件数组,结果我直接调用获取控件信息的text函数,还是报错。

12.findOne与find
findOne和find均是用于在此控件的子控件(以此控件为根节点的所有控件)中查找控件,均传递类型为控件选择器的参数。这两个函数和选择器中的两个函数的作用完全一致,只是作用范围不一样罢了。控件选择器中的两个函数是全局查找,而这里的两个函数将当前组件当做了找到的根节点。
ini
let currentUiSelectArray = className("android.widget.FrameLayout").find();
// 获取Ui选择器
let currentUiSelector = className("android.widget.TextView").text("按键精灵");
if (!currentUiSelectArray.empty()) {
currentUiSelectArray.forEach(currentUiSelect => {
let getSelect = currentUiSelect.findOne(currentUiSelector);
console.log(getSelect);
let getSelectArray = currentUiSelect.find(currentUiSelector);
console.log(getSelectArray.empty());
});
}

5.控件集合
1.概况
上面介绍的所有函数返回值为控件数组的都可以调用控件集合的函数,其实上面说的控件数组就是控件集合,在这里代表一个意思。
2.size与get
size和get分别代表获取控件集合的大小和当前索引的控件信息,一般集合或者数组都会带有这两个函数。
ini
let currentUiSelectArray = className("android.widget.TextView").find();
console.log(currentUiSelectArray.size());
currentUiSelectArray.forEach((currentSelect, index) => {
if (currentSelect.text() == "按键精灵") {
let getSelect = currentUiSelectArray.get(index);
console.log(getSelect.text());
}
});

3.each
此函数用于集合遍历,类似于js中的数组遍历。但是,这个函数只能和我下面那样调用,不支持箭头函数,同时函数只返回控件信息一个参数,不会返回当前索引。因此,一般情况下,我们不会使用这个函数,将控件集合当做普通的js数组进行遍历,反而功能更加齐全,还能支持箭头函数。
ini
let currentUiSelectArray = className("android.widget.TextView").find();
currentUiSelectArray.each(function(currentSelect) {
console.log(currentSelect.text());
});

4.empty与nonEmpty
empty和nonEmpty分别代表控件集合是否为空和是否非空,返回值均为boolean类型,是对立关系。正常情况下,我们使用一个就可以了。
ini
let currentUiSelectArray = className("android.widget.TextView").find();
console.log(currentUiSelectArray.empty());
console.log(currentUiSelectArray.nonEmpty());

5.findOne与find
这两个函数和控件信息中的两个函数完全一致,控件信息的函数是查找当前控件的子控件符合控件选择器的控件信息,而这两函数是查找控件集合中符合控件选择器的控件信息。
ini
let currentUiSelectArray = className("android.widget.TextView").find();
if (!currentUiSelectArray.empty()) {
// 获取Ui选择器
let currentUiSelector = className("android.widget.TextView").text("按键精灵");
console.log(currentUiSelectArray.findOne(currentUiSelector));
console.log(currentUiSelectArray.find(currentUiSelector).get(0));
}

6.总结
上述大部分函数我已经进行测试,有个别有问题的函数我也已经指出。没有给出代码案例的函数都是较为简单的函数,如果使用过程中,这些函数都出现了问题,可以私信我,我进行测试。有些函数功能其实是重复的,真正写代码过程中,会因为个人变成习惯等原因,一直会用几个函数,其他函数会忽略掉。出现这种情况是在所难免的,遇到无法完成的功能再来看这个文章即可。脚本编程一般不会出现效率特低的情况,所以完成功能过程中不论调用几次函数对最后的结果影响不大。脚本执行效率等问题,一般出现在图片处理上,不会出现在这种函数上,小伙伴们找到适合自己编程习惯的调用方式即可。
特别注意,只有通过个人主页博客或者个人介绍中方式,才能获取源码