节点

节点信息

节点对象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整型节点的绘制顺序
boundsRect型空间对象
top整型顶部位置
bottom整型底部位置
left整型左边位置
right整型右边位置
visibleBoundsRect型可视空间对象

getOneNodeInfo(timeout)

通过选择器获取第一个节点对象

@param timeout 等待时间,单位是毫秒, 如果是0,代表不等待
@return NodeInfo 对象 或者null

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();
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 节点信息集合

function main(){ //获取选择器对象 //选择 节点 clz=android.widget.CheckBox所有节点, var node = clz("android.widget.CheckBox").getNodeInfo(10000); logd(node); } main();
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

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

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 节点集合

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 节点集合

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 节点集合

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 节点集合(数组)

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();
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 失败

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|布尔型}

function main(){ var node = text("我是文本").getOneNodeInfo(10000); var result = node.clickEx(); if (result){ toast("点击成功"); } else { toast("点击失败"); } } main();

longClick()

执行条件:无障碍7.0以上或者手势执行为代理服务

长点击节点

@return bool, true 成功 ,false 失败

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|布尔型}

function main(){ var node = text("我是文本").getOneNodeInfo(10000); var result = node.longClickEx(); if (result){ toast("点击成功"); } else { toast("点击失败"); } } main();

clickCenter()

执行条件:无障碍7.0以上或者手势执行为代理服务

节点点击中心点

@return bool, true 成功 ,false 失败

function main(){ //获取选择器对象 //选择 节点 clz=android.widget.CheckBox所有节点 var node = clz("android.widget.CheckBox").getOneNodeInfo(10000); if (node) { node.clickCenter(); } else { toast("无节点"); } } main();

refresh()

刷新节点缓存

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代表有

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|布尔型}

function main(){ var node = scrollable(true).getOneNodeInfo(10000); var result = node.scrollForward(); if (result){ toast("滚动成功"); } else { toast("滚动失败"); } } main();

scrollBackward()

执行条件:无障碍5.0以上或者手势执行为代理服务

向后滚动

@return {boolean|布尔型}

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 失败

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 失败

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|布尔型}

// 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以上或者手势执行为代理服务

清除节点文本数据

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|*}

function main(){ var result = setFetchNodeMode(1,true,true,"nsf"); toast("result:"+result); } main();

lastNodeEventTime()

适用版本(EC 5.14.0+)

获取最近的节点事件触发时间,可通过时间判断节点服务是否可用

@return {long} 长整型时间,毫秒级别

function main(){ startEnv(); logd("开始监听"); while(true){ let d = lastNodeEventTime(); logd("time-> "+d); sleep(1000) } } main();

has(selectors)

直接通过选择器判断元素是否存在

@param selectors 选择器
@return {null|布尔型}

function main(){ var selectors = text("设置"); var result = has(selectors); if (result){ toast("存在节点"); } else { toast("不存在节点"); } } main();
// Demo By 飞云编程学院 let selectors = text('我').visible(true); if (has(selectors)) { logd('存在'); } else { logw('不存在'); }

waitExistNode(selectors,timeout)

等待时长,通过选择器判断元素是否存在

@param selectors 选择器
@param timeout 超时时间,单位毫秒
@return {null|布尔型}

function main(){ var selectors = text("设置"); var result = waitExistNode(selectors,10000); if (result){ toast("存在节点"); } else { toast("不存在节点"); } } main();
// 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|布尔型}

function main(){ var ac = "com.xxx.MainActivity"; var result = waitExistActivity(ac,10000); if (result){ toast("存在界面"); } else { toast("不存在界面"); } } main();
// Demo By 飞云编程学院 // 页面没出现,返回null // 页面出现,返回true if(waitExistActivity('com.ss.android.ugc.aweme.main.MainActivity',5000)) { logd('页面出现'); } else { logw('页面未出现'); };

getText(selectors)

获取选择器文本

@param selectors 选择器
@return {字符串数组|null|字符串集合}

function main(){ var selectors = clz("android.widget.TextView"); var result = getText(selectors); toast("result:"+result); } main();
// 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

function main(){ var result = getOneNodeInfo(clz("android.widget.TextView"),10*1000); toast("result:"+result); if (result){ result.click(); } } main();
// 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数组|节点信息集合}

function main(){ var result = getNodeInfo(clz("android.widget.TextView"),10*1000); toast("result:"+result); } main();
// 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); }

抖音发送随机表情

// 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对象数组}

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();
// 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}

function main(){ var result = addNodeFlag(0); toast("result:"+result); } main();

removeNodeFlag(flag)

移除节点获取的某个标志位

@param flag 参见 AccessibilityServiceInfo.FLAG_*,如果是0是强制刷新
@return {null|boolean}

function main(){ var result = removeNodeFlag(0); toast("result:"+result); } main();

dumpXml()

将元素节点变成XML

@return string string|null

function main(){ var result = dumpXml(); if (result){ toast("ok"); } else { toast("no"); } } main();

lockNode()

锁定当前节点,锁定后,后面就算界面刷新,但是节点还是老的信息,需要和releaseNode进行配合才能进行解锁

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()

释放节点的锁,释放后,当界面刷新的时候,节点信息会变成最新的

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();

实例

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; } } }
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); } }