跳到主要内容

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)

参数

参数类型必填说明
modenumber查询模式,默认 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)

参数

参数类型说明
idstring控件 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)

参数

参数类型说明
textstring控件文本,完全匹配

示例

-- 完全匹配
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)

参数

参数类型说明
patternstringLua 模式字符串

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)

参数

参数类型说明
nnumber索引,从 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)。

参数

参数类型说明
nnumber控件深度,0 表示根节点

边界条件

  • n < 0 - 无效,不匹配任何控件
  • 深度值大于控件树实际深度 - 不匹配任何控件

示例

node.app():type("Button"):depth(5)  -- 深度为 5 的按钮
node.app():depth(0) -- 根节点

.childCount(n)

添加子控件数量约束。

参数

参数类型说明
nnumber子控件数量,0 表示叶子节点

边界条件

  • n < 0 - 无效,不匹配任何控件

示例

-- 有 3 个子控件的容器
node.app():childCount(3)

-- 没有子控件(叶子节点)
node.app():childCount(0)

.timeout(ms)

设置等待超时时间。

声明

query:timeout(ms)

参数

参数类型说明
msnumber超时毫秒数,0 表示不等待,最大 60000

边界条件

  • ms = 0 - 不等待,立即返回当前结果
  • ms < 0 - 无效,等效于 0
  • ms > 60000 - 自动裁剪到 60000

说明

设置后,get()exists() 等终结方法会在超时时间内轮询查找控件。

示例

-- 等待最多 5 秒
local btn = node.app():text("加载完成"):timeout(5000):get()
if btn then btn:click() end

.interval(ms)

设置轮询间隔。

声明

query:interval(ms)

参数

参数类型说明
msnumber轮询间隔毫秒数,默认 100,最小 10

说明:间隔过小会增加 CPU 负担,建议不低于 50ms

示例

-- 每 200ms 检查一次
node.app():text("完成"):timeout(5000):interval(200):get()

.limit(n)

限制 all() 返回的最大数量。

声明

query:limit(n)

参数

参数类型说明
nnumber最大返回数量,必须 > 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()

返回值

类型说明
tableElement 数组,未找到返回空表 {}

示例

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

参数

参数类型说明
msnumber超时毫秒数

返回值

类型说明
booleantrue 表示控件已消失,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)