跳到主要内容

全局方法

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

参数:

参数类型说明
xnumberX 坐标
ynumberY 坐标

边界条件:

  • 坐标超出屏幕 - 返回 false
  • 负数坐标 - 返回 false

返回值: boolean


node.longTap(x, y, duration)

长按指定坐标。

-- 长按 1 秒
node.longTap(540, 960, 1000)

参数:

参数类型说明
xnumberX 坐标
ynumberY 坐标
durationnumber持续时间(毫秒),默认 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, y1number起点坐标
x2, y2number终点坐标
durationnumber持续时间(毫秒),最小 50ms

边界条件:

  • 起点终点相同 - 等效于长按
  • 坐标超出屏幕 - 自动裁剪到屏幕边界
  • duration 过小 - 可能导致滑动不流畅

返回值: boolean


node.pinch(cx, cy, startDistance, endDistance, duration)

执行缩放手势(双指捏合/张开)。

-- 放大:双指张开
node.pinch(540, 960, 100, 300, 500)

-- 缩小:双指捏合
node.pinch(540, 960, 300, 100, 500)

参数:

参数类型说明
cxnumber中心点 X
cynumber中心点 Y
startDistancenumber起始距离(两指间距),最小 10
endDistancenumber结束距离,最小 10
durationnumber持续时间(毫秒),最小 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)

参数:

参数类型说明
pointstable路径点数组 {{x1,y1}, {x2,y2}, ...},至少 2 个点
durationnumber持续时间(毫秒),最小 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

参数:

参数类型说明
conditionfunction条件函数,返回 true 表示条件成立
timeoutnumber超时时间(毫秒),最大 60000
intervalnumber轮询间隔(毫秒),默认 100,最小 10

边界条件:

  • timeout <= 0 - 立即返回条件当前状态
  • condition 函数抛出异常 - 返回 false
  • interval > timeout - 仅检查一次

返回值: boolean - 条件是否在超时前成立


node.dump(maxDepth)

获取当前界面控件树快照(用于调试)。

local tree = node.dump()
print(json.encode(tree))

-- 限制深度避免输出过大
local tree = node.dump(3) -- 最多 3 层

参数:

参数类型说明
maxDepthnumber可选,最大深度,默认无限制

返回值: 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)