节点
# 节点信息
节点对象NodeInfo,可以通过获取getNodeInfo方法获取到节点信息的数组
节点包含的信息如下
|名称|数据类型|说明|
|-|-|-|
|id | 字符串 | 资源的ID |
|clz | 字符串 | 视图类名 ,例如 android.widget.TextView |
|pkg | 字符串 | 包名 ,例如com.xx |
|desc | 字符串 | 内容描述 |
|text | 字符串 | 文本 |
|checkable | 布尔型 | 是否可选中 |
|checked | 布尔型 | 是否选中 |
|clickable | 布尔型 | 是否可点击 |
|enabled | 布尔型 | 是否启用 |
|focusable | 布尔型 | 是否可获取焦点 |
|focused | 布尔型 | 是否聚焦 |
|longClickable | 布尔型 | 是否可长点击 |
|scrollable | 布尔型 | 是否滚动 |
|editable | 布尔型 | 是否可输入 |
|selected | 布尔型 | 是否被选择 |
|childCount | 整型 | 子节点的个数 |
|index | 整型 | 节点的索引 |
|depth | 整型 | 节点的层级深度 |
|drawingOrder | 整型 | 节点的绘制顺序 |
|bounds | Rect型 | 空间对象 |
|top | 整型 | 顶部位置 |
|bottom | 整型 | 底部位置 |
|left | 整型 | 左边位置 |
|right | 整型 | 右边位置 |
|visibleBounds | Rect型 | 可视空间对象 |
## getOneNodeInfo(timeout)
通过选择器获取第一个节点对象
@param timeout 等待时间,单位是毫秒, 如果是0,代表不等待
@return NodeInfo 对象 或者null
```js
function main(){
//获取选择器对象
//选择 节点 clz=android.widget.CheckBox所有节点,
var node = clz("android.widget.CheckBox").getOneNodeInfo(10000);
if (node) {
var x= node.click();
logd(x);
} else {
toast("无节点");
}
}
main();
```
```js
function main(){
//获取选择器对象
//选择 节点 clz=android.widget.ViewGroup 所有节点,
var node = clz("android.widget.ViewGroup").getOneNodeInfo(10000);
if (node) {
//获取子节点
node =node.getOneNodeInfo(text("广告"),10000);
if (!node){
toast("无子节点");
return;
}
var x= node.click();
logd(x);
} else {
toast("无节点");
}
}
main();
```
## getNodeInfo(timeout)
获取节点信息集合
@param timeout 等待时间,单位是毫秒, 如果是0,代表不等待
@return NodeInfo 节点信息集合
```js
function main(){
//获取选择器对象
//选择 节点 clz=android.widget.CheckBox所有节点,
var node = clz("android.widget.CheckBox").getNodeInfo(10000);
logd(node);
}
main();
```
```js
function main(){
//获取选择器对象
//选择 节点 clz=android.widget.ViewGroup 所有节点,
var node = clz("android.widget.ViewGroup").getNodeInfo(10000);
if (node) {
//获取子节点
node =node.getNodeInfo(text("广告").clz("android.widget.TextView"),10000);
if (!node){
toast("无子节点");
return;
}
var x= node.click();
logd(x);
} else {
toast("无节点");
}
}
main();
```
## parent()
该节点的父级节点对象
@return NodeInfo 对象 或者null
```js
function main(){
//获取选择器对象
//选择 节点 clz=android.widget.CheckBox所有节点
var node = clz("android.widget.CheckBox").getOneNodeInfo(10000);
if (node) {
var x= node.parent();
logd(x);
} else {
toast("无节点");
}
}
main();
```
## child(index)
取得单个子节点对象
@param index 子节点索引
@return NodeInfo 对象 或者null
```js
function main(){
//选择 节点 clz=android.widget.ViewGroup 所有节点
var node = clz("android.widget.ViewGroup").getOneNodeInfo(10000);
if (node) {
var x= node.child(0);
logd(x);
} else {
toast("无节点");
}
}
main();
```
## allChildren()
获取所有子节点集合
@return NodeInfo 节点集合
```js
function main(){
//选择 节点 clz=android.widget.ViewGroup 所有节点
var node = clz("android.widget.ViewGroup").getOneNodeInfo(10000);
if (node) {
var x= node.allChildren();
logd(x);
} else {
toast("无节点");
}
}
main();
```
## siblings()
当前节点的所有兄弟节点集合
@return NodeInfo 节点集合
```js
function main(){
//选择 节点 clz=android.widget.ViewGroup 所有节点
var node = clz("android.widget.ViewGroup").getOneNodeInfo(10000);
if (node) {
var x= node.siblings();
logd(x);
} else {
toast("无节点");
}
}
main();
```
## previousSiblings()
在当前节点前面的兄弟节点集合
@return NodeInfo 节点集合
```js
function main(){
//选择 节点 clz=android.widget.ViewGroup 所有节点
var node = clz("android.widget.ViewGroup").getOneNodeInfo(10000);
if (node!=null) {
var x= node.previousSiblings();
logd(x);
} else {
toast("无节点");
}
}
main();
```
## nextSiblings()
在当前节点后面的兄弟节点集合
@return NodeInfo 节点集合(数组)
```js
function main(){
//选择 节点 clz=android.widget.ViewGroup 所有节点
var node = clz("android.widget.ViewGroup").getOneNodeInfo(10000);
if (node) {
var x= node.nextSiblings();
logd(x);
} else {
toast("无节点");
}
}
main();
```
```JS
let selector = id('com.ss.android.ugc.aweme:id/dch').getOneNodeInfo(5000)
if (selector != null) {
var x = selector.previousSiblings();
logd(x.length, x[0].bounds);
}
```
## click()
> 执行条件:无障碍7.0以上或者手势执行为代理服务
点击节点,节点区域随机点击
@return bool, true 成功 ,false 失败
```js
function main(){
//获取选择器对象
//选择 节点 clz=android.widget.CheckBox所有节点
var node = clz("android.widget.CheckBox").getOneNodeInfo(10000);
if (node) {
node.click()
} else {
toast("无节点");
}
}
main();
```
## clickEx()
> 执行条件:无障碍5.0以上或者手势执行为代理服务
无指针方式点击选择器,节点必须是可点击的才行
@return {boolean|布尔型}
```js
function main(){
var node = text("我是文本").getOneNodeInfo(10000);
var result = node.clickEx();
if (result){
toast("点击成功");
} else {
toast("点击失败");
}
}
main();
```
## longClick()
> 执行条件:无障碍7.0以上或者手势执行为代理服务
长点击节点
@return bool, true 成功 ,false 失败
```js
function main(){
//获取选择器对象
//选择 节点 clz=android.widget.CheckBox所有节点
var node = clz("android.widget.CheckBox").getOneNodeInfo(10000);
if (node) {
node.longClick()
} else {
toast("无节点");
}
}
main();
```
## longClickEx()
> 执行条件:无障碍5.0以上或者手势执行为代理服务
无指针方式长点击选择器,节点必须是可点击的才行
@return {boolean|布尔型}
```js
function main(){
var node = text("我是文本").getOneNodeInfo(10000);
var result = node.longClickEx();
if (result){
toast("点击成功");
} else {
toast("点击失败");
}
}
main();
```
## clickCenter()
> 执行条件:无障碍7.0以上或者手势执行为代理服务
节点点击中心点
@return bool, true 成功 ,false 失败
```js
function main(){
//获取选择器对象
//选择 节点 clz=android.widget.CheckBox所有节点
var node = clz("android.widget.CheckBox").getOneNodeInfo(10000);
if (node) {
node.clickCenter();
} else {
toast("无节点");
}
}
main();
```
## refresh()
刷新节点缓存
```js
function main(){
//获取选择器对象
//选择 节点 clz=android.widget.EditText 所有节点
var node = clz("android.widget.EditText").getOneNodeInfo(10000);
if (node) {
node.refresh();
} else {
toast("无节点");
}
}
main();
```
## isValid()
节点信息是否有效
@return bool|布尔型 true代表有
```js
function main(){
//获取选择器对象
//选择 节点 clz=android.widget.EditText 所有节点
var node = clz("android.widget.EditText").getOneNodeInfo(10000);
if (node) {
var x =node.isValid();
toast("节点有效性:"+x);
} else {
toast("无节点");
}
}
main();
```
## scrollForward()
> 执行条件:无障碍5.0以上或者手势执行为代理服务
向前滚动
@return {boolean|布尔型}
```js
function main(){
var node = scrollable(true).getOneNodeInfo(10000);
var result = node.scrollForward();
if (result){
toast("滚动成功");
} else {
toast("滚动失败");
}
}
main();
```
## scrollBackward()
> 执行条件:无障碍5.0以上或者手势执行为代理服务
向后滚动
@return {boolean|布尔型}
```js
function main(){
var node = scrollable(true).getOneNodeInfo(10000);
var result = node.scrollBackward();
if (result){
toast("滚动成功");
} else {
toast("滚动失败");
}
}
main();
```
## inputText(content)
> 执行条件:无障碍5.0以上或者手势执行为代理服务
对某个节点输入数据
@param content 要输入的内容
@return bool, true 成功 ,false 失败
```js
function main(){
//获取选择器对象
//选择 节点 clz=android.widget.EditText 所有节点
var node = clz("android.widget.EditText").getOneNodeInfo(10000);
if (node) {
node.inputText("飞云编程学院")
} else {
toast("无节点");
}
}
main();
```
## imeInputText(content)
> 执行条件:无障碍5.0以上或者手势执行为代理服务
使用输入法对某个节点输入数据,前提是已经设置本程序的输入法为默认输入法
@param content 要输入的内容
@return bool, true 成功 ,false 失败
```js
function main(){
//获取选择器对象
//选择 节点 clz=android.widget.EditText 所有节点
var node = clz("android.widget.EditText").getOneNodeInfo(10000);
if (node) {
logd(node.imeInputText("飞云编程学院"))
} else {
toast("无节点");
}
}
main();
```
## imeInputKeyCode(selectors,KEYCODE)
使用输入法输入内容,前提是已经设置本程序的输入法为默认输入法
适合没有节点的情况,例如游戏等
@param selectors 选择器,可以为空,如果为空,前提是输入框是聚焦的状态
@param KEYCODE 具体请看 KeyEvent.KEYCODE_*的值,例如66 = enter 67=del,84=SEARCH
@return {boolean|布尔型}
```js
// Demo By 飞云编程学院
if(agentEvent.setCurrentIme()) {
var selector = clz("android.widget.EditText");
var node = selector.getOneNodeInfo(1000)
if (selector) {
node.imeInputText("学脚本那里去?")
imeInputKeyCode(selector,66)
sleep(200);
node.imeInputText("飞云编程学院")
imeInputKeyCode(selector,66)
sleep(200);
node.imeInputText("网址是多少?")
imeInputKeyCode(selector,66)
sleep(200);
node.imeInputText("www.feiyunjs.com")
} else {
toast("无节点");
}
logd('恢复输入法:' + agentEvent.restoreIme());
} else {
loge('设置输入法失败');
}
```
## clearText()
> 执行条件:无障碍5.0以上或者手势执行为代理服务
清除节点文本数据
```js
function main(){
//获取选择器对象
//选择 节点 clz=android.widget.EditText 所有节点
var node = clz("android.widget.EditText").getOneNodeInfo();
if (node) {
var r =node.clearText();
logd("r -=> "+r);
} else {
toast("无节点");
}
}
main();
```
# 节点操作
## setFetchNodeMode(mode,fetchInvisibleNode,fetchNotImportantNode,algorithm)
设置获取节点的模式
@param mode 1 是增强型, 2 是快速型,默认是增强型
@param fetchInvisibleNode 是否抓取隐藏的元素,默认不抓取
@param fetchNotImportantNode 是否抓取不重要的元素
@param algorithm 节点查找算法,默认是nsf
- nsf 节点静态算法
- bsf 广度优先
- dsf 深度优先
@return {boolean|*}
```js
function main(){
var result = setFetchNodeMode(1,true,true,"nsf");
toast("result:"+result);
}
main();
```
## lastNodeEventTime()
> 适用版本(EC 5.14.0+)
获取最近的节点事件触发时间,可通过时间判断节点服务是否可用
@return {long} 长整型时间,毫秒级别
```js
function main(){
startEnv();
logd("开始监听");
while(true){
let d = lastNodeEventTime();
logd("time-> "+d);
sleep(1000)
}
}
main();
```
## has(selectors)
直接通过选择器判断元素是否存在
@param selectors 选择器
@return {null|布尔型}
```js
function main(){
var selectors = text("设置");
var result = has(selectors);
if (result){
toast("存在节点");
} else {
toast("不存在节点");
}
}
main();
```
```js
// Demo By 飞云编程学院
let selectors = text('我').visible(true);
if (has(selectors)) {
logd('存在');
} else {
logw('不存在');
}
```
## waitExistNode(selectors,timeout)
等待时长,通过选择器判断元素是否存在
@param selectors 选择器
@param timeout 超时时间,单位毫秒
@return {null|布尔型}
```js
function main(){
var selectors = text("设置");
var result = waitExistNode(selectors,10000);
if (result){
toast("存在节点");
} else {
toast("不存在节点");
}
}
main();
```
```js
// Demo By 飞云编程学院
//等待组件出现,使用visible(true)筛选客户区组件
let selectors = text('喜欢').visible(true);
if(waitExistNode(selectors,5000)) {
logd('组件出现');
} else {
logw('组件未出现');
};
```
## waitExistActivity(activity,timeout)
等待activity界面出现
@param activity 界面名称
@param timeout 超时时间,单位毫秒
@return {null|布尔型}
```js
function main(){
var ac = "com.xxx.MainActivity";
var result = waitExistActivity(ac,10000);
if (result){
toast("存在界面");
} else {
toast("不存在界面");
}
}
main();
```
```js
// Demo By 飞云编程学院
// 页面没出现,返回null
// 页面出现,返回true
if(waitExistActivity('com.ss.android.ugc.aweme.main.MainActivity',5000)) {
logd('页面出现');
} else {
logw('页面未出现');
};
```
## getText(selectors)
获取选择器文本
@param selectors 选择器
@return {字符串数组|null|字符串集合}
```js
function main(){
var selectors = clz("android.widget.TextView");
var result = getText(selectors);
toast("result:"+result);
}
main();
```
```js
// Demo By 飞云编程学院
let selectors = id("com.ss.android.ugc.aweme:id/bfk").visible(true);
logd(getText(selectors));
```
## getOneNodeInfo(selectors,timeout)
通过选择器 获取第一个节点信息
@param selectors 选择器
@param timeout 等待时间,单位是毫秒
@return NodeInfo 对象或者null
```js
function main(){
var result = getOneNodeInfo(clz("android.widget.TextView"),10*1000);
toast("result:"+result);
if (result){
result.click();
}
}
main();
```
```js
// Demo By 飞云编程学院
let selectors = id("com.ss.android.ugc.aweme:id/enk").visible(true);
let result = getOneNodeInfo(selectors,2000);
logd(result);
logd(result.text);
```
## getNodeInfo(selectors,timeout)
获取节点信息集合
@param selectors 选择器
@param timeout 等待时间,单位是毫秒
@return {null|NodeInfo数组|节点信息集合}
```js
function main(){
var result = getNodeInfo(clz("android.widget.TextView"),10*1000);
toast("result:"+result);
}
main();
```
```js
// Demo By 飞云编程学院
let selectors = id("com.ss.android.ugc.aweme:id/enk").visible(true);
let nodes = getNodeInfo(selectors,2000);
logd(nodes);
logd(nodes.length);
for (let i = 0; i < nodes.length; i++) {
logd(nodes[i].text);
}
```
#### 抖音发送随机表情
```js
// Demo By 飞云编程学院
let selectors = id("com.ss.android.ugc.aweme:id/bfc").clickable(true);
if (has(selectors)) {
// 遍历组件
var result = getNodeInfo(selectors, 1000);
if (result.length > 0) {
let num = random(0, result.length);
// 随机点击一个表情
if (result[num].click()) {
logd('已发送第 ' + (num + 1) + '个表情')
}
}
} else {
loge('未找到随机表情');
}
```
## getNodeAttrs(selectors,attr)
获取节点属性信息
@param selectors 选择器
@param attr 属性值,例如 text,className,更多的属性请参考NodeInfo对象属性
@return {null|字符串数组|Rect对象数组}
```js
function main(){
var selectors = clz("android.widget.TextView");
//获取所有text属性
var result = getNodeAttrs(selectors,"text");
toast("result:"+result);
//获取所有bounds属性
result = getNodeAttrs(selectors,"bounds");
toast("result:"+result);
}
main();
```
```js
// Demo By 飞云编程学院
let selectors = id("com.ss.android.ugc.aweme:id/enk").visible(true);
logd(getNodeAttrs(selectors,'text'));
```
## addNodeFlag(flag)
加上节点获取的某个标志位
@param flag 参见 AccessibilityServiceInfo.FLAG_*,如果是0是强制刷新
@return {null|boolean}
```js
function main(){
var result = addNodeFlag(0);
toast("result:"+result);
}
main();
```
## removeNodeFlag(flag)
移除节点获取的某个标志位
@param flag 参见 AccessibilityServiceInfo.FLAG_*,如果是0是强制刷新
@return {null|boolean}
```js
function main(){
var result = removeNodeFlag(0);
toast("result:"+result);
}
main();
```
## dumpXml()
将元素节点变成XML
@return string string|null
```js
function main(){
var result = dumpXml();
if (result){
toast("ok");
} else {
toast("no");
}
}
main();
```
## lockNode()
锁定当前节点,锁定后,后面就算界面刷新,但是节点还是老的信息,需要和releaseNode进行配合才能进行解锁
```js
function main(){
logd("锁住节点...")
//锁住节点,界面刷新也不动
lockNode();
for (var i = 0; i < 10; i++) {
var n = text("设置").getOneNodeInfo(1000);
logd("lock "+n)
sleep(1000);
}
logd("释放节点锁...")
//释放节点锁
releaseNode();
for (var i = 0; i < 10; i++) {
var n = text("设置").getOneNodeInfo(1000);
logd("unlocked "+n)
sleep(1000);
}
}
main();
```
## releaseNode()
释放节点的锁,释放后,当界面刷新的时候,节点信息会变成最新的
```js
function main(){
logd("锁住节点...")
//锁住节点,界面刷新也不动
lockNode();
for (var i = 0; i < 10; i++) {
var n = text("设置").getOneNodeInfo(1000);
logd("lock "+n)
sleep(1000);
}
logd("释放节点锁...")
//释放节点锁
releaseNode();
for (var i = 0; i < 10; i++) {
var n = text("设置").getOneNodeInfo(1000);
logd("unlocked "+n)
sleep(1000);
}
}
main();
```
# 实例
```js
let selector = id("com.ss.android.ugc.aweme:id/bfg");
if (selector) {
logd(JSON.stringify(getNodeRectEx(selector)));
var x = getNodeRectEx(selector).centerX;
var y = getNodeRectEx(selector).centerY;
logd(clickPoint(x,y));
}
/**
@description 获取节点尺寸等信息
@version 20201229
@author 飞云<283054503@qq.com>
@param selectors {object} :组件选择器
@return {object | null}:返回节点信息对象,失败返回null
*/
function getNodeRectEx(selector) {
if (selector) {
let node = selector.getOneNodeInfo(10 * 1000);
if (node) {
let nodeInfo = {
'top': node.bounds.top,
'right': node.bounds.right,
'bottom': node.bounds.bottom,
'left': node.bounds.left,
'width': node.bounds.right - node.bounds.left,
'height': node.bounds.bottom - node.bounds.top,
'centerX': Math.round((node.bounds.right + node.bounds.left) / 2),
'centerY': Math.round((node.bounds.bottom + node.bounds.top) / 2),
}
// logd(JSON.stringify(nodeInfo));
return nodeInfo;
}
}
}
```
```js
let selectors = id("com.ss.android.ugc.aweme:id/b3n").visible(true);
if (selectors) {
logd(getNodeCenterX(selectors));
logd(getNodeCenterY(selectors));
logd(getNodeWidth(selectors));
logd(getNodeHeight(selectors));
}
/**
@description 取组件中心点X坐标
@version 20201129
@author 飞云<283054503@qq.com>
@param selectors {object} :组件选择器
@return {int}:返回整数型坐标,失败返回null
*/
function getNodeCenterX(selectors) {
let node = selectors.getOneNodeInfo(1000);
if (node) {
let bounds = node.bounds;
let centerX = (bounds.left + bounds.right) / 2;
return Math.round(centerX);
}
}
/**
@description 取组件中心点Y坐标
@version 20201129
@author 飞云<283054503@qq.com>
@param selectors {object} :组件选择器
@return {int}:返回整数型坐标,失败返回null
*/
function getNodeCenterY(selectors) {
let node = selectors.getOneNodeInfo(1000);
if (node) {
let bounds = node.bounds;
let centerY = (bounds.top + bounds.bottom) / 2;
return Math.round(centerY);
}
}
/**
@description 取组件宽度
@version 20201129
@author 飞云<283054503@qq.com>
@param selectors {object} :组件选择器
@return {int}:返回整数型,失败返回null
*/
function getNodeWidth(selectors) {
let node = selectors.getOneNodeInfo(1000);
if (node) {
let bounds = node.bounds;
let width = bounds.right - bounds.left;
return Math.round(width);
}
}
/**
@description 取组件高度
@version 20201129
@author 飞云<283054503@qq.com>
@param selectors {object} :组件选择器
@return {int}:返回整数型,失败返回null
*/
function getNodeHeight(selectors) {
let node = selectors.getOneNodeInfo(1000);
if (node) {
let bounds = node.bounds;
let height = bounds.bottom - bounds.top;
return Math.round(height);
}
}
```