找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 16|回复: 1

屏幕控件扫描仪穷人版布局分析器

[复制链接]
发表于 半小时前 | 显示全部楼层 |阅读模式
屏幕控件扫描仪穷人版布局分析器

QQ截图20261105061938.png

QQ截图20261105080109.png

QQ截图20261105080058.png

  1. /**
  2. * 案例33:屏幕控件扫描仪穷人版布局分析器
  3. *
  4. * 题材来源:写自动化脚本最烦的就是"不知道控件在哪",
  5. *         "布局分析"是收费+电脑端功能,这个脚本让手机
  6. *          直接跑一遍,当前屏幕所有可点控件的文字、id、中心坐标全列出来,
  7. *          复制坐标就能写 clickPoint(x,y)。
  8. * 原理:auto.getXml() 返回当前窗口的无障碍节点树 XML(案例31锤死可用),
  9. *      正则扒出每个 <node> 标签的 text / resource-id / class / bounds /
  10. *      clickable 属性,bounds 算中心点,过滤掉纯装饰节点。
  11. * 零新 API:只用 auto.getXml() + printl + 正则,纯 ES5。
  12. *
  13. * 用法:把脚本挂后台,切到任何 App 界面(登录页、设置页、商品页...),
  14. *      运行 → 日志里就是这张屏幕的"控件地图"。
  15. *      想只看某个按钮:过滤关键字 填 "登录" 之类的词。
  16. */

  17. // ========== 配置区 ==========
  18. var 过滤关键字 = "";     // 空=列全部可点控件;填词=只显示名字含这个词的
  19. var 最多列出 = 40;       // 防止刷屏,超过就省略
  20. var 只列可点击 = true;   // false=连有文字的节点一起列(有些文字不可点但值得知道)
  21. var 输出间隔毫秒 = 300;  // 有节奏输出:清单每行之间的停顿,0=瞬间刷完

  22. // ========== 属性扒取 ==========
  23. function 扒属性(标签, 属性名) {
  24.     var m = 标签.match(new RegExp(属性名 + '="([^"]*)"'));
  25.     return m == null ? "" : m[1];
  26. }

  27. // bounds="[110,1140][970,1228]" → {x,y 中心点, w,h 宽高}
  28. function 解析bounds(串) {
  29.     var m = 串.match(/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/);
  30.     if (m == null) return null;
  31.     var 左 = parseInt(m[1], 10), 上 = parseInt(m[2], 10);
  32.     var 右 = parseInt(m[3], 10), 下 = parseInt(m[4], 10);
  33.     return {
  34.         x: Math.round((左 + 右) / 2),
  35.         y: Math.round((上 + 下) / 2),
  36.         w: 右 - 左,
  37.         h: 下 - 上
  38.     };
  39. }

  40. // 控件"人话名":text > content-desc > resource-id 尾段 > class 尾段
  41. function 取名字(标签) {
  42.     var 文 = 扒属性(标签, "text");
  43.     if (文 !== "") return 文;
  44.     var desc = 扒属性(标签, "content-desc");
  45.     if (desc !== "") return desc;
  46.     var rid = 扒属性(标签, "resource-id");
  47.     if (rid !== "") return "#" + rid.split("/").pop();
  48.     var cls = 扒属性(标签, "class");
  49.     return cls === "" ? "(无名)" : cls.split(".").pop();
  50. }

  51. // ========== 主流程 ==========
  52. function main() {
  53.     printl("=== 案例33 屏幕控件扫描仪 启动 ===");
  54.     var xml;
  55.     try {
  56.         xml = "" + auto.getXml();
  57.     } catch (e) {
  58.         printl("❌ getXml 失败(无障碍没开?): " + e);
  59.         return;
  60.     }
  61.     if (xml.length < 10) { printl("❌ XML 是空的,当前界面可能不允许读取"); return; }

  62.     // 前台包名 + 屏幕尺寸(根节点 bounds)
  63.     var 包 = xml.match(/package="([^"]+)"/);
  64.     包 = 包 == null ? "未知" : 包[1];
  65.     var 首 = xml.match(/<node[^>]*>/);
  66.     var 根 = 首 == null ? null : 解析bounds(扒属性(首[0], "bounds"));
  67.     printl("📱 前台: " + 包 + " | 屏幕约 " + (根 ? 根.w + "x" + 根.h : "?") + " | XML " + xml.length + " 字符");

  68.     // 逐节点解析
  69.     var 标签表 = xml.match(/<node[^>]*>/g) || [];
  70.     var 清单 = [];
  71.     for (var i = 0; i < 标签表.length; i++) {
  72.         var 标签 = 标签表[i];
  73.         if (扒属性(标签, "visible-to-user") === "false") continue;   // 看不见的跳过
  74.         var 可点 = 扒属性(标签, "clickable") === "true";
  75.         var 有文 = 扒属性(标签, "text") !== "" || 扒属性(标签, "content-desc") !== "";
  76.         if (只列可点击 && !可点 && !有文) continue;
  77.         if (!可点 && !有文) continue;                                  // 纯装饰节点不要

  78.         var 名 = 取名字(标签);
  79.         if (过滤关键字 !== "" && 名.indexOf(过滤关键字) < 0) continue;

  80.         var b = 解析bounds(扒属性(标签, "bounds"));
  81.         if (b == null) continue;
  82.         清单.push({ 名: 名, 类: 扒属性(标签, "class").split(".").pop(), 可点: 可点, b: b });
  83.     }

  84.     printl("🔍 节点共 " + 标签表.length + " 个,入选 " + 清单.length + " 个" +
  85.            (过滤关键字 !== "" ? "(关键字:" + 过滤关键字 + ")" : "") + " ==========");
  86.     sleep(600);   // 报完统计,喘口气再逐行上清单
  87.     var 上限 = 清单.length < 最多列出 ? 清单.length : 最多列出;
  88.     for (var j = 0; j < 上限; j++) {
  89.         var c = 清单[j];
  90.         var 显名 = c.名.length > 20 ? c.名.slice(0, 20) + "…" : c.名;
  91.         printl(" " + (j + 1) + ". [" + 显名 + "] " + c.类 +
  92.                (c.可点 ? " 可点" : " 仅文字") +
  93.                " → clickPoint(" + c.b.x + ", " + c.b.y + ") 尺寸" + c.b.w + "x" + c.b.h);
  94.         if (输出间隔毫秒 > 0) sleep(输出间隔毫秒);
  95.     }
  96.     if (清单.length > 上限) printl(" ...其余 " + (清单.length - 上限) + " 个省略(调 最多列出)");
  97.     sleep(600);   // 清单完,收尾提示也留个节拍

  98.     printl("💡 用法:看中的控件,把行尾坐标抄进 clickPoint(x,y) 就能点;");
  99.     printl("   文字类控件也可以直接 auto.findText("" + (清单.length > 0 ? 清单[0].名 : "某文字") + "") 找节点再 clickNode;");
  100.     printl("   常驻玩法:每 2 秒扫一次 + 对比清单,界面出现新按钮(弹窗/广告)立刻报警。");
  101.     printl("=== 案例33 收工 ===");
  102. }

  103. main();
复制代码


0

主题

1

回帖

2

积分

软件网站新手

积分
2
发表于 27 分钟前 | 显示全部楼层

屏幕控件扫描仪穷人版布局分析器

看你的这个穷人版布局分析器挺实用的。我试了一下,确实挺方便的,不用再去手动找控件的位置。不过我这边用的是安卓手机,能直接跑吗?还有,你有没有考虑过怎么处理一些重复或相似的控件?会不会把它们都列出来了?
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|

GMT+8, 2026-11-6 07:19

安卓脚本软件 · 键鼠盒子 · HID · 机械臂

快速回复 返回顶部 返回列表

随机推荐 · 热帖 / 新帖 / 随便看看

社区会员(随机展示)