跳到主要内容

WebView 控件

操作 WebView 内嵌网页的 DOM 元素

概述

WebView 控件模块用于查找和操作 Android App 中 WebView 内嵌的网页元素(H5 页面)。

技术基础:

  • 基于 Android AccessibilityService
  • WebView 将 DOM 结构暴露给 AccessibilityService
  • 通过 AccessibilityNodeInfo 访问网页元素
  • 不依赖 Chrome DevTools Protocol

适用场景:

  • App 内嵌的 H5 页面
  • 微信小程序(WebView 部分)
  • 混合应用(Hybrid App)

快速开始

local node = require("new_node")

-- 方式 1:自动定位第一个 WebView
local link = node.webview():text("注册"):get()
if link then
link:click()
end

-- 方式 2:指定 WebView 后在其内部查找
local wv = node.app():type("WebView"):get()
if wv then
local btn = wv:query():text("提交"):get()
if btn then btn:click() end
end

-- 表单输入
local username = node.webview():id("username"):get()
if username then username:input("admin") end

local password = node.webview():id("password"):get()
if password then password:input("123456") end

local submit = node.webview():text("登录"):get()
if submit then submit:click() end

工作原理

AccessibilityService 视角下的 WebView:

┌─────────────────────────────────────────┐
│ App 界面 │
├─────────────────────────────────────────┤
│ LinearLayout │
│ ├── TextView ("标题") │
│ └── WebView ←── node.app():type("WebView") 定位
│ ├── [DOM 映射] heading "欢迎" │
│ ├── [DOM 映射] editText │
│ ├── [DOM 映射] button "登录" │
│ └── [DOM 映射] link "注册" │
└─────────────────────────────────────────┘

node.webview() 实际上是:
1. 找到第一个 type="WebView" 的控件
2. 在其子树中进行查询
3. 如果找不到 WebView,后续 get()/all() 等返回 nil/空表

关键点:

  • WebView 内的 DOM 元素会被映射为 AccessibilityNodeInfo
  • 不是所有 HTML 属性都能访问,只有无障碍相关的
  • 没有真正的 CSS/XPath 支持(AccessibilityService 不提供)

API 总览

详细文档

入口方法

方法返回值说明
node.webview()Query在第一个 WebView 内查询
node.webview(index)Query在第 n 个 WebView 内查询
element:query()Query在指定控件内创建查询器

约束方法(与 App 控件相同)

方法说明
.id(id)控件 ID
.idContains(text)ID 包含
.text(text)文本完全匹配
.textContains(text)文本包含
.textMatches(pattern)文本 Lua 模式匹配
.desc(desc)描述匹配
.type(className)控件类型
.clickable(bool)可点击
.scrollable(bool)可滚动
.enabled(bool)可用
.visible(bool)可见
.index(n)结果索引

配置方法

方法说明
.timeout(ms)等待超时(默认 0,最大 60000ms)
.interval(ms)轮询间隔(默认 100ms)
.limit(n)限制 all() 返回数量

终结方法

方法返回值说明
.get()Element/nil获取第一个匹配
.all()table获取所有匹配(空时返回 {}
.exists()boolean是否存在
.count()number匹配数量
.waitUntilGone(ms)boolean等待控件消失
.debug()string打印查询条件(调试用)

快捷方法

方法返回值说明
.tryClick()boolean找到则点击
.tryLongClick()boolean找到则长按
.tryInput(text)boolean找到则输入
.tryClear()boolean找到则清空
.tryFocus()boolean找到则聚焦

使用示例

基础查找

-- 在第一个 WebView 内查找文本
local elem = node.webview():text("注册"):get()
if elem then
elem:click()
end

-- 在第二个 WebView 内查找
local elem = node.webview(2):text("确定"):get()

-- 先定位 WebView,再在其内部查找
local wv = node.app():type("WebView"):get()
if wv then
local btn = wv:query():text("提交"):get()
if btn then btn:click() end
end

表单操作

-- 输入用户名(通过 ID 定位)
local username = node.webview():id("username"):get()
if username then
username:input("admin")
end

-- 输入密码
local password = node.webview():id("password"):get()
if password then
password:input("123456")
end

-- 点击登录按钮
local login = node.webview():text("登录"):clickable():get()
if login then
login:click()
end

带等待的查找

-- 等待页面加载完成
local success = node.webview():text("加载完成"):timeout(10000):get()
if success then
print("页面加载完成")
end

-- 等待并检查登录结果
if node.webview():text("登录成功"):timeout(5000):exists() then
print("登录成功")
elseif node.webview():text("密码错误"):exists() then
print("登录失败")
end

遍历元素

-- 获取所有链接文本
local links = node.webview():clickable():all()
if links then
for i, link in ipairs(links) do
print(i, link.text)
end
end

关系查询

local label = node.webview():text("商品名称"):get()
if label then
-- 获取父控件
local card = label:parent()

-- 在父控件内查找价格
local price = card:query():textContains("¥"):get()
if price then
print("价格:", price.text)
end

-- 获取兄弟控件
local next = label:next()
end

Element 属性

WebView 内的元素与 App 控件共享相同的 Element 接口:

属性类型说明
.textstring文本内容
.descstring描述
.idstring控件 ID
.typestring类型(className)
.boundsRect位置大小
.clickableboolean可点击
.enabledboolean可用

最佳实践

1. 确保 WebView 已加载

-- ❌ WebView 可能还没加载完
local btn = node.webview():text("登录"):get()

-- ✅ 等待 WebView 内容出现
local btn = node.webview():text("登录"):timeout(5000):get()

2. 使用稳定的定位方式

-- ✅ ID 最稳定
node.webview():id("submit-btn")

-- ⚠️ 文本可能随语言变化
node.webview():text("Login")

3. 组合多个约束

-- ❌ 可能匹配多个
node.webview():clickable():get()

-- ✅ 添加更多约束
node.webview():text("提交"):clickable():get()

4. 检查返回值

-- ❌ 不检查可能 nil 报错
node.webview():text("按钮"):get():click()

-- ✅ 检查返回值
local btn = node.webview():text("按钮"):get()
if btn then btn:click() end

注意事项

  1. WebView 必须已加载 - 确保 WebView 已打开并加载完成
  2. 某些 App 可能禁用 - 部分 App 会禁用 WebView 的无障碍访问
  3. 复杂页面性能 - 大型网页控件树遍历可能较慢
  4. iframe 支持有限 - 跨域 iframe 内容可能无法访问
  5. 没有 CSS/XPath - AccessibilityService 不提供这些能力