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是没问题的,但是一有节点识别的代码在里面点击启动就会闪退,这是什么原因

已存档
LiGo · 2 个月前
第 1 条补充
·
2 个月前

能否再帮我修改一下按钮样式,启动前是文本“启动”,启动后是文本“停止”

回答

Hamibot AI
1
· 2 个月前

文档地址 https://docs.hamibot.com/

出现闪退的主要原因可能涉及以下三点:

  1. 线程安全问题
ad.setClick(function() {
  if (ad.isRunning) {
    // 错误示范(直接操作UI)
    toast("脚本已停止");
    ad.isRunning = false;
  } else {
    // 正确处理方式
    ui.run(() => {
      toast("脚本已启动");
      ad.isRunning = true;
      主程序();
    });
  }
});
  1. 选择器使用规范
function 主程序() {
  // 添加异常捕获
  try {
    // 节点查找必须验证存在性
    let node = text("目标文本").findOne(1000);
    if(node) {
      log("找到节点:" + node.text());
      // 执行点击/滑动等操作
    } else {
      log("未找到目标节点");
    }
  } catch(e) {
    log("错误信息:" + e.message);
  }
}
  1. 上下文依赖问题
// 在悬浮窗事件回调中需要显式绑定上下文
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);
      });
    }
  });
}

常见故障点排查:

  1. 确保所有涉及控件操作的代码都通过ui.run()执行
  2. 检查auto.waitFor()是否在脚本开头正确声明
  3. 使用try-catch包裹所有可能失败的节点操作
  4. 在高版本Android设备上检查悬浮窗权限是否完全开启
  5. 查看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;
      主程序();
    }
  }
});

样式修改要点说明

  1. 按钮组件升级
  • 使用原生<button>组件替代FloatingActionButton
  • 添加了textSizetextColor等文本样式属性
  • 使用backgroundTint控制背景颜色
  1. 状态同步机制
// 在主程序中保持状态同步
function 主程序() {
  threads.start(function() {
    try {
      // 模拟执行任务
      while(ad.isRunning) {
        // 执行节点操作...
        sleep(1000);
      }
    } catch(e) {
      log("任务异常:" + e.message);
    }
  });
}
  1. 完整样式参数示例
<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"
/>

注意事项

  1. 所有UI更新必须通过ui.run()执行
  2. 颜色值建议使用十六进制格式(#FF4040)
  3. 布局参数保持wrap_content保证自适应
  4. 可通过修改rippleColor添加点击波纹效果
  5. 使用elevation属性提升悬浮层级视觉效果

扩展样式建议

  • 添加图标支持:drawableLeft="@drawable/ic_play_arrow_white_24dp"
  • 实现圆角矩形:backgroundRadius="8dp"
  • 添加阴影效果:shadowColor="#888888"
ℹ️ 本回答由 AI 生成,内容仅供参考