UiObject
# UiObject
UiObject表示一个控件,可以通过这个对象获取到控件的属性,也可以对控件进行点击、长按等操作。
获取一个UiObject通常通过选择器的findOne(), findOnce()等函数,也可以通过UiCollection来获取,或者通过UiObject.child(), UiObject.parent()等函数来获取一个控件的子控件或父控件。
## UiObject.click()
|返回值类型|备注|
|-|-|
|Boolean| |
**点击该控件**,并返回是否点击成功。
如果该函数返回false,可能是该控件不可点击(clickable为false),当前界面无法响应该点击等。
> 以下非官方文档内容
下面是新浪微博登录例子。
注意代码中的click()前面使用了findOne()
如果没有findOne(),判断会出错。
```js
//此代码由飞云脚本圈原创(www.feiyunjs.com)
if (text("微博用户协议和隐私条款").exists()) {
let isEnter = false //是否继续执行
if (id("checkBox").findOne().click()) {
//判断选中状态,自动处理
if (id("checkBox").findOne().checked()) {
log("已选中")
isEnter = true
} else {
if (id("checkBox").findOne().click()) {
log("已选中")
isEnter = true
} else {
log("未选中")
}
}
}
if (isEnter) {
log("我已阅读并同意上述条款")
sleep(random(1000, 3000)) //随机延时
if (id("next").findOne().click()) {
log("下一步")
sleep(random(1000, 3000)) //随机延时
}
}
}
```
有的APP中,尽管该函数返回false,实际上仍然是点击成功的。
比如QQ附近的人,从会话页返回到个人资料页:
```js
//返回到个人资料页
//此代码由飞云脚本圈原创(www.feiyunjs.com)
//判断返回按钮是否存在
var object = id('name').className('LinearLayout').clickable(true).depth(7)
if (object .exists()) {
//点击返回按钮
object .click()
log("返回到个人资料页")
sleep(random(1000,3000)); //随机延时
};
```
而下面这种写法就是错误的。脚本运行时,会因为判断问题出现页面反复闪回的问题。
```js
var object = id('name').className('LinearLayout').clickable(true).depth(7)
if (object.click()) {
log("返回到个人资料页")
sleep(random(1000,3000)); //随机延时
} else {
log("返回失败")
};
```
## UiObject.longClick()
|返回值类型|备注|
|-|-|
|Boolean| |
**长按该控件**,并返回是否点击成功。
如果该函数返回false,可能是该控件不可点击(longClickable为false),当前界面无法响应该点击等。
## UiObject.setText(text)
|参数|类型|描述|
|-|-|-|
|text| {string}| 文本|
|返回值类型|备注|
|-|-|
|Boolean| |
**设置输入框控件的文本内容**,并返回是否设置成功。
该函数只对可编辑的输入框(editable为true)有效。
## UiObject.copy()
|返回值类型|备注|
|-|-|
|Boolean| |
**对输入框文本的选中内容进行复制**,并返回是否操作成功。
该函数只能用于输入框控件,并且当前输入框控件有选中的文本。可以通过setSelection()函数来设置输入框选中的内容。
```js
var et = className("EditText").findOne();
//选中前两个字
et.setSelection(0, 2);
//对选中内容进行复制
if(et.copy()){
toast("复制成功");
}else{
toast("复制失败");
}
```
## UiObject.cut()
|返回值类型|备注|
|-|-|
|Boolean| |
**对输入框文本的选中内容进行剪切**,并返回是否操作成功。
该函数只能用于输入框控件,并且当前输入框控件有选中的文本。可以通过setSelection()函数来设置输入框选中的内容。
## UiObject.paste()
|返回值类型|备注|
|-|-|
|Boolean| |
**对输入框控件进行粘贴操作**,把剪贴板内容粘贴到输入框中,并返回是否操作成功。
```js
//设置剪贴板内容为“你好”
setClip("你好");
var et = className("EditText").findOne();
et.paste();
```
## UiObject.setSelection(start, end)
|参数|类型|描述|
|-|-|-|
|start |{number}| 选中内容起始位置|
| end |{number}| 选中内容结束位置(不包括)|
|返回值类型|备注|
|-|-|
|Boolean| |
**对输入框控件设置选中的文字内容**,并返回是否操作成功。
索引是从0开始计算的;并且,选中内容不包含end位置的字符。例如,如果一个输入框内容为"123456789",要选中"4567"的文字的代码为et.setSelection(3, 7)。
该函数也可以用来设置光标位置,只要参数的end等于start,即可把输入框光标设置在start的位置。例如et.setSelection(1, 1)会把光标设置在第一个字符的后面。
## UiObject.scrollForward()
|返回值类型|备注|
|-|-|
|Boolean| |
**对控件执行向前滑动的操作**,并返回是否操作成功。
向前滑动包括了向右和向下滑动。如果一个控件既可以向右滑动和向下滑动,那么执行scrollForward()的行为是未知的(这是因为Android文档没有指出这一点,同时也没有充分的测试可供参考)。
滑动操作的第一步是找到需要滑动的控件,例如要滑动QQ消息列表,则在悬浮窗布局层次分析中找到AbsListView,这个控件就是消息列表控件。注意到其scrollable属性为true,并找出其id为"recent_chat_list",从而下滑QQ消息列表的代码为:
```js
id("recent_chat_list").className("AbsListView").findOne().scrollForward();
```
> 以下非官方例子:
某音v6.6.0其他用户的关注列表,翻页例子。如果要操作粉丝列表,将depth的值改为9即可。
```js
//等待某音其他用户的关注列表页面出现:
waitForActivity("com.ss.android.ugc.aweme.following.ui.FollowRelationTabActivity")
//翻页:
log(id("cd0").depth(10).findOne().scrollForward())
```
另一种操作某音用户的粉丝列表的简单写法:
```js
do {
sleep(1000);//停个1秒 等页面滑动执行完成
//当前页面逻辑
} while (scrollForward());
```

## UiObject.scrollBackward()
|返回值类型|备注|
|-|-|
|Boolean| |
**对控件执行向后滑动的操作**,并返回是否操作成功。
向后滑动包括了向右和向下滑动。如果一个控件既可以向右滑动和向下滑动,那么执行scrollForward()的行为是未知的(这是因为Android文档没有指出这一点,同时也没有充分的测试可供参考)。
## UiObject.select()
|返回值类型|备注|
|-|-|
|Boolean| |
**对控件执行"选中"操作**,并返回是否操作成功。"选中"和isSelected()的属性相关,但该操作十分少用。
## UiObject.collapse()
|返回值类型|备注|
|-|-|
|Boolean| |
**对控件执行折叠操作**,并返回是否操作成功。
## UiObject.expand()
|返回值类型|备注|
|-|-|
|Boolean| |
**对控件执行操作**,并返回是否操作成功。
## UiObject.show()
|返回值类型|备注|
|-|-|
|Boolean| |
对集合中所有控件执行显示操作,并返回是否全部操作成功。
## UiObject.scrollUp()
|返回值类型|备注|
|-|-|
|Boolean| |
对集合中所有控件执行向上滑的操作,并返回是否全部操作成功。
## UiObject.scrollDown()
|返回值类型|备注|
|-|-|
|Boolean| |
对集合中所有控件执行向下滑的操作,并返回是否全部操作成功。
## UiObject.scrollLeft()
|返回值类型|备注|
|-|-|
|Boolean| |
对集合中所有控件执行向左滑的操作,并返回是否全部操作成功。
## UiObject.scrollRight()
|返回值类型|备注|
|-|-|
|Boolean| |
对集合中所有控件执行向右滑的操作,并返回是否全部操作成功。
## children()
|返回值类型|备注|
|-|-|
|UiCollection| 控件集合 |
**返回该控件的所有子控件组成的控件集合**。可以用于遍历一个控件的子控件,例如:
```js
id("a64").findOne().children().forEach(function (child) {
log(child.className());
});
```
也可以这样写,但是这种方法容易出问题,不推荐使用:
```js
var object = id("a64").findOne().children()
for (var i = 0; i < object.length; i++) {
log(object[i].className());
};
```
约宝宝APP遍历附近的人表项:
```js
//此代码由飞云脚本圈原创(www.feiyunjs.com)
let object = className("android.view.ViewGroup").find();
if (!object.empty()) {
// 遍历组件
object.forEach(function (currentValue, index) {
// 遍历子组件
currentValue.children().forEach(function (child, num) {
log(child.id());
//根据得到的id,判断是否进入用户详情
});
});
};
```

微信运动点赞实例:
> 下面这个代码的写法有些臃肿了,其中使用了多次遍历子组件。更简单的办法是直接寻找点赞图标。不过那样似乎不太稳定,有时候会出现找不到的情况。
```js
// 检查页面
//此代码由飞云脚本圈原创(www.feiyunjs.com)
object = text("排行榜").findOnce();
if (object != null) {
object = desc("返回").findOnce();
if (object != null) {
toast("开始执行");
while (true) {
let object = id("bks").find(); //寻找图像前的序号(跳过自己)
if (!object.empty()) {
// 遍历组件
object.forEach(function (currentValue, index) {
let father = currentValue.parent(); //取昵称父组件对象
if (father && father.id()) {
// com.tencent.mm:id/bkr
//列表项目item
object = currentValue.parent().parent().parent();
if (object && object.id()) {
// com.tencent.mm:id/nc
object.children().forEach(function (child) {
// com.tencent.mm:id/be8
// 遍历子组件
child.children().forEach(function (child1) {
child1.children().forEach(function (child2) {
child2.children().forEach(function (child3) {
child3.children().forEach(function (child4) {
if (child4.id() == "com.tencent.mm:id/bkx") {
log(child4.text()); //用户备注/昵称
};
if (child4.id() == "com.tencent.mm:id/bjb") {
log("找到点赞图标");
};
});
});
});
});
});
};
};
});
} else {
toastLog("解析列表失败");
device.vibrate(500); //震动提示
exit();
};
//检测是否到达尾部
object = text("邀请朋友").findOnce();
if (object != null) {
toastLog("已到达尾部");
exit();
};
// 向上翻页
swipe(100, device.height - 10, 100, getHeadHeight() + 50, random(500, 1000)); //向上滑动翻页
sleep(1000)
};
};
};
//取头部高度
function getHeadHeight() {
let rect = id("jw").findOne().bounds();
return rect.height();
};
```
## childCount()
|返回值类型|备注|
|-|-|
|number| |
返回子控件数目。
## child(i)
|参数|类型|描述|
|-|-|-|
| i| {number}| 子控件索引|
|返回值类型|备注|
|-|-|
|UiObject| |
返回第i+1个子控件。如果i>=控件数目或者小于0,则抛出异常。
>d 需要注意的是,由于布局捕捉的问题,该函数可能返回null,也就是可能获取不到某个子控件。
遍历子控件的示例:
```js
var list = className("AbsListView").findOne();
for(var i = 0; i < list.childCount(); i++){
var child = list.child(i);
log(child.className());
}
```
## parent()
|返回值类型|备注|
|-|-|
|UiObject| |
**返回该控件的父控件**。如果该控件没有父控件,返回null。
```js
// 花椒直播关注用户接口
//此代码由飞云脚本圈原创(www.feiyunjs.com)
function APP_关注用户() {
if (textEndsWith("关注").exists() && text("TA").exists() && text("私信").exists()) {
if (text("已关注").exists()) {
log("该用户已关注")
} else if (id("csi").findOne().parent().click()) {
// 该用户未关注
log("关注成功")
sleep(1000); //延时
return true;
} else {
log("Error:界面分析失败,请检查APP版本或运行环境")
};
return false;
};
};
```
```js
// 遍历某音用户,粉丝列表中的用户昵称
// APP版本:7.2.1
//此代码由飞云脚本圈原创(www.feiyunjs.com)
var object = className("TextView").depth(13).find();
if (!object.empty()) {
for (var i = 0; i < object.length; i++) {
if (object[i].text() != "" && object[i].text() != "关注") {
var tv = object[i].parent().parent().parent();
if (tv != null) {
tv.click() //点击列表项,进入用户资料页
sleep(random(1000,3000))
// 执行发送私信/关注等操作
if (textStartsWith("作品").exists() && textStartsWith("动态").exists() && textStartsWith("粉丝").exists()) {
back()
sleep(random(1000,3000))
}
} else {
log("点击表项失败")
};
}
};
} else {
log("Error:没找到用户昵称╭(╯^╰)╮");
};
```
遍历兄弟组件:
```js
// 微博的用户任务中心,遍历兄弟组件
// APP版本:9.7.3
//此代码由飞云脚本圈原创(www.feiyunjs.com)
let object = textStartsWith("填写学校").findOne(1000)
if (object != null) {
let child = object.parent().children()
for (var i = 0; i < child.length; i++) {
let tv = child[i]
log(tv.text())
};
} else {
log("未找到组件")
}
```
## bounds()
|返回值类型|备注|
|-|-|
|Rect| |
**返回控件在屏幕上的范围**,其值是一个Rect对象。
示例:
```js
var scope = text("Auto.js").findOne().bounds();
toast("控件在屏幕上的范围为" + scope);
```
如果一个控件本身无法通过click()点击,那么我们可以利用bounds()函数获取其坐标,再利用坐标点击。例如:
```js
let scope = desc("打开侧拉菜单").findOne().bounds();
let x = scope.centerX()
let y = scope.centerY()
click(parseInt(x), parseInt(y))
//如果使用root权限,则用 Tap
```
> 以下非官方说明
|参数|描述|
|-|-|-|
|x1|区域左上角x坐标 |
|y1|区域左上角y坐标|
|x2|区域右下角x坐标|
|y2|区域右下角y坐标|
## boundsInParent()
|返回值类型|备注|
|-|-|
|Rect| |
**返回控件在父控件中的范围**,其值是一个Rect对象。
## drawingOrder()
|返回值类型|备注|
|-|-|
|number| |
**返回控件在父控件中的绘制次序**。该函数在安卓7.0及以上才有效,7.0以下版本调用会返回0。
## id()
|返回值类型|备注|
|-|-|
|string| |
**获取控件的id**,如果一个控件没有id,则返回null。
## text()
|返回值类型|备注|
|-|-|
|string| |
**获取控件的文本**,如果控件没有文本,返回""。
```js
// 取某音用户昵称
//此代码由飞云脚本圈原创(www.feiyunjs.com)
function APP_取用户昵称() {
// 判斷是否在用戶資料頁
desc("更多").waitFor(); //等待控件出现
if (text("获赞").exists() && text("关注").exists() && text("粉丝").exists()) { //判断是否存在指定节点
if (id("bka").exists()) {
return id("bka").findOne().text(); //返回用户昵称
} else {
log("Error:界面分析失败,请检查某音版本或运行环境")
};
} else {
log("Error:当前不在用户资料页")
};
};
```
[Auto.js Pro获取QQ附近的人资料页用户昵称](http://www.feiyunjs.com/868.html)
```js
// 取QQ附近的人用户昵称
//此代码由飞云脚本圈原创(www.feiyunjs.com)
/*
修改日期:20190628
功能说明:
起始界面:用户资料页
备注说明:这段代码不准确。需要进一步优化
分辨率:全分辨率
模块作者:
*/
function APP_取用户昵称() {
// 判斷是否在用戶資料頁
waitForActivity("com.tencent.mobileqq.nearby.profilecard.NearbyPeopleProfileActivity"); //等待页面出现
if (text("资料").exists() && text("动态").exists()) { //判断是否存在指定节点
var object = id("name").depth(10).className("TextView")
if (object.exists()) {
return object.findOne().text()
};
} else {
log(getNowFormatDate() + "当前不在用户资料页")
};
return "";
};
```
## findByText(str)
|参数|类型|描述|
|-|-|-|
| str |{string} 文本|
返回 UiCollection
**根据文本text在子控件中递归地寻找并返回文本或描述(desc)包含这段文本str的控件**,返回它们组成的集合。
该函数会在当前控件的子控件,孙控件,曾孙控件...中搜索text或desc包含str的控件,并返回它们组合的集合。
## findOne(selector)
|参数|类型|描述|
|-|-|-|
|selector| UiSelector|
|返回值类型|备注|
|-|-|
|UiObject| |
根据选择器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)
|参数|类型|描述|
|-|-|-|
|selector |UiSelector|
|返回值类型|备注|
|-|-|
|UiCollection| 控件集合 |
根据选择器selector在该控件的子控件、孙控件...中搜索符合该选择器条件的控件,并返回它们组合的集合。
# UiCollection
UiCollection, 控件集合, 通过选择器的find(), untilFind()方法返回的对象。
UiCollection"继承"于数组,实际上是一个UiObject的数组,因此可以使用数组的函数和属性,例如使用length属性获取UiCollection的大小,使用forEach函数来遍历UiCollection。
例如,采用forEach遍历屏幕上所有的文本控件并打印出文本内容的代码为:
```js
console.show();
className("TextView").find().forEach(function(tv){
if(tv.text() != ""){
log(tv.text());
}
});
```
也可以使用传统的数组遍历方式:
```js
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()
|返回值类型|备注|
|-|-|
|number| |
返回集合中的控件数。
> 历史遗留函数,相当于属性length。
## UiCollection.get(i)
|参数|类型|描述|
|-|-|-|
| i |{number}| 索引|
|返回值类型|备注|
|-|-|
|UiObject| |
返回集合中第i+1个控件(UiObject)。
> 历史遗留函数,建议直接使用数组下标的方式访问元素。
## UiCollection.each(func)
|参数|类型|描述|
|-|-|-|
|func| {Function} |遍历函数,参数为UiObject。|
遍历集合。
> 历史遗留函数,相当于forEach。参考forEach。
## empty()
|返回值类型|备注|
|-|-|
|Boolean| |
返回控件集合是否为空。
## nonEmpty()
|返回值类型|备注|
|-|-|
|Boolean| |
返回控件集合是否非空。
## UiCollection.find(selector)
|参数|类型|描述|
|-|-|-|
|selector |UiSelector|
|返回值类型|备注|
|-|-|
|UiCollection| 控件集合 |
根据selector所确定的条件在该控件集合的控件、子控件、孙控件...中找到所有符合条件的控件并返回找到的控件集合。
>d 注意这会递归地遍历控件集合里所有的控件以及他们的子控件。和数组的filter函数不同。
例如:
```
var names = id("name").find();
//在集合
var clickableNames = names.find(clickable());
```
## UiCollection.findOne(selector)
|参数|类型|描述|
|-|-|-|
|selector |UiSelector|
|返回值类型|备注|
|-|-|
|UiObject| |
根据选择器selector在该控件集合的控件的子控件、孙控件...中搜索符合该选择器条件的控件,并返回找到的第一个控件;如果没有找到符合条件的控件则返回null。