基础知识
控件(Widget)
- 控件(Widget)的概念类似于DOM中的元素对象
- 手机屏幕上显示的整个页面就是由很多不同的控件组成的
常见的控件:
TextView
显示文字(“android.widget.TextView”)
ImageView
显示图片(“android.widget.ImageView”)
EditText
输入框
Button
按钮,可附带图片
CheckBox
复选框 - 每个控件有其属性,例如:
className
类名,表示控件类型(“android.widget.TextView”\等)
packageName
包名,表示控件所在的应用包(“com.tencent.mobileqq”)
clickable
控件是否可点击
editable
控件是否可编辑
longClickable
控件是否可长按
bounds
控件在屏幕上的范围,当控件无法点击,用这个属性获取坐标
checkable
控件是否可勾选
checked
控件是否可已勾选
scrollable
控件是否可滑动
selected
控件是否已选择
visibleToUser
控件是否可见
enabled
控件是否已启用
depth
控件的布局深度
drawingOrder
控件在父控件的绘制顺序。
indexInParent
控件在父控件的位置
使用Auto.js软件,在悬浮窗的“布局范围分析”中可以选择屏幕上的控件,并查看其各项属性的值
常用函数
以下介绍一些编写脚本时常用的代码,更多见Auto.js文档官方文档
sleep(n)
暂停运行n毫秒,常见的情况是,软件的运行速度“跟不上”脚本,因此需要一定时间的等待(尤其是需要点击的控件以滑动的方式进入,若不等待可能点击不到)toast(message)
显示气泡信息log(message)
在控制台中输出信息toastLog(message)
上面两个函数的合体exit()
立即停止脚本运行
currentPackage()
是当前正在运行的应用的包名waitForPackage(package[, period = 200])
等待指定的应用出现,period 轮询等待间隔(毫秒)currentActivity()
当前正在运行的Activity的名称waitForActivity(activity[, period = 200])
等待指定的Activity出现waitFor()
等待某物出现
例如,代码第一句一般为auto.waitFor()
,检查无障碍服务是否已经启用,如果没有启用则跳转到无障碍界面,并等待无障碍服务启动
又例如,要等待某个文本为“发送”的控件出现,代码为text("发送").waitFor();
exists()
判断当前屏幕上是否存在某控件,如if(text("发送").exists())...
获取控件并操作
我们首先要①通过一定条件筛选出所需的控件②获取目标控件,③才能对其进行点击和编辑等操作(例如,要先获取到“确定”按钮的位置,才能控制脚本去点击它)
①筛选所需的控件:UiSelector选择器
我们使用UiSelector选择器筛选出所需要的控件,筛选的依据就是控件的属性
筛选时要保证条件可唯一确定某个控件,否则可能因为没有选择到所需的控件而无法实现期望的操作:
- 通常用一个独一无二的属性来定位一个控件:这样的属性一般是
id
、text
、desc
等
例如用text("confirm").findOne().click()
来点击一个按钮
在这个例子中,
text("confirm")
是选择器的条件(控件的text为"confirm"),findOne()
表示基于这个条件找到一个控件,从而我们可以得到确认按钮,再执行click()
即可点击"发送"按钮
- 有时单靠一个属性无法定位(例如QQ中很多控件id相同),就需要通过链式调用来组合条件:
例如className("ImageView").depth(10).clickable(true).findOne().click()
function clickBtn(btnText,waitingDelay){//传入一个按钮,确保点击了该按钮(搭配findOne,可能传入null或按钮)
var btn = text(btnText).clickable(true).findOne(waitingDelay);//在给定时间内持续寻找,直到找到
if(!btn)return false;//未在给定时间内找到按钮
while(!btn.click());//不断点击按钮直到成功,因此之前要确保按钮是可点击的
return true;
}
function clickByCoord(btnText,waitingDelay){//控件本身无法点击,转而获取其坐标并模拟点击动作
var btn = text(btnText).findOne(waitingDelay);//这里处理的是无法点击的控件
if(!btn)return false;
var coord= btn.bounds();
while(!click(coord.centerX(), coord.centerY()));//click(x,y)
return true;
}
UiSelector选择器:根据文本选择
text(str)
选择出text属性等于字符串str的控件textContains(str)
textStartsWith(prefix)
textEndsWith(suffix)
textMatches(reg)
选出其text文本与正则表达式reg匹配的控件
正则表达式:
应用场景1:获取价格控件(¥ 1
、¥ 9.99
等)
如果用JavaScript正则表达式(正则表达式是RegExp对象):
首尾用/.../
括起来var reg = new RegExp(/^¥\s\d+(\.\d+)?$/); textMatches(reg).findOnce(); 或直接 textMatches(/^¥\s\d+(\.\d+)?$/).findOnce();
如果用Java正则表达式(正则表达式是字符串):
首尾用"..."
括起来
且在字符串内,需要使用\\
来表达\
var reg = "^¥\\s\\d+(\\.\\d+)?$"; textMatches(reg).findOnce();
应用场景2:要要等待text为“发送”或“接收”或“确认xxx”的控件出现(三者之一均可)
则可以这样写:textMatches(/(发送)|(接收)|(确认.*)/).waitFor();
UiSelector选择器:其他常用选择器
clickable([b = true])
选择那些可以点击的控件,例如className("ImageView").clickable(true)
或className("ImageView").clickable()
表示可以点击的图片控件id(str)
:类似text(str)
,还有idContains(str)
、idStartsWith(prefix)
、idMatches(reg)
等desc(str)
同上className(str)
同上boundsInside(left, top, right, buttom)
、boundsContains(left, top, right, buttom)
、bounds(left, top, right, buttom)
基于坐标来选择控件,详见坐标篇UiSelector.filter(f)
为选择器附加自定义的过滤条件,其中f为一个函数,传入一个控件对象,返回true或false
②获取目标控件
直接在选择器后面加上findOne方法,就可以获取控件
例如
var w = id("action_log").findOne(6000);
if(w != null)
w.click();//如果找到控件则点击
else
toast("没有找到日志图标");//否则提示没有找到
获取控件的方法总结
UiSelector.findOne()
查找满足条件的控件并返回,若找不到,函数会阻塞,直至所描述的控件出现为止(因此此函数不会返回null
)UiSelector.findOne(timeout)
同上,区别在于规定了查找控件的时间:如果超过timeout毫秒还未找到,则返回null
UiSelector.findOnce()
相较于上面的持续搜索,此函数只在屏幕上搜索一次(而非不停地一直搜索)UiSelector.findOnce(i)
返回第 i 个符合条件的控件
上面的方法返回一个控件对象UiObject
:只会搜索并返回第一个符合条件的控件(这里的控件次序,是搜索算法深度优先搜索DSF决定的)
如果要返回所有符合条件的控件集合UiCollection
:使用下面的方法
UiSelector.find()
返回 UiCollection。找出所有满足条件的控件并返回一个控件集合(这个搜索只进行一次,并不保证一定会找到,因而会出现返回的控件集合为空的情况)UiSelector.untilFind()
返回 UiCollection。同上,区别在于如果屏幕上一直没有出现满足条件的控件,则该函数会保持阻塞(函数永远不会返回空集合)
UiCollection实际上是一个UiObject控件的数组(继承了数组),因此可以使用数组的函数和属性,例如length
属性、forEach
函数,遍历时可以用传统的for
遍历,也可用数组的forEach
遍历,更多详见对控件的操作(控件集合UiCollection篇)
更多获取控件的方法
如果已经获取到某个控件,还可以根据该控件与其他控件之间的层级关系,获取其它控件(如获取控件的父控件、子控件等);也可以在该控件的子控件、孙控件…中寻找符合条件的控件
这部分内容详见后文:③操作和点击控件-对控件的操作:基于当前的控件获取其他控件
③操作和点击控件
成功获取一个控件对象,即UiObject后,就可以操作这个控件:可以通过获取到这个控件对象的属性,也可以对控件进行点击、长按等操作
注意,并非所有clickable为false的控件都真的不能点击,这取决于控件的实现。例如对于某个自定义的类名为android.view.View的控件,clickable属性为false却能点击。
获取控件的属性
UiObject.text()
获取控件的文本,如果控件没有文本,返回""UiObject.id()
获取控件的id,如果一个控件没有id,则返回nullUiObject.bounds()
返回控件在屏幕上的范围,其值是一个Rect对象- 更多详见官方文档:UiObject
对控件的操作:点击、操作文本等
UiObject.click()
点击该控件,并返回是否点击成功UiObject.longClick()
长按该控件UiObject.scrollUp()
、UiObject.scrollLeft()
、UiObject.scrollForward()
等- 关于文本的操作,见后文“输入文本”部分
对控件的操作:基于当前的控件获取其他控件
实际上,页面控件对象的组织结构非常类似于DOM树,各控件之间都是有着层次结构的(可以获取一个节点的父节点、子节点等)
UiObject.children()
返回 UiCollection。返回该控件的所有子控件组成的控件集合UiObject.parent()
返回一个控件对象 UiObject。返回该控件的父控件UiObject.findOne(selector)
选择器selector在该控件的子控件、孙控件…中搜索符合该选择器条件的控件,并返回找到的第一个控件
例如var like = aWidget.findOne(id("feed_action_view_like"));
UiObject.find(selector)
同上,在该控件的子控件、孙控件…中搜索,但返回的是符合条件的控件集合UiObject.findByText(str)
在当前控件的子控件,孙控件…中搜索其text或desc属性包含str字符串的控件,并返回它们的集合
各个控件之间的具体层次结构可以在Auto.js应用悬浮窗的"布局层次分析中"查看
对控件的操作:基于坐标的操作
有时获取一个控件比较困难,可以直接基于坐标来进行点击或拖动等操作
要获取要点击的位置的坐标,可以在开发者选项中开启"指针位置",也可直接结合Auto.js应用提供的“布局范围分析”中控件的bounds属性得到坐标值
click(x, y)
模拟点击,时长约150ms,因此可能有连击速度过慢的问题,可以用press()代替longClick(x, y)
模拟长按press(x, y, duration)
模拟按住,duration为按住时长(单位ms)
按住时长较短,被系统认为是点击;时长超过500毫秒,则被系统认为是长按swipe(x1, y1, x2, y2, duration)
模拟从坐标(x1, y1)滑动到(x2, y2),返回是否成功gesture(duration, [x1, y1], [x2, y2], ...)
模拟沿着一系列路径滑动
如gesture(1000, [0, 0], [500, 500], [500, 1000])
gestures([delay1, duration1, [x1, y1], [x2, y2], ...], [delay2, duration2, [x3, y3], [x4, y4], ...], ...)
模拟多个同时的手势。每个手势的参数为[delay, duration, 坐标], delay为延迟多久(毫秒)才执行该手势(可省略,默认为0);duration为执行时长;
更多基于坐标的操作,详见对控件的操作(坐标篇)
输入文本
配合剪切板实现输入:
setClip(text);
getClip()
返回系统剪贴板内容UiObject.paste()
对输入框控件进行粘贴操作
根据传入的参数来输入文本:
setText([i, ]text)
将第i 个输入框的文本设置为text;不加参数i,则把所有输入框的文本都置为textinput([i, ]text)
在将第i 个输入框原来的文本上追加文本text,不加参数i则会把所有输入框的文本追加内容text
上面介绍的是全局函数,下面是一些基于控件的操作:
UiObject.setText(text)
设置输入框控件的文本内容,只对可编辑的输入框(editable为true)有效UiObject.setSelection(start, end)
对输入框控件设置选中的文字内容UiObject.copy()
对输入框文本的选中内容进行复制,只能用于输入框控件,并且当前输入框控件有选中的文本(可以通过setSelection()
函数来设置输入框选中的内容)UiObject.cut()
对输入框文本的选中内容进行剪切,后面同上UiObject.paste()
对输入框控件进行粘贴,粘贴剪贴板的内容