安卓节点找控件:选择器写法与失效排查
「设置」两个字明明在屏幕上,脚本点下去没反应。加一行日志,text("设置").getOneNodeInfo(3000) 返回 null。节点根本没取到,后面的点击当然是空的。
节点查找用选择器定位控件,找不到时先看层级和属性。我见过有人一上来就换 click 函数,换了三遍没用,最后发现是节点压根没取到。
点不到,先看返回什么
先看节点取没取到,别急着调点击函数:
function main() { var node = text("设置").getOneNodeInfo(3000); if (node) { logd(JSON.stringify(node)); node.click(); } else { loge("没取到节点"); }}
main();getOneNodeInfo(3000) 里的 3000 是等待时间,单位毫秒;填 0 表示不等待,立刻返回。返回 null 就是没找到,别急着怀疑点击函数。
拿到了节点,JSON.stringify 打出来能看到它的属性。重点看四个:text、id、clz(类名)、pkg(包名),再加上 bounds 坐标。选择器就是按这些属性找的。
选择器就那么几种写法
常写的就这几种,按属性分:
| 写法 | 匹配方式 | 例子 |
|---|---|---|
text() | 文本全等 | text("设置") |
textMatch() | 文本正则 | textMatch(".*设置.*") |
id() | 资源 ID 全等 | id("com.xx:id/a1") |
idMatch() | 资源 ID 正则 | idMatch(".*a1.*") |
clz() | 类名全等 | clz("android.widget.TextView") |
clzMatch() | 类名正则 | clzMatch(".*TextView.*") |
pkg() | 包名全等 | pkg("com.xx") |
pkgMatch() | 包名正则 | pkgMatch(".*tencent.*") |
desc() | 描述全等 | desc("我是描述") |
descMatch() | 描述正则 | descMatch(".*描述.*") |
bounds() | 区域范围 | bounds(0, 0, 800, 800) |
visible() / depth() / drawingOrder() | 可见、层级、绘制顺序 | visible(true) |
xpath() | XPath 定位,EC 10.12.0+ | xpath("//node[@text='易点云测']") |
全等匹配最脆,文本里多一个空格、多一个数字就失效。动态内容(消息条数、价格、时间)一律用 textMatch。
还有一种情况:游戏、Flutter 页面、自绘控件,节点树里根本没有文字,选择器怎么写都取不到,这种只能转图色。
多个属性可以串起来,一条选择器同时满足:
// 包含「选择器」、已勾选、且类名是 CheckBoxvar selector = textMatch(".*选择器.*") .checked(true) .clz("android.widget.CheckBox");var result = click(selector);click(selector) 是选择器版本,传选择器对象进去,省掉先取节点这一步。
层级不对,用 child() 和 parent() 串
同一个「设置」可能在列表里出现好几次,直接 text("设置") 取到的是第一个,不一定是你想要的那个。这时候靠层级:
// 先选滚动容器,再在子节点里找 CheckBoxvar selector = clz("android.widget.ScrollView") .child() .clz("android.widget.CheckBox");var result = click(selector);.child() 表示下一步的条件作用在子节点上。父级稳定、子级好认,这种组合比硬拼属性可靠。
反过来也行,先拿到节点,再往回找父级:
function main() { var node = clz("android.widget.CheckBox").getOneNodeInfo(10000); if (node) { var p = node.parent(); logd(JSON.stringify(p)); }}
main();节点对象本身还能继续在子节点里找:node.getOneNodeInfo(text("广告"), 10000) 或者 node.getNodeInfo(text("广告").clz("android.widget.TextView"), 10000)。父节点只找一次,后面都在子节点里找,比每次从根节点扫一遍快。
还是找不到,按这份清单走
按顺序来,前一条不通再看下一条:
- 确认设备有节点能力。无障碍、代理、root 模式都有节点和点击;纯 HID 模式只有点击,取不到节点,这种设备直接走图色或 OCR。
- 用
getOneNodeInfo(0)快速看一眼返回,别用长等待反复试。 - 全等换正则:
text→textMatch,id→idMatch,clz→clzMatch。 - 换属性:文本不稳就换
id或desc,界面改版时desc往往比text活得久。 - 加范围:
bounds()把查找限制在屏幕某块区域,绕开同名控件。 - 先父后子:父级
child()级联,或者先取节点再parent()往回走。 - 对照 IDEA 的节点面板,看控件的真实属性、
depth和drawingOrder。被遮挡、不可见的控件,getOneNodeInfo也可能取不到,需要滚动或等待后再取。 - 以上都不行,就别在节点上耗了,改图色或 OCR。相关写法在 图色 image-api 和 OCR ocr-api 里。
节点没取到头,后面写多少点击都是空的。先把返回值和属性看清楚,再决定换哪种选择器。
关于我们:EasyClick 做手机自动化脚本开发和群控,覆盖安卓免 ROOT、iOS 免越狱、鸿蒙。选择器和节点的完整函数说明在 选择器&节点;安卓开发环境还没装好的,看 安卓自动化开发插件安装教程。