控件

基于控件的操作可以说是Auto.js独特的功能。这个功能可以帮我们更高效的做出模拟操作行为或者获取当前页面的信息等。

0.在正式进入脚本教程前,先给萌新们普及点概念:

什么是控件?

    控件是指对数据和方法的封装。
    控件可以有自己的属性和方法
    1. 其中属性是控件数据的简单访问者
    2. 方法则是控件的一些简单而可见的功能
    控件创建过程包括设计、开发、调试工作, 然后是控件的使用。

    > 这样理解可能有点难,萌新可以理解为,控件就是页面上的一个个内容。
    1. 比如说一个图片就是一个图片控件,一个输入框就是一个输入框控件等等,常见的控件有:`TextView,EditText,Button,CheckBox,RadioButton,ImageView,ImageButton,ProgressBar`等。
    2. 然后这些控件有着一定的属性,我们可以通过他们的属性用脚本获取到他们,也可以通过控件类的函数对他进行一定的操作,和获取信息等。
    3. 比如直接通过控件对齐进行点击,设置输入框控件内的文字,获取某控件的位置等等。

我们如何获取到控件信息

    在`auto.js`软件中有直接获取到控件信息的功能,在`auto.js`软件主页面,点击右上角,打开侧拉菜单,大致中间位置的悬浮窗打开。
    1. 这个`auto.js`的悬浮窗就含有获取控件信息的功能。
    2. 点击悬浮窗我们可以看到有5个功能,中间蓝色的就是用于查看控件信息的功能
    3. 点击之后,会让你选择用布局范围分析和布局层次分析。

    布局范围分析,使用该功能,可以直接看到当前页面上所有控件的边框即控件位置,这样可以清晰的了解各种控件的位置以及大小。
    但是这种方法也有缺点,就是比如有两个完全相同大小的控件重叠在一起就看不见了,这时候就要用到布局层次分析。

    布局层次分析,即根据软件当前页面的源码布局层次进行观察,这样可以看到所有控件及其层次等

    但是这样子对于比较复杂的布局九很难进行观察,毕竟内容较多,距离远的话难以比较。所以在布局层次和布局范围中进行切换查看是比较好的获取控件信息的方法。

    点击选择的控件即可查看控件信息,比较主要的控件信息有`id`即控件的`id`属性,`text`控件的文本属性,`bounds`控件的边框位置,`desc控件`的`desc`值,有些控件会把`text`控件放空,并把上面的文本写入`desc`值。`clickable/longClickable`控件是否可以点击/长按,如果控件的`clickable`为false那么点击也没用。`checked`控件是否选中,`depth`在第几层次,`indexInParent`在父控件中是第几个,在教程文档的选择器那一栏中有更多属性。这些信息对于以后写脚本都有很大的帮助。

    生成代码,这个功能可以直接人性化的智能帮你选出如何对一个控件进行操作的代码,这个功能很好用,但是一般情况只会选择最简洁的代码,和我们需要实现的功能可能会稍有偏差,需要我们人为进行修改,但是大部分情况还是可以的。另外比较复杂的布局暂时可能会有获取失败的情况。所以不要太依赖于次功能,只要学会了以下内容,自动生成出来的那些代码,自己肯定都会写。

2.如何写控件操作脚本,控件操作的脚本编程思想是什么?

    我们要对一个控件进行操作
    首先我们要让脚本识别的那个函数,在`js`的基于空间的操作中有三大类:
    1. 控件选择器
    2. 控件合集
    3. 控件
    这三个类都有各自的方法,要理解这个可能有点难,这里需要面向对象编程思想,有兴趣的,可以百度了解一下。
    在写这类脚本语句中,一般要多重调用的,比如:
    `id("a").text("abc").findOne().click();`
    可以看出一句话非常的长,这里的id(),text(),.findOne(),.click()分别都是上面那三个类中的函数
    比如`id()`这个函数是控件选择器的函数,他们可以直接作为脚本开头(全局函数),他的返回值还是控件选择器,所以后面接的函数还是控件选择器的函数,`text()`这就也是一个控件选择器的函数,他的返回值还是控件选择器,所以后面继续接控件选择器的函数`findOne()`然后这个函数的返回值是控件,所以后面接控件的函数`click()`。
    另外上面那句语句解释一下,就是,选择`id`属性为`a`的,`text`属性为`abc`的控件,直到找到一个符合以上条件的控件,对其点击。

    其实你们可以直接把自己需要找的控件的属性用选择器筛选出来,如果有多个那就用英文的点将其连接即可。

    总结:写控件操作的代码时,不但要实现需要实现的功能,还要注意返回值和之后使用的函数,以及如何切换来实现需要的功能。
    当然,如果你愿意去学一下面向对象编程思想当然是更好的啦。

3.控件选择器

   控件选择器即含有对控件进行筛选的功能,比如大部分控件选择器都是用属性来筛选控件的,并且他们的返回值都是控件选择器,
   在这里,返回控件选择器本生是为了,方便以后进行,链式调用,也就是将很多要筛选的属性直接串联,最后再用findOne转换成控件。

   我们来说几个常用控件选择器函数的使用方法。
   假如有`desc`属性为`ab,abcd,abcde,cde,cd `
   那么我们用教程中的选择器`desc()`也就是匹配desc值为传入字符串的控件,比如`desc("cd")`这样子的话只会匹配出上面desc值为cd的一个控件
   如果用`descContains("cd")`,desc属性包含传入的字符串的话,那就能匹配出desc值为`abcd,abcde,cde,cd`的控件,还有的`descStartsWith()`和`descEndsWith()`即为分别匹配以传入的字符串开头或者结尾的·desc值的控件,自己体会肯定能懂吧,
   除了这四种常用的,还有一个就是descMatches(正则表达式),这个用于传入正则表达式,进行匹配,之后我会单独写一篇文章将正则表达式的匹配,这个在百度上也都有,应该来说是可以通过自己的尝试自学会的。

   > 几乎属性数据类型是字符串的控件都有着五个选择器函数进行选择,例如:`text() , textContains() , textStartsWith() , textEndsWith() , textMatches() , id() , idContains() , idStartsWith() , idEndsWith() , idMatches()` ,还有`className,package`等就不一一举例了。

    还有一些布尔值的属性的筛选器,就很简单了,比如说clickable属性的选择器函数clickable(要匹配的找值),同样可可以作为全局函数,比如clickable(true)这个函数就可以筛选当前页面上所有可以点击的控件,同样的布尔值属性的选择器函数还有checkable()控件是否可以勾选,selected()控件是否已经勾选,longClickable()控件是否可以长按,另外还有enabled()控件是否已经启用,scrollable()控件是否可以滑动,editable()控件是否可以编辑,等等。这些功能都大同小异,教程和示例都有详细的使用方法。那我们要写一个选择器筛选当前页面上可以点击,长按的控件,就为clickable(true).longClickable(true)。这个应该能理解,那我们继续看一些稍微复杂咋一些的内容。

    有的时候真的会碰到一些奇葩的软件,或者像QQ那种故意的,所有属性要么没有,要么都一样,这个时候可以用控件的边框位置来进行筛选,主要有两个比较常用的函数,bounds()和boundsInside(),我们要传入的变量就是控件的位置范围,由四个整数值组成前两个值为控件长方形左上角的坐标,后两个值是控件右下角点的坐标。简单举个例子,我们要筛选左上角坐标233,233到右下角坐标666,666内部的控件即为boundsInside(233,233,666,666),这样在这个框内部的所有·控件会全部筛选出来,与他不同的一个边框选择器是直接的bounds()这个函数相对来说不算很常用,因为他的作用是匹配出边框是传入的四个值的控件,必须完全一样,在很多属性都一样的时候,这个功能非常的常用,因为bounds属性完全相同的控件就是完全重叠的那些,一般都只有一个,但是这个功能也有个很大的缺点那就是做适配功能很难,在·两个分辨率不同的手机上大部分控件的bounds是完全不一样的,只要不是非常外层的普通控件,等比缩放这类方法肯定是不可取的,主要还要观察控件的出现方式,就算写出来了,计算机结果完全相同,没有一点偏差也是几乎不可能的。所以这个功能一般用于特定分辨率,不需要适配的脚本中。

    还有些没说的选择器函数一个是drawingOrder();这个控件用于筛选控件在其父控件中是否是第某个,另外他可以作为全局函数使用,如果使用drawingOrder(0),筛选出所有在父控件中的第一个控件。当然最外层的布局也是第0个,并且从筛选触来的内容角度来说,最外层控件肯定在第一个·,那直接使用findOne()就可以直接获取到UI外层控件了。

    最后要介绍的是 一个最强的万能选择器函数-过滤函数,他传入的内容是一个返回布尔值的匿名函数,函数的传入值是当前符合的所有控件。他会把会让函数返回值为false的内容全部过滤掉。我们直接用文档中的一个例子来对齐进行解释,假如我们要写的是过滤出text属性有10个字符的控件,他的代码是filter(function(w){return w.text().length==10}),我们知道function是新函数的意思,return在函数中即是函数的结束也是函数返回内容的代码,这个函数中先获取了控件w,即传入进来的控件的一个,的·text属性(即后面要讲的控件.text(),获取控件信息的函数)这个属性值是字符串,可以用length属性来获取到他的长度,然后用关系运算符 == (作用是比较左右两边内容是否相同,相同返回true,不同false)把他和我们要过滤出来的10进行比较,即他最后返回的布尔值数据类型。然后他会过滤出那些所有返回值为true,即符合的控件。

    说了这么久,控件用于过滤选择器算是说完了。还有就是通过各种功能让控件选择器转换成控件或者控件合集,注意:现在开始说的函数都不能作为全局函数放在开头使用,最主要常见的函数就是findOne()了,他的作用是寻找屏幕上符合前面控件选择器的函数,直到出现,并返回第一个出现的控件,另外他还可以传入一个参数—最大查找时间,即位,如果到了时间还没有找到的话,直接返回undifined,以继续脚本。和他相似的还有一个函数是findOnce(),不同于之前那个函数,这个函数只会寻找一次,如果当前屏幕上没有,则直接返回null,他也可以传入一个参数,用于写,需要获取当前屏幕上第几个符合条件的内容,如果当前屏幕上的个数,不够获取的个数,则返回null。这两个函数是比较简单的函数,就先不举例子了。

    还有find()函数会把选择器转换成合集,他的功能是在当前屏幕上搜索所有符合条件的控件并且都放入控件合集,一起返回,如果当前页面没有,那就会直接返回一个空的控件合集。和他有一个相似的函数untilFind()同样是返回控件合集,但是不同在于,他会循环寻找,至少要找到一个才会返回合集。也就是说不会返回空合集。

    还有一些控件选择器函数,返回的是其他数据类型,例如函数 选择器.exists(),他返回的就是当前屏幕上是否存在符合条件的控件选择器。

    甚至还有一些函数没有返回值,只有一些运行效果,比如 选择器.waitFor(),他会一直等待符合条件的控件出现。

4.控件

   关于控件是什么上面已经介绍过了,这里我们一样来介绍几个控件的函数,首先是可以获取到控件信息的函数,最主要有text(),desc(),id(),当热其他所有属性也是行的,他们的返回值都是自己属性对应的类型。相同的还有classname,clickable,packagename等等,所有悬浮窗中有的属性都可以直接这样用函数获取到。另外还有childCount()用来返回这个控件有多少个子控件;drawingOrder()用语返回他他在父控件中的绘制顺序, 等比较少用的功能在app内文档中都有使用说明。

    其次我们可以对控件做出操作,就是有操作效果的函数,比如函数click()就可以直接点击一个控件。注:如果clickable的值为false,那点击效果是没有用的,如果明明是个按钮控件,clickable值却是false那,一般,切换到布局层次可以看到有和他重叠的clickable值为true的控件。还有longClick()长按,setText()设置输入框内内容,等等。那我们来举个例子,我们要把id为edit的输入框控件内的文字改为123456,他的代码为id("edit").findOne().setText("123456");  非常简单,自己理解吧。还有很多类似的例如选中,等函数,文档里都有使用方法,大同小异,这里就不一一介绍了。

    还有一些可以用来找到与这个控件有关的控件的函数,比如他的父控件和他的子控件,我们知道安卓的控件是由控件多层嵌套出来的,所以控件可以含有他的子控件,也有他的父控件(最外层不算)。我们通过控件函数.parent()可以获得到这个函数的父控件函数,通过child(第几个),获取到他的第几个子控件,注意序号从零开始。

    还有个函数children()用于返回这个控件的所有子控件,另一个比较常用的函数findByText(str)需要传入一段文<字符串>,这个函数返回他所有子控件或者孙控件中text或者desc属性中包含这段文字的所有控件。这时候细心的朋友肯定就能注意到,前面返回控件类,只有一个,是怎么返回多个的呢。这里涉及到一个新的类那就是控件集合。他的底层类似于一个控件数组,可以存放多个控件,也可以用上一章讲的中括号下标的方法来获取到其中第几个控件,也可以用控件合集里的get(下标)函数来获取,还有就是size()获取合集大小,即为里面有多少个控件,和数组的length属性相同。下面就来详细讲讲控件合集吧。

5.控件合集

    首先它是一个类,继承上一章数组类,也就是有所有数组里的功能和属性。他自然也有自己的新函数,我们获取到一个控件合集后肯定是要对它做出操作的。上面对控件的操作,比如点击 长按 选中之类的功能,这里控件合集也能用,比如点击一个控件合集,他会自动按顺序点击控件合集里的每一个控件。内部有一个自动遍历数组的功能each(遍历函数) 里面传入一个函数,写对每个控件的操作,这个函数的参数是控件。这样不需要自己写循环来遍历方便了很多。

    附注:其实控件选择器后面也可以直接根操作函数,功能会是直接对当前界面上所有符合选择器条件的控件进行该操作。运行时控件选择器会调用自己的find()函数在页面上找出符合条件的所有控件的合集,再用控件合集内的遍历并对每个控件进行操作。要是你确定你想要的控件已经出现在这个界面了,那么这样子写,代码会简洁很多咯。

    如何·获取到一个控件合集。除了上面说的两种方法,还有可以通过控件选择器获得,之前说的选择器内的函数find()就可以返回调用这个函数的选择器,也就是前面的所有限定条件,全部符合的所有控件。这个控件合集中也有一个find(控件选择器)函数,但是和控件选择器中的不同。他需要传入一个控件选择器,然后返回这个控件合集中符合该控件选择器的所有控件及其子孙控件的合集。比如说我们要返回一个合集中所有控件和他们子孙控件,text属性是abc,desc属性是def的控件合集,那只要再那个合集后面接.find(text("abc").desc("def"))就行啦。还有一个用法差不多的findOne()这个是用来返回合集中所有控件及其子孙控件中第一个符合传入的选择器的控件。

    还有两个相对来说不常用的功能,就是判断合集是否为空和判断合集是否不是空的。函数名为empty()和nonEmpty()。其实判断控件合集长度用length属性或者get()函数也可以做出类似效果。

总结

    至此控件的所有功能已经讲完了,要是能学熟练本章的内容,做出模拟人操作(或者更快速)的脚本几乎是没问题了。再来汇总一下我们写控件操作的代码思路:首先,最基本的就是通过控件选择器根据控件的独特特点是筛选出要操作的控件,再用函数转换成合集或者控件,最后再用控件的函数对其进行操作或者获取信息,具体方法根据实际情况而定咯。

课后习题

   这次给大家留个思考题,答案会公布在评论区。写一个脚本,循环在qq界发送一句话(刷屏)。提示:要循环发送,首先要把代码放在一个循环内部,循环内,就是把输入框控件中设置成自己要发送的内容,然后在点击发送,用全控件操作更稳定。另外由于qq大部分控件的id都是name,所以如何筛选出输入框控件和发送按钮控件有难度哦