全局方法
基于 AccessibilityService 提供的全局能力。
状态检查
node.isReady()
检查无障碍服务是否就绪。
if not node.isReady() then
error("请先启用无障碍服务")
end
返回值: boolean
说明: 返回 false 的情况:
- 无障碍服务未启用
- 无障碍服务已启用但未连接
- 当前无可访问的窗口
全局动作
基于 performGlobalAction() 实现,所有方法返回 boolean。
node.back()
模拟返回键。
local success = node.back()
实现: performGlobalAction(GLOBAL_ACTION_BACK)
node.home()
返回主屏幕。
node.home()
实现: performGlobalAction(GLOBAL_ACTION_HOME)
node.recents()
打开最近任务列表。
node.recents()
实现: performGlobalAction(GLOBAL_ACTION_RECENTS)
node.notifications()
打开通知栏。
node.notifications()
实现: performGlobalAction(GLOBAL_ACTION_NOTIFICATIONS)
node.quickSettings()
打开快速设置面板。
node.quickSettings()
实现: performGlobalAction(GLOBAL_ACTION_QUICK_SETTINGS)
node.powerDialog()
打开电源菜单(关机/重启)。
node.powerDialog()
实现: performGlobalAction(GLOBAL_ACTION_POWER_DIALOG)
node.splitScreen()
切换分屏模式。
node.splitScreen()
版本要求: Android 7.0 (API 24) 及以上
实现: performGlobalAction(GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN)
node.lockScreen()
锁定屏幕。
node.lockScreen()
版本要求: Android 9.0 (API 28) 及以上
实现: performGlobalAction(GLOBAL_ACTION_LOCK_SCREEN)
手势操作
基于 AccessibilityService.dispatchGesture() 实现。
手势 API 需要 Android 7.0 (API 24) 及以上版本。
node.tap(x, y)
点击指定坐标。
local success = node.tap(540, 960)
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| x | number | X 坐标 |
| y | number | Y 坐标 |
边界条件:
- 坐标超出屏幕 - 返回 false
- 负数坐标 - 返回 false
返回值: boolean
node.longTap(x, y, duration)
长按指定坐标。
-- 长按 1 秒
node.longTap(540, 960, 1000)
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| x | number | X 坐标 |
| y | number | Y 坐标 |
| duration | number | 持续时间(毫秒),默认 500,最小 100 |
边界条件:
- 坐标超出屏幕 - 返回 false
- duration 过小 - 可能不被识别为长按
返回值: boolean
node.swipe(x1, y1, x2, y2, duration)
滑动手势。
-- 从 (100, 500) 滑动到 (100, 200),持续 300ms
node.swipe(100, 500, 100, 200, 300)
-- 向下滑动刷新
node.swipe(540, 300, 540, 800, 500)
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| x1, y1 | number | 起点坐标 |
| x2, y2 | number | 终点坐标 |
| duration | number | 持续时间(毫秒),最小 50ms |
边界条件:
- 起点终点相同 - 等效于长按
- 坐标超出屏幕 - 自动裁剪到屏幕边界
- duration 过小 - 可能导致滑动不流畅
返回值: boolean
node.pinch(cx, cy, startDistance, endDistance, duration)
执行缩放手势(双指捏合/张开)。
-- 放大:双指张开
node.pinch(540, 960, 100, 300, 500)
-- 缩小:双指捏合
node.pinch(540, 960, 300, 100, 500)
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| cx | number | 中心点 X |
| cy | number | 中心点 Y |
| startDistance | number | 起始距离(两指间距),最小 10 |
| endDistance | number | 结束距离,最小 10 |
| duration | number | 持续时间(毫秒),最小 50ms |
边界条件:
- startDistance == endDistance - 无实际缩放效果
- 距离值过大 - 手指可能超出屏幕边界,自动裁剪
- 中心点超出屏幕 - 返回 false
返回值: boolean
node.gesture(points, duration)
执行自定义手势路径。
-- 直线滑动
node.gesture({{100, 500}, {100, 200}}, 300)
-- 曲线路径(贝塞尔曲线效果)
node.gesture({
{100, 500},
{150, 400},
{200, 300},
{250, 200}
}, 500)
-- 九宫格解锁:1 -> 5 -> 9
node.gesture({
{270, 800}, -- 点 1
{540, 1100}, -- 点 5
{810, 1400} -- 点 9
}, 500)
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| points | table | 路径点数组 {{x1,y1}, {x2,y2}, ...},至少 2 个点 |
| duration | number | 持续时间(毫秒),最小 50ms |
边界条件:
- 点数少于 2 个 - 返回 false
- 点坐标超出屏幕 - 自动裁剪到屏幕边界
- duration 过小 - 可能导致手势不流畅
返回值: boolean
工具方法
node.root()
获取控件树根节点。
local root = node.root()
if root then
print(root.type) -- 通常是 "FrameLayout"
print(root.childCount)
end
返回值: Element - 根节点 Element 对象
node.focused()
获取当前焦点控件。
local focused = node.focused()
if focused then
print("当前焦点:", focused.text)
end
返回值: Element/nil
node.currentPackage()
获取当前前台应用包名。
local pkg = node.currentPackage()
print(pkg) -- "com.example.app"
返回值: string
node.currentActivity()
获取当前 Activity 类名。
local activity = node.currentActivity()
print(activity) -- "com.example.app.MainActivity"
返回值: string
node.waitUntil(condition, timeout, interval)
等待条件成立。
-- 等待控件出现
local success = node.waitUntil(function()
return node.app():text("完成"):exists()
end, 5000)
-- 等待窗口变化,每 200ms 检查一次
local success = node.waitUntil(function()
return node.currentPackage() == "com.target.app"
end, 10000, 200)
if success then
print("条件满足")
else
print("等待超时")
end
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| condition | function | 条件函数,返回 true 表示条件成立 |
| timeout | number | 超时时间(毫秒),最大 60000 |
| interval | number | 轮询间隔(毫秒),默认 100,最小 10 |
边界条件:
timeout <= 0- 立即返回条件当前状态condition函数抛出异常 - 返回 falseinterval > timeout- 仅检查一次
返回值: boolean - 条件是否在超时前成立
node.dump(maxDepth)
获取当前界面控件树快照(用于调试)。
local tree = node.dump()
print(json.encode(tree))
-- 限制深度避免输出过大
local tree = node.dump(3) -- 最多 3 层
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| maxDepth | number | 可选,最大深度,默认无限制 |
返回值: table - 控件树结构
注意: 控件树可能很大,建议在调试时限制深度
-- 返回格式示例
{
type = "FrameLayout",
text = "",
id = "",
bounds = {x = 0, y = 0, width = 1080, height = 1920},
children = {
{type = "LinearLayout", ...},
{type = "TextView", text = "Hello", ...}
}
}
使用示例
自动返回主页
-- 连续返回直到主页
local maxTries = 10
for i = 1, maxTries do
if node.currentPackage() == "com.android.launcher" then
break
end
node.back()
sys.msleep(500)
end
等待页面加载后操作
-- 点击按钮
local btn = node.app():text("打开"):get()
if btn then btn:click() end
-- 等待新页面加载
local success = node.waitUntil(function()
return node.app():text("页面标题"):exists()
end, 10000)
if success then
print("页面加载完成")
-- 继续操作...
else
print("加载超时")
end
滑动刷新
-- 下拉刷新
node.swipe(540, 400, 540, 1200, 300)
-- 等待刷新完成
node.waitUntil(function()
return not node.app():text("正在刷新"):exists()
end, 5000)
手势解锁
-- 九宫格解锁:L 形图案(1 -> 4 -> 7 -> 8 -> 9)
local points = {
{270, 800}, -- 点 1
{270, 1100}, -- 点 4
{270, 1400}, -- 点 7
{540, 1400}, -- 点 8
{810, 1400} -- 点 9
}
node.gesture(points, 500)