ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

按键精灵手机端开发安卓版实战:手写一个可最小化、可拖动的「运行日志悬浮窗」(附完整 源码 + 踩坑记录)

2026/10/1 15:34:07 拓冰建站 浏览量
按键精灵手机端开发安卓版实战:手写一个可最小化、可拖动的「运行日志悬浮窗」(附完整 源码 + 踩坑记录) 写过安卓脚本的人应该都有过这种体验脚本跑在手机上你在电脑这头盯着按键精灵助手的调试面板看TracePrint的输出。一旦拔掉数据线、或者换成手机上直接跑日志就彻底看不见了。脚本跑飞了你也不知道飞在哪一步、卡在哪个循环、找色失败了多少次。常见的三种日志方案对比方案实时性需要连电脑需要看文件适合场景TracePrint调试面板实时✅ 必须❌开发调试阶段Log.Open写文件事后❌✅ 要用文件管理器翻长期运行记录悬浮窗日志本文实时❌❌真机跑脚本、无人值守悬浮窗日志最大的好处是脚本在手机上自己跑日志就浮在屏幕上随时能看嫌挡视线就点一下最小化缩成一个小图标。先看效果与设计拆解效果屏幕左上角一个半透明深色浮窗绿色字体滚动输出运行日志顶部有标题栏和「—」最小化按钮按住浮窗任意位置可以拖动防止挡住脚本操作点「—」后缩成右下角一个 54×54 的小方块点一下还原。结构拆解整个界面其实就是两个悬浮窗 若干控件日志浮窗 (240×320) ├── 标题栏 文字控件 / 深灰底 / 左右内缩 5px ├── 标题文字 文字控件 / 白色 运行日志 ├── btn_min 按钮控件 / 右上角 — → 触发 LogMinimize └── 日志内容 文字控件 / 亮绿 / 实时刷新 图标浮窗 (54×54) ← 最小化后显示 └── 图标文字 文字控件 / 日志 整体点击 → 触发 LogRestore这里有个很关键的设计选择最小化不是隐藏控件而是两个独立浮窗互相显示/隐藏。原因后面踩坑部分会讲。完整源码可直接复制运行下面这份代码可以直接贴进按键精灵移动版把FindColor那段换成你自己的业务逻辑即可。 文件扩展名保存为.mq。// // 按键精灵移动版MQ / Q语法 // 功能可拖动、可最小化的【运行日志悬浮窗】 // 环境安卓 root / 无障碍FW 系列命令最低客户端 3.3.8无障碍 5.0.0 // ---------------------------------------------------------------- // ⚠️ 核心约束FW 的 SetOn* 事件函数内部【无法访问外部变量】 // 因此 LogMinimize / LogRestore 里的浮窗名必须写成字符串字面量 // 不能引用 FW_LOG、FW_ICON 这些全局变量。 // // ---------------- 全局配置仅在主流程与普通函数中使用 ---------------- Dim FW_LOG 日志浮窗 主日志浮窗名 Dim FW_ICON 图标浮窗 最小化后的小图标浮窗名 Dim TV_LOG 日志内容 日志正文控件名 Dim LBL_ICON 图标文字 小图标上的文字控件名 Dim LOG_X 10, LOG_Y 60 主浮窗初始坐标 Dim ICO_X 0, ICO_Y 300 图标浮窗初始坐标 Dim LOG_W 240, LOG_H 320 主浮窗宽高 Dim ICO_W 54, ICO_H 54 图标浮窗宽高 Dim LOG_MAX 12 日志窗最多保留的行数 Dim LOG_BUF 日志文本缓冲 Dim NL Chr(10) 换行符若你的版本不换行改成 Chr(13) Chr(10) // // 一、初始化创建两个浮窗全脚本只调用一次 // Function LogInit() ---------- 1. 主日志浮窗 ---------- FW.NewFWindow FW_LOG, LOG_X, LOG_Y, LOG_W, LOG_H FW.SetBackColor FW_LOG, 3A3A3A 16进制颜色格式为 BBGGRR FW.Opacity FW_LOG, 85 整体不透明度0 全透明 ~ 100 不透明 FW.IsTouchMove FW_LOG, 1 允许整窗拖动 FW.SetSmoothing FW_LOG, 5 圆角 标题栏左右各内缩 5px绕开浮窗圆角避免方形控件把圆角盖成直角 FW.AddTextView FW_LOG, 标题栏, , 5, 5, LOG_W - 10, 24 FW.SetBackColor 标题栏, 2B2B2B FW.AddTextView FW_LOG, 标题文字, 运行日志, 12, 8, LOG_W - 60, 20 FW.SetTextColor 标题文字, FFFFFF FW.SetTextSize 标题文字, 10 最小化按钮右上角 FW.AddButton FW_LOG, btn_min, —, LOG_W - 30, 8, 22, 20 FW.SetBackColor btn_min, 2B2B2B FW.SetTextColor btn_min, FFFFFF FW.SetOnClick btn_min, LogMinimize 日志正文 FW.AddTextView FW_LOG, TV_LOG, , 10, 36, LOG_W - 20, LOG_H - 46 FW.SetTextColor TV_LOG, 0FE7A2 BBGGRR → RGB(162,231,15) 亮绿 FW.SetTextSize TV_LOG, 10 FW.Show FW_LOG ---------- 2. 小图标浮窗点击还原日志 ---------- FW.NewFWindow FW_ICON, ICO_X, ICO_Y, ICO_W, ICO_H FW.SetBackColor FW_ICON, 3A3A3A FW.SetSmoothing FW_ICON, 5 FW.IsTouchMove FW_ICON, 1 关键点击事件绑在【悬浮窗本身】不再额外套一个按钮 否则按钮会吃掉触摸事件导致能拖动浮窗、拖不动按钮 FW.SetOnClick FW_ICON, LogRestore FW.AddTextView FW_ICON, LBL_ICON, 日志, 0, 0, ICO_W, ICO_H FW.SetTextColor LBL_ICON, 0FE7A2 FW.SetTextSize LBL_ICON, 10 End Function // // 二、最小化 / 还原悬浮窗事件函数 // ⚠️ 事件函数内读不到外部变量所以这里的名字只能写字面量 // Function LogMinimize() FW.Hide 日志浮窗 隐藏大窗 FW.Show 图标浮窗 显示小图标 End Function Function LogRestore() FW.Hide 图标浮窗 隐藏小图标 FW.Show 日志浮窗 恢复大窗 End Function // // 三、写日志核心普通函数可以正常使用全局变量 // 参数text —— 一行日志内容 // 说明自动加时间戳超过 LOG_MAX 行自动丢弃最旧的模拟滚动效果 // Function LogAdd(text) LOG_BUF LOG_BUF [ LogStamp() ] text NL LOG_BUF LogKeepLast(LOG_BUF, LOG_MAX) FW.SetText TV_LOG, LOG_BUF FW.SetText 需要客户端 3.5.1 及以上 End Function 只保留最后 n 行 Function LogKeepLast(s, n) Dim arr Split(s, NL) Dim total Len(arr) Dim i, out If total n Then LogKeepLast s Exit Function End If out For i total - n 1 To total out out arr[i] NL Next LogKeepLast out End Function 时:分:秒由设备系统时间戳换算用作日志前缀 Function LogStamp() Dim sec Time() Mod 86400 LogStamp Pad0(sec \ 3600) : Pad0((sec Mod 3600) \ 60) : Pad0(sec Mod 60) End Function 个位数补零 Function Pad0(v) If v 10 Then Pad0 0 v Else Pad0 CStr(v) End If End Function // // 四、主流程演示循环找色把每次结果实时写进日志悬浮窗 // LogInit LogAdd 脚本启动开始监听目标颜色 Dim intX, intY, count 0 Do count count 1 0,0,0,0 表示全屏范围实际使用时换成你自己的目标颜色与容差 FindColor 0, 0, 0, 0, FFFFFF, 0, 0.9, intX, intY If intX -1 Then LogAdd 第 count 次找到目标 ( intX , intY )执行点击 Tap intX, intY Else LogAdd 第 count 次未找到目标1 秒后重试 End If Delay 1000 Loop 脚本停止后悬浮窗会自动消失无需手动清理核心代码逐段拆解1. 创建浮窗FW.NewFWindow参数类型说明fwNamestring悬浮窗名称全局唯一leftX / topYnumber屏幕坐标默认 0width / heightnumber宽高省略则自适应内容两个必须知道的点一个脚本最多创建 32 个悬浮窗脚本停止后浮窗会自动消失不用手动清理。2. 添加子控件注意leftX / topY是相对于父控件这里是浮窗左上角的坐标不是屏幕绝对坐标。整个浮窗内部就是一个简易的相对布局系统。3. 外观设置目的命令说明浮窗背景色FW.SetBackColor(名称, 3A3A3A)16 进制格式BBGGRR整体不透明度FW.Opacity(名称, 85)0 全透明 ~ 100 不透明圆角FW.SetSmoothing(名称, 5)数值越大越圆允许拖动FW.IsTouchMove(名称, 1)1 允许 / 0 禁止文字颜色 / 字号FW.SetTextColor/FW.SetTextSize按控件设置运行时改文字FW.SetText(eleName, text)就是靠它刷新日志4. 写日志的核心LogAdd原文的浮窗里塞的是一段固定文字看着像日志其实不会变。真正的日志系统必须能动态追加靠的就是FW.SetText三个细节加时间戳方便定位卡在哪一秒只保留最后 N 行文字控件高度是固定的行数超了不会自动滚动手动裁掉旧行比什么都简单可靠用字符串拼接缓冲不要用 table 去做队列——按键的 table 是可以t[Len(t)1] v追加但每次都要重排、转字符串不如直接拼字符串来得直观。业务代码里只要这样调用If intX -1 Then LogAdd 第 count 次找到目标 ( intX , intY ) Else LogAdd 第 count 次未找到目标1 秒后重试 End If【日志窗里滚动的效果能看到时间戳前缀】踩坑记录这部分最重要坑 1最隐蔽悬浮窗事件函数里读不到全局变量这是这次最大的收获。MQ 的官方语法说明里明确写着在悬浮窗控件的事件函数如SetOnClick这类以SetOn开头的事件中无法访问外部变量只能使用事件函数内部的局部变量。也就是说你下面这么写是无效的Dim FW_LOG 日志浮窗 Function LogMinimize() FW.Hide FW_LOG ❌ FW_LOG 在这里是 null浮窗名传进去是空的 End Function必须写成字面量Function LogMinimize() FW.Hide 日志浮窗 ✅ 直接写名字 FW.Show 图标浮窗 End Function所以浮窗名、控件名建议一次定好之后当成常量用字符串直接写。这也解释了为什么很多示例代码里事件函数中的名字都是硬编码的——不是作者懒得抽变量是这套机制不允许。顺带一提普通函数比如上面的LogAdd不受这个限制全局变量照常用。受限的只有FW.SetOn*绑定的事件函数。坑 2顶部标题栏会把浮窗的圆角切成直角现象浮窗设了FW.SetSmoothing但顶部那条深色标题栏是方形的直接把上面两个圆角盖住了。原因很简单子控件永远是矩形绘制在浮窗之上不会继承浮窗的圆角裁剪。只要控件贴着左右边缘且铺满颜色圆角就一定被遮。两个解决思路方案 A本文用的标题栏不贴边左右各内缩 5pxleftX 5, width 浮窗宽 - 10同时把标题栏和浮窗的底色设成很接近的深灰2B2B2B/3A3A3A视觉上几乎看不出那 5px 的接缝方案 B干脆不给标题栏设背景色标题只用一个文字控件靠浮窗自身的背景色撑住。如果非要完美圆角 实心标题栏那得靠两张带圆角的图片去贴成本不划算。坑 3小图标上的按钮点得动、却拖不动现象主浮窗能随便拖但最小化后那个小图标怎么拖都不动。原因FW.IsTouchMove是作用在悬浮窗这一层的而你在浮窗里放了一个铺满的按钮触摸落在按钮上时事件被子控件消费掉了浮窗根本收不到拖动。修复小图标不要套按钮直接把点击事件绑在悬浮窗本身上。FW.SetOnClick的第一个参数支持悬浮窗或控件名称所以这样写是合法的FW.NewFWindow 图标浮窗, 0, 300, 54, 54 FW.IsTouchMove 图标浮窗, 1 整窗可拖 FW.SetOnClick 图标浮窗, LogRestore 整窗可点这样拖动和点击都在同一层级不会互相吞事件。如果实测发现你的版本上整窗点击没能触发退一步的稳妥做法是保留按钮但别让它铺满浮窗做成 70×70、按钮只占中间 46×46留出一圈可拖动的边缘。坑 4颜色是BBGGRR不是RRGGBB这条坑过无数人。FW 系列命令里的颜色字符串格式是BBGGRR和按键精灵取色命令一致不是网页/CSS 里的RRGGBB。对照一下就明白了颜色FW 里应写说明红色0000FFB00, G00, RFF纯绿00FF00这次 GFF白色FFFFFF顺序无所谓深灰3A3A3A顺序无所谓本文的日志绿0FE7A2B0F, GE7, RA2 → 实际显示为亮绿所以灰色系随便写彩色一定要换算。你从取色器拿到的RRGGBB要反过来写成BBGGRR才会显示成你想要的颜色。坑 5FW.Hide和FW.Close不是一回事FW.Hide——隐藏之后FW.Show还能显示回来最小化要用这个FW.Close——关闭并销毁销毁后的浮窗不能再FW.Show必须重新FW.NewFWindow。很多人做最小化用错了Close结果点还原直接没反应。这套悬浮窗日志我现在的用法是把它当成**脚本的仪表盘**∶循环次数、找色成功/失败、超时重试、当前状态全部实时打上去。脚本在手机上一跑几小时出了什么问题扫一眼浮窗就知道比事后翻日志文件快得多。代码里两个关键点再强调一遍别的都可以忘:│1.**Fw的‘Seton*’事件函数读不到全局变量**—浮窗名—律写字面量;2**颜色是BBGGRR’不是‘RRGGBB~**——彩色必须换算。如果这篇对你有帮助点个赞收藏一下。有任何关于按键精灵移动端开发安卓版的问题或者想看悬浮窗日志多线程的做法欢迎评论区交流。