Query 查询器
所有约束条件之间是 AND(且) 关系,控件必须同时满足所有约束才会被匹配。
-- 必须同时满足:type=Button AND text="确定" AND clickable=true
node.app():type("Button"):text("确定"):clickable()
如需 OR(或)逻辑,可使用以下方式:
-- 方式1:Lua 模式匹配(推荐)
node.app():textMatches("确定|取消") -- 匹配"确定"或"取消"
-- 方式2:多次查询
local btn = node.app():text("确定"):get() or node.app():text("取消"):get()
node.app()
创建 App 控件查询器。
声明
local query = node.app()
local query = node.app(mode)
参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| mode | number | 否 | 查询模式,默认 0 |
模式说明:
0- 标准模式,查询所有控件1- 过滤系统控件(状态栏、导航栏等)2- 仅可见控件
返回值
| 类型 | 说明 |
|---|---|
| Query | 查询器对象 |
示例
local node = require("new_node")
-- 标准模式
local q = node.app()
-- 过滤系统控件
local q = node.app(1)
.id(id)
添加 ID 约束。
声明
query:id(id)
参数
| 参数 | 类型 | 说明 |
|---|---|---|
| id | string | 控件 ID,支持完整 ID 或简写 |
示例
-- 完整 ID
node.app():id("com.example:id/btn_login")
-- 简写(自动补全当前包名)
node.app():id("btn_login")
.idContains(text)
添加 ID 包含约束。
示例
node.app():idContains("btn_") -- ID 包含 "btn_"
node.app():idContains("login") -- ID 包含 "login"
.idStartsWith(text)
添加 ID 前缀约束。
示例
node.app():idStartsWith("btn_") -- ID 以 "btn_" 开头
.idEndsWith(text)
添加 ID 后缀约束。
示例
node.app():idEndsWith("_button") -- ID 以 "_button" 结尾
.idMatches(pattern)
添加 ID Lua 模式约束。
示例
node.app():idMatches("btn_%d+") -- 匹配 btn_1, btn_2 等
.text(text)
添加文本约束(完全匹配)。
声明
query:text(text)
参数
| 参数 | 类型 | 说明 |
|---|---|---|
| text | string | 控件文本,完全匹配 |
示例
-- 完全匹配
node.app():text("登录")
node.app():text("确定")
.textContains(text)
添加文本包含约束。
声明
query:textContains(text)
示例
node.app():textContains("价格")
node.app():textContains("¥")
.textStartsWith(text)
添加文本前缀约束。
声明
query:textStartsWith(text)
示例
node.app():textStartsWith("第") -- 以"第"开头
node.app():textStartsWith("价格:") -- 以"价格:"开头
.textEndsWith(text)
添加文本后缀约束。
声明
query:textEndsWith(text)
示例
node.app():textEndsWith("元") -- 以"元"结尾
node.app():textEndsWith("...") -- 以"..."结尾
.textMatches(pattern)
添加文本 Lua 模式约束。
声明
query:textMatches(pattern)
参数
| 参数 | 类型 | 说明 |
|---|---|---|
| pattern | string | Lua 模式字符串 |
Lua 模式语法
| 模式 | 说明 |
|---|---|
%d | 数字 |
%a | 字母 |
%s | 空白字符 |
%w | 字母数字 |
. | 任意字符 |
* | 0 或多个 |
+ | 1 或多个 |
? | 0 或 1 个 |
^ | 开头 |
$ | 结尾 |
[abc] | 字符集 |
[^abc] | 排除字符集 |
转义特殊字符
如果要匹配字面字符,需用 % 转义:
| 字符 | 转义 | 示例 |
|---|---|---|
. | %. | "价格%.99" 匹配 "价格.99" |
( ) | %( %) | "%(总计%)" 匹配 "(总计)" |
% | %% | "100%%" 匹配 "100%" |
+ - * ? | %+ %- %* %? | |
[ ] | %[ %] | |
^ $ | %^ %$ | 仅在非首尾位置需转义 |
示例
node.app():textMatches("%d+%.%d%d") -- 匹配价格格式如 "12.99"
node.app():textMatches("^第%d+章") -- 以"第X章"开头
node.app():textMatches("[Ll]ogin") -- 匹配 Login 或 login
node.app():textMatches("价格.*元$") -- 以"元"结尾的价格
node.app():textMatches("折扣%(.*%)") -- 匹配 "折扣(50%)" 等
.desc(desc)
添加描述约束(content-description)。
声明
query:desc(desc)
示例
node.app():desc("返回按钮")
node.app():desc("登录按钮")
.descMatches(pattern)
添加描述 Lua 模式约束。
示例
node.app():descMatches(".*按钮$") -- 以"按钮"结尾
.descContains(text)
添加描述包含约束。
示例
node.app():descContains("按钮")
.descStartsWith(text)
添加描述前缀约束。
示例
node.app():descStartsWith("点击")
.descEndsWith(text)
添加描述后缀约束。
示例
node.app():descEndsWith("按钮")
.hint(text)
添加提示文本约束(EditText 的 hint)。
示例
node.app():hint("请输入用户名"):get()
.hintContains(text)
添加提示文本包含约束。
示例
node.app():hintContains("用户名"):get()
.hintStartsWith(text)
添加提示文本前缀约束。
示例
node.app():hintStartsWith("请输入"):get()
.hintEndsWith(text)
添加提示文本后缀约束。
示例
node.app():hintEndsWith("..."):get()
.hintMatches(pattern)
添加提示文本 Lua 模式约束。
示例
node.app():hintMatches("^请输入.*名$"):get()
.type(type)
添加控件类型约束。
声明
query:type(type)
常用类型
| 类型 | 说明 |
|---|---|
Button | 按钮 |
TextView | 文本 |
EditText | 输入框 |
ImageView | 图片 |
ImageButton | 图片按钮 |
CheckBox | 复选框 |
RadioButton | 单选按钮 |
Switch | 开关 |
SeekBar | 滑块 |
ProgressBar | 进度条 |
Spinner | 下拉选择 |
ListView | 列表 |
RecyclerView | 列表(推荐) |
ScrollView | 滚动视图 |
HorizontalScrollView | 水平滚动 |
ViewPager | 翻页视图 |
TabLayout | 标签栏 |
WebView | 网页视图 |
说明:可以使用简短类名(如 Button)或完整类名(如 android.widget.Button)
示例
node.app():type("Button")
node.app():type("EditText"):clickable()
.pkg(package)
添加包名约束。
示例
node.app():pkg("com.example.app"):text("确定")
.clickable(value)
添加可点击约束。
声明
query:clickable() -- 可点击
query:clickable(true) -- 可点击
query:clickable(false) -- 不可点击
.longClickable(value)
添加可长按约束。
声明
query:longClickable() -- 可长按
query:longClickable(true) -- 可长按
query:longClickable(false) -- 不可长按
.scrollable(value)
添加可滚动约束。
声明
query:scrollable() -- 可滚动
query:scrollable(true) -- 可滚动
query:scrollable(false) -- 不可滚动
示例
-- 查找可滚动的列表容器
local list = node.app():scrollable():type("RecyclerView"):get()
.enabled(value)
添加可用状态约束。
声明
query:enabled() -- 可用
query:enabled(false) -- 禁用状态
.editable(value)
添加可编辑约束。
声明
query:editable() -- 可编辑
query:editable(true) -- 可编辑
query:editable(false) -- 不可编辑
示例
-- 查找可编辑的输入框
node.app():type("EditText"):editable():get()
.focusable(value)
添加可聚焦约束。
声明
query:focusable() -- 可聚焦
query:focusable(false) -- 不可聚焦
.checkable(value)
添加可选中约束。
声明
query:checkable() -- 可选中
query:checkable(false) -- 不可选中
.checked(value)
添加选中状态约束。
示例
-- 查找已选中的复选框
node.app():type("CheckBox"):checked()
-- 查找未选中的
node.app():type("CheckBox"):checked(false)
.focused(value)
添加焦点状态约束。
示例
node.app():type("EditText"):focused()
.selected(value)
添加选择状态约束。
.visible(value)
添加可见性约束。
声明
query:visible() -- 可见
query:visible(false) -- 不可见
说明
基于 AccessibilityNodeInfo.isVisibleToUser() 属性:
- 控件在屏幕可见区域内
- 控件未被其他控件遮挡
- 控件的 visibility 属性为 VISIBLE
示例
-- 只查找屏幕上可见的按钮
node.app():type("Button"):visible():all()
.index(n)
从匹配结果中取第 n 个。
声明
query:index(n)
参数
| 参数 | 类型 | 说明 |
|---|---|---|
| n | number | 索引,从 1 开始,负数从末尾计数 |
边界情况
index(0)- 无效,返回 nil- 正数越界 - 返回 nil
- 负数越界 - 返回 nil
示例
-- 第一个 TextView
node.app():type("TextView"):index(1)
-- 最后一个
node.app():type("TextView"):index(-1)
-- 第 3 个(如果不足 3 个,get() 返回 nil)
node.app():type("TextView"):index(3)
.depth(n)
添加控件深度约束(根节点深度为 0)。
参数
| 参数 | 类型 | 说明 |
|---|---|---|
| n | number | 控件深度,0 表示根节点 |
边界条件
n < 0- 无效,不匹配任何控件- 深度值大于控件树实际深度 - 不匹配任何控件
示例
node.app():type("Button"):depth(5) -- 深度为 5 的按钮
node.app():depth(0) -- 根节点
.childCount(n)
添加子控件数量约束。
参数
| 参数 | 类型 | 说明 |
|---|---|---|
| n | number | 子控件数量,0 表示叶子节点 |
边界条件
n < 0- 无效,不匹配任何控件
示例
-- 有 3 个子控件的容器
node.app():childCount(3)
-- 没有子控件(叶子节点)
node.app():childCount(0)
.timeout(ms)
设置等待超时时间。
声明
query:timeout(ms)
参数
| 参数 | 类型 | 说明 |
|---|---|---|
| ms | number | 超时毫秒数,0 表示不等待,最大 60000 |
边界条件
ms = 0- 不等待,立即返回当前结果ms < 0- 无效,等效于 0ms > 60000- 自动裁剪到 60000
说明
设置后,get()、exists() 等终结方法会在超时时间内轮询查找控件。
示例
-- 等待最多 5 秒
local btn = node.app():text("加载完成"):timeout(5000):get()
if btn then btn:click() end
.interval(ms)
设置轮询间隔。
声明
query:interval(ms)
参数
| 参数 | 类型 | 说明 |
|---|---|---|
| ms | number | 轮询间隔毫秒数,默认 100,最小 10 |
说明:间隔过小会增加 CPU 负担,建议不低于 50ms
示例
-- 每 200ms 检查一次
node.app():text("完成"):timeout(5000):interval(200):get()
.limit(n)
限制 all() 返回的最大数量。
声明
query:limit(n)
参数
| 参数 | 类型 | 说明 |
|---|---|---|
| n | number | 最大返回数量,必须 > 0 |
边界条件
n <= 0- 无效,返回空表- 不设置 limit - 返回所有匹配项
说明
当页面控件数量很多时,使用 limit() 避免内存问题。
示例
-- 只获取前 10 个 TextView
local items = node.app():type("TextView"):limit(10):all()
.get()
获取第一个匹配的控件。
声明
local element = query:get()
返回值
| 类型 | 说明 |
|---|---|
| Element | 控件对象,未找到返回 nil |
示例
local btn = node.app():text("确定"):get()
if btn then
print(btn.text)
btn:click()
end
.all()
获取所有匹配的控件。
声明
local elements = query:all()
返回值
| 类型 | 说明 |
|---|---|
| table | Element 数组,未找到返回空表 {} |
示例
-- all() 返回空表,不是 nil,可以直接 ipairs
local items = node.app():type("TextView"):all()
print("找到", #items, "个")
for i, item in ipairs(items) do
print(i, item.text)
end
.exists()
检查是否存在匹配的控件。
声明
local exists = query:exists()
返回值
| 类型 | 说明 |
|---|---|
| boolean | 是否存在 |
示例
if node.app():text("登录成功"):timeout(3000):exists() then
print("登录成功")
end
.count()
获取匹配控件数量。
声明
local count = query:count()
返回值
| 类型 | 说明 |
|---|---|
| number | 匹配数量 |
示例
local count = node.app():type("Button"):count()
print("页面有", count, "个按钮")
.waitUntilGone(ms)
等待控件消失。
声明
local gone = query:waitUntilGone(ms)
参数
| 参数 | 类型 | 说明 |
|---|---|---|
| ms | number | 超时毫秒数 |
返回值
| 类型 | 说明 |
|---|---|
| boolean | true 表示控件已消失,false 表示超时仍存在 |
示例
-- 等待加载提示消失
if node.app():text("加载中"):waitUntilGone(5000) then
print("加载完成")
else
print("加载超时")
end
快捷方法
Query 提供了一系列快捷方法,自动处理 nil 检查,简化代码。
所有快捷方法返回值: boolean - 找到且操作成功返回 true,找不到或操作失败返回 false
注意:快捷方法会继承 Query 的 timeout() 配置,即先等待控件出现再执行操作。
.tryClick()
找到则点击,返回是否成功。
local ok = query:tryClick()
等价于:
local elem = query:get()
local ok = elem and elem:click() or false
.tryLongClick()
找到则长按。
local ok = query:tryLongClick()
.tryInput(text)
找到则输入文本。
local ok = query:tryInput("hello")
.tryClear()
找到则清空文本。
local ok = query:tryClear()
.tryFocus()
找到则聚焦。
local ok = query:tryFocus()
示例
-- 快捷写法
node.app():text("登录"):tryClick()
node.app():id("search"):tryInput("关键词")
-- 等价的标准写法
local btn = node.app():text("登录"):get()
if btn then btn:click() end
local input = node.app():id("search"):get()
if input then input:input("关键词") end
调试方法
.debug() / __tostring
获取 Query 的可读描述,用于调试。
local q = node.app():text("登录"):clickable():timeout(5000)
print(q:debug()) -- "Query[text='登录', clickable=true, timeout=5000]"
-- 也可以直接 print
print(q)