如何获取已打开的app页面的XML结构?如果XML结构不行的话,获取其他类型的结构化数据也可以。
要求:

  1. 数据具备可读性。
  2. 导出数据到手机存储。
824566462 · 2 小时前

回答

Hamibot AI
1
· 2 小时前

在 Hamibot 中,获取当前页面 UI 结构的标准方式是通过无障碍服务(Accessibility Service)遍历界面节点树。你可以编写脚本获取节点信息,将其格式化为 XML 并保存到手机存储中。

以下是实现该功能的完整脚本及操作步骤。

前置准备

  1. 无障碍权限:确保 Hamibot 助手已开启无障碍服务权限。
  2. 存储权限:确保 Hamibot 助手已获取文件存储权限,以便写入文件。
  3. 目标应用:运行脚本前,请先打开你需要获取 UI 结构的应用页面。

脚本代码

将以下代码保存为脚本并运行。脚本会遍历当前页面的所有控件,生成 XML 文件并保存至 /sdcard/hamibot_ui_dump.xml

// 文档地址 https://docs.hamibot.com/
auto.waitFor();

var root = auto.root();
if (root == null) {
    log("无法获取根节点,请检查无障碍权限是否已开启");
    hamibot.exit();
}

var xmlContent = dumpNode(root, 0);
var path = "/sdcard/hamibot_ui_dump.xml";

// 确保目录存在并写入文件
files.createWithDirs(path);
files.write(path, xmlContent);

log("UI 结构已导出至:" + path);
hamibot.exit();

// 递归遍历节点生成 XML
function dumpNode(node, indent) {
    var sb = "";
    var spaces = "";
    for (var i = 0; i < indent; i++) {
        spaces += "  ";
    }

    sb += spaces + "<node";
    
    // 获取常见属性
    var className = safeGet(node, "className");
    var text = safeGet(node, "text");
    var desc = safeGet(node, "contentDesc");
    var bounds = safeGet(node, "bounds");
    var visible = safeGet(node, "visibleToUser");
    var checkable = safeGet(node, "checkable");
    var checked = safeGet(node, "checked");
    var clickable = safeGet(node, "clickable");
    var scrollable = safeGet(node, "scrollable");
    var longClickable = safeGet(node, "longClickable");
    var enabled = safeGet(node, "enabled");
    var password = safeGet(node, "password");
    var selected = safeGet(node, "selected");
    var id = safeGet(node, "viewIdResourceName");

    // 添加属性到 XML
    if (className) sb += " class=\"" + escapeXml(className) + "\"";
    if (text) sb += " text=\"" + escapeXml(text) + "\"";
    if (desc) sb += " content-desc=\"" + escapeXml(desc) + "\"";
    if (bounds) sb += " bounds=\"" + escapeXml(bounds) + "\"";
    if (id) sb += " resource-id=\"" + escapeXml(id) + "\"";
    if (visible !== null) sb += " visible=\"" + visible + "\"";
    if (checkable !== null) sb += " checkable=\"" + checkable + "\"";
    if (checked !== null) sb += " checked=\"" + checked + "\"";
    if (clickable !== null) sb += " clickable=\"" + clickable + "\"";
    if (scrollable !== null) sb += " scrollable=\"" + scrollable + "\"";
    if (longClickable !== null) sb += " long-clickable=\"" + longClickable + "\"";
    if (enabled !== null) sb += " enabled=\"" + enabled + "\"";
    if (password !== null) sb += " password=\"" + password + "\"";
    if (selected !== null) sb += " selected=\"" + selected + "\"";

    var childCount = node.childCount();
    if (childCount > 0) {
        sb += ">\n";
        for (var i = 0; i < childCount; i++) {
            var child = node.child(i);
            if (child != null) {
                sb += dumpNode(child, indent + 1);
            }
        }
        sb += spaces + "</node>\n";
    } else {
        sb += "/>\n";
    }
    return sb;
}

// 安全获取属性,防止报错
function safeGet(node, methodName) {
    try {
        var func = node[methodName];
        if (typeof func == "function") {
            return func.call(node);
        }
        return null;
    } catch (e) {
        return null;
    }
}

// 转义 XML 特殊字符
function escapeXml(str) {
    if (str == null) return "";
    var s = str.toString();
    s = s.replace(/&/g, "&amp;");
    s = s.replace(/</g, "&lt;");
    s = s.replace(/>/g, "&gt;");
    s = s.replace(/"/g, "&quot;");
    s = s.replace(/'/g, "&apos;");
    return s;
}

使用说明

  1. 运行脚本:在 Hamibot 控制台或 APP 中运行上述脚本。
  2. 查看文件:脚本运行结束后,使用文件管理器打开 /sdcard/hamibot_ui_dump.xml
  3. 数据解读
    • class:控件类名,用于定位。
    • text:控件显示的文本。
    • content-desc:控件的内容描述,常用于无障碍定位。
    • bounds:控件在屏幕上的坐标范围。
    • resource-id:控件的资源 ID,是最稳定的定位方式。

注意事项

  • 兼容性:代码已适配 Rhino 1.7.15 引擎,未使用箭头函数或默认参数。
  • 性能:如果页面控件数量极大,生成过程可能需要几秒钟。
  • 权限:如果文件写入失败,请检查 Hamibot 是否拥有存储权限。
  • 动态内容:获取的是脚本运行瞬间的页面结构,动态变化的内容需要重新运行脚本获取。

通过这种方式,你可以获得结构清晰、可读性强的 XML 数据,便于后续编写自动化脚本时定位元素。

ℹ️ 本回答由 AI 生成,内容仅供参考