var xuanfuanniu = function(window, view) {
//判断是否缺少构造参数。
if (!window || !view) {
//缺少构造参数,抛出错误。
throw "缺参数";
};
//记录按键被按下时的触摸坐标
this.x = 0, this.y = 0;
//记录按键被按下时的悬浮窗位置
this.windowX, this.windowY;
//按下时长超过此值则执行长按等动作
this.downTime = 500;
//记录定时执行器的返回id
this.Timeout = 0;
//添加状态变量,用于记录是否已启动
this.isRunning = false;
//创建点击长按事件
this.Click = function() {};
this.LongClick = function() {};
//可修改点击长按事件
this.setClick = function(fun) {
//判断参数类型是否为函数?
if (typeof fun == "function") {
this.Click = fun;
};
};
this.setLongClick = function(fun, ji) {
//判断参数类型是否为函数?
if (typeof fun == "function") {
this.LongClick = fun;
//判断参数是否可为设置数字?
if (parseInt(ji) <= 1000) {
this.downTime = parseInt(ji);
};
};
};
view.setOnTouchListener(new android.view.View.OnTouchListener((view, event) => {
//判断当前触控事件,以便执行操作。
switch (event.getAction()) {
//按下事件。
case event.ACTION_DOWN:
//按下记录各种坐标数据。
this.x = event.getRawX();
this.y = event.getRawY();
this.windowX = window.getX();
this.windowY = window.getY();
//创建一个定时器用来定时执行长按操作。
this.Timeout = setTimeout(() => {
this.LongClick();
this.Timeout = 0;
}, this.downTime);
return true;
//移动事件。
case event.ACTION_MOVE:
//移动距离过大则判断为移动状态
if (Math.abs(event.getRawY() - this.y) > 5 && Math.abs(event.getRawX() - this.x) > 5) {
//移动状态清除定时器
if (this.Timeout) {
//定时器存在则清除定时器。
clearTimeout(this.Timeout);
this.Timeout = 0;
};
//移动手指时调整悬浮窗位置
window.setPosition(this.windowX + (event.getRawX() - this.x), this.windowY + (event.getRawY() - this.y));
};
return true;
//抬起事件。
case event.ACTION_UP:
if (this.Timeout) {
//手指抬起时,定时器存在,说明没有移动和按下时间小于长按时间。
//清除定时器。
clearTimeout(this.Timeout);
this.Timeout = 0;
//执行点击事件。
this.Click();
};
return true;
};
//控件的触控事件函数必须要返回true。否则报错。
return true;
}));
};
//创建并生成一个悬浮窗。
var window = floaty.window(
//创建一个按钮,并设置其id宽高等属性。
<frame>
<com.google.android.material.floatingactionbutton.FloatingActionButton
layout_width="wrap_content"
backgroundTint="#000000"
tint="#FFFFFFFF"
id="but"
layout_height="wrap_content"
src="@drawable/ic_play_arrow_black_48dp"
/>
</frame>
);
//空运行定时器保持脚本运行中,这是悬浮窗脚本所必需的。
setInterval(() => {}, 500);
//创建一个新的悬浮控制模块 ad 并带入参数(所要控制的悬浮窗和用来控制悬浮窗移动的控件)。
var ad = new xuanfuanniu(window, window.but);
//设置长按事件 - 长按退出
ad.setLongClick(function() {
//输出气泡信息。
toast("脚本已退出");
//脚本停止代码。
exit();
});
//设置点击事件 - 点击切换启动/停止状态
ad.setClick(function() {
if (ad.isRunning) {
// 如果正在运行,则停止
toast("脚本已停止");
ad.isRunning = false;
// 这里可以添加你的停止逻辑
} else {
// 否则启动
ad.isRunning = true;
// 这里可以添加你的启动逻辑
主程序()
}
});
function 主程序() {
toast("脚本已启动");
}
我这段代码正常打印log是没问题的,但是一有节点识别的代码在里面点击启动就会闪退,这是什么原因
能否再帮我修改一下按钮样式,启动前是文本“启动”,启动后是文本“停止”
回答
文档地址 https://docs.hamibot.com/
出现闪退的主要原因可能涉及以下三点:
- 线程安全问题:
ad.setClick(function() {
if (ad.isRunning) {
// 错误示范(直接操作UI)
toast("脚本已停止");
ad.isRunning = false;
} else {
// 正确处理方式
ui.run(() => {
toast("脚本已启动");
ad.isRunning = true;
主程序();
});
}
});
- 选择器使用规范:
function 主程序() {
// 添加异常捕获
try {
// 节点查找必须验证存在性
let node = text("目标文本").findOne(1000);
if(node) {
log("找到节点:" + node.text());
// 执行点击/滑动等操作
} else {
log("未找到目标节点");
}
} catch(e) {
log("错误信息:" + e.message);
}
}
- 上下文依赖问题:
// 在悬浮窗事件回调中需要显式绑定上下文
view.setOnTouchListener(new android.view.View.OnTouchListener({
onTouch: function(view, event) {
// 所有涉及context的操作使用Java.context
let packageName = Java.context.getPackageName();
log("当前包名:" + packageName);
return true;
}
}));
建议修改方案:
function 主程序() {
// 使用异步处理防止阻塞UI线程
threads.start(function() {
try {
// 增加超时保护
let node = text("目标文本").findOne(3000);
if(node) {
// 所有UI反馈通过ui.run
ui.run(() => {
toast("找到节点:" + node.text());
});
// 执行具体操作...
}
} catch(e) {
// 捕获详细错误信息
log("运行错误:" + e.message + "\n堆栈:" + e.stack);
ui.run(() => {
toast("发生错误:" + e.message);
});
}
});
}
常见故障点排查:
- 确保所有涉及控件操作的代码都通过
ui.run()
执行 - 检查
auto.waitFor()
是否在脚本开头正确声明 - 使用
try-catch
包裹所有可能失败的节点操作 - 在高版本Android设备上检查悬浮窗权限是否完全开启
- 查看
hamibot://require?path=...
协议调用的模块是否存在兼容性问题
补充回答 1
文档地址 https://docs.hamibot.com/
以下是实现按钮状态切换样式的完整修改方案:
// 创建悬浮窗时使用包含文本的按钮布局
var window = floaty.window(
<frame>
<button
id="but"
text="启动"
textSize="14sp"
textColor="#FFFFFF"
backgroundTint="#000000"
padding="8dp"
layout_width="wrap_content"
layout_height="wrap_content"
/>
</frame>
);
// 在构造函数后添加状态样式管理器
var ad = new xuanfuanniu(window, window.but);
// 修改点击事件处理
ad.setClick(function() {
ui.run(() => {
if (ad.isRunning) {
// 停止状态样式
window.but.setText("启动");
window.but.setBackgroundColor("#000000");
toast("脚本已停止");
ad.isRunning = false;
} else {
// 启动状态样式
window.but.setText("停止");
window.but.setBackgroundColor("#FF4040");
toast("脚本已启动");
ad.isRunning = true;
主程序();
}
}
});
样式修改要点说明:
- 按钮组件升级:
- 使用原生
<button>
组件替代FloatingActionButton - 添加了
textSize
、textColor
等文本样式属性 - 使用
backgroundTint
控制背景颜色
- 状态同步机制:
// 在主程序中保持状态同步
function 主程序() {
threads.start(function() {
try {
// 模拟执行任务
while(ad.isRunning) {
// 执行节点操作...
sleep(1000);
}
} catch(e) {
log("任务异常:" + e.message);
}
});
}
- 完整样式参数示例:
<button
id="but"
text="启动"
textSize="16sp"
textColor="#FFFFFF"
fontFamily="sans-serif-medium"
letterSpacing="0.1"
backgroundTint="#000000"
rippleColor="#44FFFFFF"
elevation="4dp"
padding="12dp"
layout_width="wrap_content"
layout_height="wrap_content"
gravity="center"
/>
注意事项:
- 所有UI更新必须通过
ui.run()
执行 - 颜色值建议使用十六进制格式(#FF4040)
- 布局参数保持
wrap_content
保证自适应 - 可通过修改
rippleColor
添加点击波纹效果 - 使用
elevation
属性提升悬浮层级视觉效果
扩展样式建议:
- 添加图标支持:
drawableLeft="@drawable/ic_play_arrow_white_24dp"
- 实现圆角矩形:
backgroundRadius="8dp"
- 添加阴影效果:
shadowColor="#888888"