
前言大众点评的「免费抽」活动每天上线大量免费商品手速不够快很容易错过。本文介绍一款基于 AutoJs 的自动报名脚本内置可视化 UI 面板支持实时日志、轮次统计文字定位 绝对坐标点击双重机制稳定运行不漂移。一、效果预览脚本启动后自动循环执行以下流程找到「免费抽」按钮 → 进入详情页 → 点击「我要报名」→ 点击「确认报名」→ 返回列表 → 下滑 → 下一轮UI 面板实时显示 运行状态待机 / 运行中 / 已停止已执行轮次成功报名次数带时间戳的滚动日志二、环境要求项目要求运行环境AutoJs / AutoJs Pro6.x权限无障碍服务必须开启分辨率本脚本坐标基于2670×1200屏幕标定 Xiaomi 14)前置操作启动前手动打开大众点评停留在「免费抽」列表页⚠️分辨率不同的设备必须重新标定坐标详见第五节。三、完整代码ui; ui.layout( vertical bg#F2F2F7 h* w* horizontal bg#E54B1C padding16 14 gravitycenter_vertical w* text text textSize22sp marginRight10/ vertical layout_weight1 text text大众点评 · 自动报名 v4 textSize17sp textColor#FFFFFF textStylebold/ text text绝对坐标版 textSize10sp textColor#FFCCBB/ /vertical /horizontal vertical bg#FFFFFF margin12 12 12 0 padding16 14 w* horizontal w* marginBottom10 text text当前状态 textSize13sp textColor#999999 layout_weight1/ text idstatusDot text● textSize14sp textColor#CCCCCC marginRight4/ text idstatusText text待机 textSize14sp textColor#999999 textStylebold/ /horizontal horizontal w* marginBottom10 text text已执行轮次 textSize13sp textColor#999999 layout_weight1/ text idroundText text0 textSize18sp textColor#E54B1C textStylebold/ text text 轮 textSize13sp textColor#999999/ /horizontal horizontal w* text text成功报名 textSize13sp textColor#999999 layout_weight1/ text idsuccessText text0 textSize18sp textColor#4CAF50 textStylebold/ text text 次 textSize13sp textColor#999999/ /horizontal /vertical horizontal margin12 8 12 0 w* button idstartBtn text▶ 开始运行 bg#E54B1C textColor#FFFFFF layout_weight1 margin0 0 5 0 textSize15sp textStylebold/ button idstopBtn text⏹ 停止 bg#CCCCCC textColor#FFFFFF layout_weight1 margin5 0 0 0 textSize15sp textStylebold enabledfalse/ /horizontal button idclearBtn text 清空日志 bg#FFFFFF textColor#999999 margin12 6 12 0 w* textSize13sp/ horizontal margin14 8 0 4 gravitycenter_vertical text text 运行日志 textSize12sp textColor#AAAAAA layout_weight1/ text idlogCountText text0 条 textSize11sp textColor#CCCCCC marginRight14/ /horizontal ScrollView idlogScroll bg#FFFFFF margin12 0 12 12 padding0 h* w* text idlogText text— 点击「开始运行」以启动自动化流程 —#10;— 请确保已授予无障碍服务权限 —#10;— 启动前请先手动停在「免费抽」列表页 — textSize11sp textColor#666666 padding12 lineSpacingExtra5dp/ /ScrollView /vertical ); // // 配置区 // var SCREEN_BOTTOM_LIMIT 2600; var running false; var n 0; var successes 0; var logCount 0; var worker null; // // 日志工具 // function pad(v) { return (0 v).slice(-2); } function appendLog(msg) { var t new Date(); var ts pad(t.getHours()) : pad(t.getMinutes()) : pad(t.getSeconds()); var line [ ts ] msg; logCount; ui.run(function () { var prev ui.logText.getText().toString(); if (prev.indexOf(点击「开始运行」) 0) prev ; ui.logText.setText(prev (prev ? \n : ) line); ui.logCountText.setText(logCount 条); ui.logScroll.post(function () { ui.logScroll.smoothScrollTo(0, ui.logText.getHeight() 200); }); }); } function updateCounter() { ui.run(function () { ui.roundText.setText(String(n)); ui.successText.setText(String(successes)); }); } // // 工具函数 // // 下滑向上滑动列表 function swipeUp() { var w device.width, h device.height; var startY Math.round(h * 0.5); var endY startY - 355; swipe(w / 2, startY, w / 2, endY, 2500); appendLog( 下滑完成); } // 找「免费抽」并点击文字定位 超出可视区域自动下滑重试 function tapFreeDraw() { while (running) { var btn text(免费抽).findOne(500); if (btn) { var bounds btn.bounds(); var btnY bounds.top; if (btnY SCREEN_BOTTOM_LIMIT) { appendLog(⚠️ 「免费抽」超出可视区域Y: btnY 下滑重试); swipeUp(); sleep(1000); continue; } var targetX bounds.left - 200; var targetY bounds.top; click(targetX, targetY); appendLog(✅ 点击「免费抽」X: targetX Y: targetY ); return true; } else { appendLog(⚠️ 未找到「免费抽」下滑重试...); swipeUp(); sleep(2000); } } return false; } // // 单轮流程 // function freeDraw() { // 1. 点击「免费抽」文字定位 if (!tapFreeDraw()) return; // 2. 等详情页加载 sleep(1000); if (!running) return; // 3. 点击「我要报名」绝对坐标 click(892, 2489); appendLog(✅ 点击「我要报名」(892, 2489)); // 4. 等确认弹窗出现 sleep(1000); if (!running) return; // 5. 点击「确认报名」绝对坐标 click(600, 2472); appendLog(✅ 点击「确认报名」(600, 2472)); successes; updateCounter(); appendLog( 本轮报名完成); // 6. 等待结果页 sleep(1000); // 7. 返回列表页 back(); sleep(500); // 8. 下滑准备下一轮 swipeUp(); sleep(500); } // // 按钮开始 // ui.startBtn.on(click, function () { if (running) return; running true; n 0; successes 0; logCount 0; ui.startBtn.setEnabled(false); ui.stopBtn.setEnabled(true); ui.statusDot.setTextColor(colors.parseColor(#4CAF50)); ui.statusText.setTextColor(colors.parseColor(#4CAF50)); ui.statusText.setText(运行中); ui.logText.setText(); appendLog(▶ 自动化启动申请无障碍权限中...); worker threads.start(function () { auto.waitFor(); appendLog( 无障碍权限已就绪); for (var i 5; i 0; i--) { appendLog(⏳ i 秒后开始请切换到大众点评...); sleep(1000); } appendLog( 开始运行); while (running) { appendLog(─── 第 n 轮开始 ───); updateCounter(); try { freeDraw(); } catch (e) { appendLog(❌ 异常 e); sleep(1000); } n; } ui.run(function () { ui.startBtn.setEnabled(true); ui.stopBtn.setEnabled(false); ui.statusDot.setTextColor(colors.parseColor(#CCCCCC)); ui.statusText.setTextColor(colors.parseColor(#999999)); ui.statusText.setText(已停止); }); appendLog(⏹ 已停止共执行 n 轮成功报名 successes 次); }); }); // // 按钮停止 // ui.stopBtn.on(click, function () { running false; if (worker) { worker.interrupt(); worker null; } ui.startBtn.setEnabled(true); ui.stopBtn.setEnabled(false); ui.statusDot.setTextColor(colors.parseColor(#CCCCCC)); ui.statusText.setTextColor(colors.parseColor(#999999)); ui.statusText.setText(已停止); appendLog(⏹ 手动停止); }); // // 按钮清空日志 // ui.clearBtn.on(click, function () { logCount 0; ui.logText.setText(); ui.logCountText.setText(0 条); });四、代码解析4.1 UI 布局脚本顶部声明ui模式整个界面用 AutoJs 的 XML 布局描述无需额外 HTMLui; ui.layout(vertical bg#F2F2F7 h* w* ... /vertical);界面分为四个区域顶部标题栏大众点评品牌色#E54B1C展示脚本名和版本数据面板实时显示状态、轮次、成功次数三行horizontal布局操作按钮开始 / 停止 / 清空日志滚动日志区ScrollView包裹text自动滚动到底部4.2 双线程模型UI 线程与自动化逻辑线程分离是本脚本不卡顿的关键// 主线程只负责 UI 交互 ui.startBtn.on(click, function () { // 启动子线程执行自动化 worker threads.start(function () { auto.waitFor(); // 等待无障碍权限就绪 while (running) { freeDraw(); // 循环执行单轮流程 } }); });所有 UI 更新必须通过ui.run()切回主线程执行否则会报线程安全错误function appendLog(msg) { ui.run(function () { ui.logText.setText(...); // 更新日志文本 ui.logScroll.smoothScrollTo(0, ui.logText.getHeight() 200); // 滚动到底 }); }4.3 文字定位找「免费抽」按钮列表页的条目位置会随滑动变化不能用固定坐标因此用文字识别定位function tapFreeDraw() { while (running) { var btn text(免费抽).findOne(500); // 查找文本节点超时 500ms if (btn) { var bounds btn.bounds(); var btnY bounds.top; // 超出屏幕可视区域则先下滑 if (btnY SCREEN_BOTTOM_LIMIT) { swipeUp(); sleep(1000); continue; } // 向左偏移 200px 点击避开「免费抽」文字本身落在其他热区 var targetX bounds.left - 200; click(targetX, btnY); return true; } else { // 未找到则下滑继续搜索 swipeUp(); sleep(2000); } } }点击时对 X 坐标向左偏移 200px目的是点到条目的封面图片区域而非文字区域防止触发错误的点击响应。SCREEN_BOTTOM_LIMIT 2600是屏幕底部安全线超过这个 Y 值说明按钮被底部导航栏遮挡需要先滑动。4.4 绝对坐标「我要报名」和「确认报名」详情页和确认弹窗的布局固定直接用绝对坐标点击效率最高// 详情页底部「我要报名」按钮 click(892, 2489); // 确认弹窗「确认报名」按钮 click(600, 2472);两次点击之间sleep(1000)等待弹窗动画完成避免坐标还未渲染就点击失败。4.5 下滑函数每轮结束后需要下滑列表让下一个「免费抽」条目出现function swipeUp() { var w device.width, h device.height; var startY Math.round(h * 0.5); var endY startY - 355; // 向上滑动 355px swipe(w / 2, startY, w / 2, endY, 2500); // 持续 2500ms模拟慢滑 }X 坐标取屏幕中线Y 起点取屏幕中部滑动距离 355px 经过实测是列表单条目高度的合适步进。持续时间 2500ms 模拟人工慢速滑动减少被 App 检测到机械操作的风险。4.6 异常捕获与 5 秒倒计时// 启动后留 5 秒切换到大众点评 for (var i 5; i 0; i--) { appendLog(⏳ i 秒后开始请切换到大众点评...); sleep(1000); } // 每轮用 try-catch 包裹单轮报错不影响整体循环 while (running) { try { freeDraw(); } catch (e) { appendLog(❌ 异常 e); sleep(1000); } n; }5 秒倒计时给用户留出手动切换 App 的时间。try-catch确保某一轮报错如控件找不到、坐标点击无响应不会导致整个脚本崩溃退出。五、不同设备适配重新标定坐标脚本中的绝对坐标基于2670×1200 分辨率设备标定其他分辨率需修改以下三处第一步确认自己的分辨率在 AutoJs 控制台运行toast(device.width × device.height);第二步用布局分析工具拿坐标AutoJs 自带「布局范围分析」工具悬浮窗 → 布局分析打开大众点评「我要报名」页面点击对应按钮查看其坐标替换代码中的两行click(892, 2489); // 改为「我要报名」按钮的实际 X, Y click(600, 2472); // 改为「确认报名」按钮的实际 X, Y第三步调整屏幕底部安全线var SCREEN_BOTTOM_LIMIT 2600; // 改为 device.height - 70 左右第四步调整下滑距离var endY startY - 355; // 355 是单条目高度按实际屏幕比例等比缩放六、使用步骤1. 将代码保存为 .js 文件导入 AutoJs 2. 打开手机「无障碍服务」授权 AutoJs 3. 打开大众点评进入「免费抽」列表页 4. 切回 AutoJs点击「▶ 开始运行」 5. 5 秒倒计时内切换回大众点评 6. 脚本自动循环可随时点「⏹ 停止」终止七、常见问题Q脚本启动后什么都没发生A检查无障碍服务是否已授权给 AutoJs设置路径手机设置 → 无障碍 → AutoJs → 开启。Q日志显示「未找到『免费抽』」一直循环A确认已停留在大众点评「免费抽」列表页而不是首页或其他 Tab。Q点击了「免费抽」但进入的是错误的详情页AX 坐标偏移量bounds.left - 200可能需要根据你的布局调整适当增减偏移值。Q「我要报名」点击无效弹窗没出现A坐标与你的分辨率不符按第五节重新标定坐标。Q报名成功数一直增加但实际没中A脚本只负责完成点击报名流程中奖与否由平台抽签决定成功报名不等于中奖。八、总结本脚本的设计思路文字定位解决列表页按钮位置不固定的问题找到就点、找不到就滑自适应任意滚动位置绝对坐标处理固定布局的详情页和弹窗简单直接响应准确双线程 ui.run()保证 UI 流畅不卡顿日志实时更新try-catch 限速 sleep提高稳定性单轮出错不崩溃节奏不过快避免风控整体代码约 200 行结构清晰改动配置区的坐标和阈值即可适配不同设备。有问题欢迎评论区交流