跳到主要内容

Element 控件对象

通过 query:get()query:all() 获得的控件对象。

属性

.text

控件的文本内容。

local elem = node.app():id("title"):get()
print(elem.text) -- "商品标题"

.hint

输入框的提示文本(EditText 的 hint)。

print(elem.hint)  -- "请输入用户名"

.desc

控件的描述(content-description)。

print(elem.desc)  -- "返回按钮"

.type

控件类型(Android 类名,对应 getClassName())。

print(elem.type)  -- "android.widget.Button"
print(elem.type) -- "android.widget.TextView"
print(elem.type) -- "android.view.View"

常见类型

  • android.widget.Button - 按钮
  • android.widget.TextView - 文本
  • android.widget.EditText - 输入框
  • android.widget.ImageView - 图片
  • android.widget.CheckBox - 复选框
  • android.widget.RecyclerView - 列表

.id

控件 ID(完整资源 ID,对应 getViewIdResourceName())。

print(elem.id)  -- "com.example:id/btn_ok"

说明:格式为 包名:id/ID名,可能为空字符串(控件无 ID)

.pkg

控件所属包名。

print(elem.pkg)  -- "com.example.app"

.bounds

控件位置和大小(屏幕坐标,基于 getBoundsInScreen())。

local b = elem.bounds
-- 基础属性
print(b.x, b.y) -- 左上角坐标(屏幕坐标系)
print(b.width, b.height) -- 宽高
-- 计算属性
print(b.left, b.top) -- = x, y
print(b.right, b.bottom) -- = x+width, y+height
print(b.centerX, b.centerY) -- 中心点

说明:坐标是相对于屏幕左上角的绝对坐标,可直接用于 node.tap(x, y)

.clickable

是否可点击。

if elem.clickable then
elem:click()
end

.longClickable

是否可长按。

.enabled

是否可用。

.checkable

是否可选中(CheckBox、Switch 等控件)。

.checked

是否已选中(CheckBox、Switch 等)。

.focused

是否获得焦点。

.focusable

是否可聚焦。

.selected

是否被选择。

.editable

是否可编辑(EditText 等输入控件)。

if elem.editable then
elem:input("文本")
end

.visible

是否可见(基于 isVisibleToUser())。

.scrollable

是否可滚动。

if elem.scrollable then
elem:scrollDown()
end

.childCount

子控件数量。

print(elem.childCount)  -- 3

.depth

控件在树中的深度。

.index

控件在父控件中的索引(从 0 开始)。

注意:这与 Query 的 index(n) 方法不同:

  • elem.index - 控件在父控件中的位置,从 0 开始
  • query:index(n) - 从查询结果中取第 n 个,从 1 开始

关系方法

:parent()

获取父控件。

返回值: Element/nil - 根节点的 parent() 返回 nil

local parent = elem:parent()
if parent then
print(parent.type)
end

:children()

获取所有直接子控件。

返回值: table - Element 数组,无子控件时返回空表 {}

-- children() 返回空表,不是 nil,可直接 ipairs
local children = elem:children()
for i, child in ipairs(children) do
print(i, child.text)
end

:child(n)

获取第 n 个子控件。

参数:

参数类型说明
nnumber索引,从 1 开始,负数从末尾计数

边界情况:

  • child(0) - 无效,返回 nil
  • 正数越界 - 返回 nil
  • 负数越界 - 返回 nil
-- 第一个子控件
local first = elem:child(1)

-- 最后一个子控件
local last = elem:child(-1)

-- 第 3 个子控件(如果不足 3 个,返回 nil)
local third = elem:child(3)

:next()

获取下一个兄弟控件。

返回值: Element/nil - 已是最后一个时返回 nil

local nextSibling = elem:next()

:prev()

获取上一个兄弟控件。

返回值: Element/nil - 已是第一个时返回 nil

local prevSibling = elem:prev()

:sibling(n)

获取第 n 个兄弟控件(相对于自身)。

参数:

参数类型说明
nnumber偏移量,正数向后,负数向前

边界情况:

  • sibling(0) - 返回自身
  • 越界 - 返回 nil
local next2 = elem:sibling(2)   -- 后面第 2 个
local prev3 = elem:sibling(-3) -- 前面第 3 个
local self = elem:sibling(0) -- 返回自身

:query()

在此控件内创建新的查询器。

返回值: Query - 以当前控件为根的查询器

说明:

  • 返回的 Query 只在当前控件的子树内查找
  • 无论当前控件是普通控件还是 WebView,都返回同类型的 Query
-- 在容器内查找
local container = node.app():id("list"):get()
local item = container:query():text("目标项"):get()

-- 在 WebView 内查找
local wv = node.app():type("WebView"):get()
local btn = wv:query():text("提交"):get()

-- 链式调用
local price = node.app():id("product")
:get()
:query():textContains("¥"):get()

动作方法

所有动作方法返回 boolean, string?,第二个值为失败原因。

错误字符串列表

错误字符串含义
"element is stale"控件已失效(界面已变化)
"action not supported"控件不支持此动作
"service disconnected"无障碍服务断开
"element not visible"控件不可见(可能被遮挡)
"element not focusable"控件不可聚焦
"element not editable"控件不可编辑
"element not scrollable"控件不可滚动
"scroll reached end"滚动已到达边界

:click()

点击控件。

返回值: boolean, string? - 是否成功,失败原因

local ok, err = elem:click()
if not ok then
print("点击失败:", err)
end

:longClick()

长按控件。

返回值: boolean, string?

local ok, err = elem:longClick()

:doubleClick()

双击控件。

返回值: boolean, string?

实现说明: 内部连续执行两次点击,间隔约 100ms

local ok, err = elem:doubleClick()

:input(text)

输入文本(会先清空原有内容)。

参数:

参数类型说明
textstring要输入的文本(支持中文和 Unicode)

说明:

  • 中文和特殊字符完全支持
  • 换行符 \n 会被正确处理
  • 空字符串等效于 clear()

返回值: boolean, string?

local ok, err = elem:input("hello world")
local ok, err = elem:input("你好世界") -- 中文支持
local ok, err = elem:input("line1\nline2") -- 多行文本

:append(text)

追加文本(不清空原有内容)。

参数:

参数类型说明
textstring要追加的文本

返回值: boolean, string?

local ok, err = elem:append(" 追加内容")

:clear()

清空文本。

适用控件: EditText 等可编辑控件

返回值: boolean, string?

local ok, err = elem:clear()

:focus()

获取焦点。

适用控件: EditText、Button 等可聚焦控件

返回值: boolean, string?

local ok, err = elem:focus()

:blur()

清除焦点(将焦点移至父控件或下一个可聚焦控件)。

返回值: boolean, string?

local ok, err = elem:blur()

:scrollUp() / :scrollDown() / :scrollLeft() / :scrollRight()

方向滚动控件。

适用控件: ScrollView、ListView、RecyclerView、ViewPager 等可滚动控件

返回值: boolean, string?

elem:scrollDown()
elem:scrollUp()
elem:scrollLeft()
elem:scrollRight()

:scrollForward() / :scrollBackward()

向前/向后滚动(AccessibilityService 原生支持)。

说明: Forward/Backward 由系统根据控件方向自动判断滚动方向

返回值: boolean, string?

elem:scrollForward()
elem:scrollBackward()

:scrollIntoView()

滚动父容器使自身可见。

返回值: boolean, string?

失败条件:

  • 父容器不可滚动
  • 控件已失效
  • 无法确定滚动方向
local item = node.app():text("目标项"):get()
if item then
local ok, err = item:scrollIntoView()
if not ok then print("滚动失败:", err) end
end

:select() / :deselect()

选中/取消选中控件。

适用控件: Tab、可选列表项、SegmentedControl 等

返回值: boolean, string?

:expand() / :collapse()

展开/折叠控件。

适用控件: ExpandableListView、Spinner、可折叠面板等

返回值: boolean, string?

:dismiss()

关闭控件(对话框等)。

适用控件: Dialog、BottomSheet、Snackbar、通知等可关闭的控件

返回值: boolean, string?

:copy() / :cut() / :paste()

复制/剪切/粘贴操作。

前置条件:

  • copy()/cut() - 需要先调用 setSelection() 选中文本
  • paste() - 系统剪贴板需要有内容

返回值: boolean, string?

-- 复制文本示例
elem:setSelection(0, 10) -- 先选中
elem:copy() -- 再复制

:setSelection(from, to)

设置文本选区。

参数:

参数类型说明
fromnumber起始位置(0 开始)
tonumber结束位置(不含)

边界情况:

  • from >= to - 清除选区(光标定位到 from)
  • 越界自动裁剪到文本长度
  • 仅对可编辑控件有效

返回值: boolean, string?

local ok = elem:setSelection(0, 5)   -- 选中前 5 个字符(索引 0-4)
local ok = elem:setSelection(5, 5) -- 光标定位到第 5 个字符后

实用方法

:refresh()

刷新控件信息(重新从控件树获取)。

返回值: boolean - 刷新是否成功

说明:

  • 返回 true - 控件仍然存在,属性已更新
  • 返回 false - 控件已不存在(界面已变化)
if elem:refresh() then
print(elem.text) -- 获取最新文本
else
print("控件已失效")
end

:isValid()

检查控件是否仍然有效(不刷新属性)。

返回值: boolean

与 refresh() 的区别:

  • isValid() - 仅检查引用是否有效,不更新属性,开销小
  • refresh() - 重新获取最新属性,开销较大
if elem:isValid() then
elem:click()
else
-- 控件已失效,需要重新查找
elem = node.app():text("确定"):get()
end

:screenshot()

截取控件区域截图。

返回值: Image/nil - 图像对象,失败返回 nil

说明:

  • 返回的 Image 与 screen 模块的 Image 类型相同
  • 控件失效或不可见时返回 nil
local img = elem:screenshot()
if img then
img:save("/sdcard/btn.png")
end

:dump()

获取控件及其子树的结构信息。

返回值: table - 控件树结构

local tree = elem:dump()
print(json.encode(tree))

:toString() / __tostring

获取控件的可读描述字符串。

返回值: string

local elem = node.app():text("确定"):get()
print(elem:toString()) -- "Button[text=确定, id=btn_ok, bounds=(100,200,300,250)]"

-- 也可以直接 print(自动调用 __tostring)
print(elem)

使用示例

遍历列表

local list = node.app():type("RecyclerView"):get()
if list then
local items = list:children()
for i, item in ipairs(items) do
-- 获取每项的标题和价格
local title = item:query():type("TextView"):index(1):get()
local price = item:query():textContains("¥"):get()

if title and price then
print(title.text, price.text)
end
end
end

关系查询

-- 通过子控件特征定位父容器
local label = node.app():text("商品名称"):get()
local card = label:parent():parent() -- 向上两级

-- 获取同级控件
local price = label:next() -- 下一个兄弟
local image = label:prev() -- 上一个兄弟

-- 在父容器内查找
local btn = card:query():text("购买"):get()
if btn then btn:click() end

等待状态变化

local checkbox = node.app():type("CheckBox"):get()

-- 点击切换状态
checkbox:click()

-- 等待状态变化
checkbox:refresh()
if checkbox.checked then
print("已选中")
end