//定义悬浮窗控制模块,命名为(悬块)。
var 悬块 = 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.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宽高等属性。
<button id="but" w="300px" h="150px" text="开始"/>
);
//空运行定时器保持脚本运行中,这是悬浮窗脚本所必需的。
setInterval(() => {}, 500);
//创建一个新的悬浮控制模块 ad 并带入参数(所要控制的悬浮窗和用来控制悬浮窗移动的控件)。
var ad = new 悬块(window, window.but);
//设置长按事件。
ad.setLongClick(function() {
//输出气泡信息。
toast("脚本已关闭");
//脚本停止代码。
exit();
});
//设置点击事件。
ad.setClick(function() {
//输出气泡信息。
toast("点击");
主程序()
});

为什么我点击运行整个程序直接闪退了

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

17:23:46.767/E: TypeError: 对象 [object Object] 不存在方法 setLongClick. (684854109a98757b5effd14d.js#65)
TypeError: 对象 [object Object] 不存在方法 setLongClick.
at 684854109a98757b5effd14d.js:65:0

第 2 条补充
·
2 个月前

可以使用了,但是点击悬浮按钮启动会直接闪退

回答

Hamibot AI
1
· 2 个月前

在你的代码中,导致闪退的主要原因是使用了 Rhino 不兼容的语法和错误的 OnTouchListener 创建方式。以下是具体的修正方法:

修正点 1:替换箭头函数为传统函数

Rhino 引擎不支持箭头函数,需将其替换为 function 形式,确保上下文正确。

修正点 2:正确创建 OnTouchListener

使用 android.view.View.OnTouchListener 时,需通过 new 和函数表达式创建,而非箭头函数。

修正点 3:保持 this 上下文

在事件处理中,this 可能指向错误的上下文,需通过闭包保留 self

修正后的代码如下:

//文档地址 https://docs.hamibot.com/
var 悬块 = function(window, view) {
    if (!window || !view) {
        throw "缺参数";
    };
    this.x = 0;
    this.y = 0;
    this.windowX = 0;
    this.windowY = 0;
    this.downTime = 500;
    this.Timeout = 0;
    this.Click = function() {};
    this.LongClick = function() {};

    var self = this; // 保留 this 上下文

    view.setOnTouchListener(new android.view.View.OnTouchListener(function(view, event) {
        switch (event.getAction()) {
            case event.ACTION_DOWN:
                self.x = event.getRawX();
                self.y = event.getRawY();
                self.windowX = window.getX();
                self.windowY = window.getY();
                self.Timeout = setTimeout(function() {
                    self.LongClick();
                    self.Timeout = 0;
                }, self.downTime);
                return true;
            case event.ACTION_MOVE:
                if (Math.abs(event.getRawY() - self.y) > 5 && Math.abs(event.getRawX() - self.x) > 5) {
                    if (self.Timeout) {
                        clearTimeout(self.Timeout);
                        self.Timeout = 0;
                    }
                    window.setPosition(self.windowX + (event.getRawX() - self.x), self.windowY + (event.getRawY() - self.y));
                }
                return true;
            case event.ACTION_UP:
                if (self.Timeout) {
                    clearTimeout(self.Timeout);
                    self.Timeout = 0;
                    self.Click();
                }
                return true;
        }
        return true;
    }));
};

var window = floaty.window(
    <frame>
        <text id="but" bg="#FF0000" w="300px" h="150px" gravity="center">开始</text>
    </frame>
);

setInterval(function() {}, 1000);

var ad = new 悬块(window, window.but);
ad.setLongClick(function() {
    toast("脚本已关闭");
    exit();
});
ad.setClick(function() {
    toast("点击");
    主程序();
});

function 主程序() {
    // 这里添加你的主程序逻辑
    toast("主程序运行");
}

关键改动说明

  1. 替换箭头函数:将所有箭头函数 () => {} 改为 function() {}
  2. 保留 this 上下文:在悬块构造函数中用 var self = this; 保持上下文。
  3. 使用 <frame> 包裹控件:确保 XML 布局结构正确,避免标签解析错误。
  4. 添加 主程序 函数:确保点击事件触发时有实际函数调用。

通过以上调整,脚本应能正常运行并响应点击与长按事件。


补充回答 1

你的错误是因为 ad 对象没有正确绑定 setLongClick 方法。问题出在 构造函数中对 this.LongClick 的定义方式 以及 事件处理函数的上下文丢失。以下是完整的修正方案:


✅ 核心问题分析

  1. this.LongClick 未绑定到实例
    this.LongClick = function() {} 会导致每个实例拥有独立的方法副本,但更严重的问题在于事件处理函数中的 this.Timeoutthis.LongClick 上下文丢失。

  2. OnTouchListenerthis 指向错误
    事件处理函数内的 this 不指向 悬块 实例,而是指向 Android 的 View 或其他上下文,导致无法访问 this.Timeoutthis.LongClick

  3. setLongClick 方法未正确绑定到实例
    构造函数中通过 this.setLongClick = function(fun, ji) 定义方法时,未使用 prototype,而事件处理函数中又无法访问 this.setLongClick


✅ 修正后的完整代码

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

var 悬块 = function(window, view) {
    if (!window || !view) {
        throw "缺参数";
    }

    var self = this; // 保留 this 上下文

    this.x = 0;
    this.y = 0;
    this.windowX = 0;
    this.windowY = 0;
    this.downTime = 500;
    this.Timeout = 0;
    this.Click = function() {};
    this.LongClick = function() {};

    // 定义 setClick 和 setLongClick 为实例方法
    this.setClick = function(fun) {
        if (typeof fun === "function") {
            self.Click = fun;
        }
    };

    this.setLongClick = function(fun, ji) {
        if (typeof fun === "function") {
            self.LongClick = fun;
            if (parseInt(ji) <= 1000) {
                self.downTime = parseInt(ji);
            }
        }
    };

    // 使用闭包绑定 this 上下文
    view.setOnTouchListener(new android.view.View.OnTouchListener(function(view, event) {
        switch (event.getAction()) {
            case event.ACTION_DOWN:
                self.x = event.getRawX();
                self.y = event.getRawY();
                self.windowX = window.getX();
                self.windowY = window.getY();
                self.Timeout = setTimeout(function() {
                    self.LongClick();
                    self.Timeout = 0;
                }, self.downTime);
                return true;

            case event.ACTION_MOVE:
                if (Math.abs(event.getRawY() - self.y) > 5 && Math.abs(event.getRawX() - self.x) > 5) {
                    if (self.Timeout) {
                        clearTimeout(self.Timeout);
                        self.Timeout = 0;
                    }
                    window.setPosition(self.windowX + (event.getRawX() - self.x), self.windowY + (event.getRawY() - self.y));
                }
                return true;

            case event.ACTION_UP:
                if (self.Timeout) {
                    clearTimeout(self.Timeout);
                    self.Timeout = 0;
                    self.Click();
                }
                return true;
        }
        return true;
    }));
};

var window = floaty.window(
    <frame>
        <text id="but" bg="#FF0000" w="300px" h="150px" gravity="center">开始</text>
    </frame>
);

setInterval(function() {}, 1000);

var ad = new 悬块(window, window.but);

// 确保方法正确绑定后再调用
if (ad && typeof ad.setLongClick === "function") {
    ad.setLongClick(function() {
        toast("脚本已关闭");
        exit();
    }, 1000); // 第二个参数可选,设置长按时间
} else {
    toast("悬块初始化失败,请检查构造参数");
}

if (ad && typeof ad.setClick === "function") {
    ad.setClick(function() {
        toast("点击");
        主程序();
    });
} else {
    toast("悬块初始化失败,请检查构造参数");
}

function 主程序() {
    toast("主程序运行");
}

✅ 关键改动说明

改动点 说明
self = this 替代 this 防止事件处理函数中 this 丢失,确保上下文正确
setLongClick 绑定到 self 保证在事件处理中可以访问到该方法
**

补充回答 2

根据你的描述,点击悬浮按钮启动脚本时闪退,可能涉及以下原因:


关键问题分析

  1. 主程序逻辑异常
    • 如果 主程序() 中包含未处理的异常(如未定义函数、空引用、非法操作等),会导致脚本崩溃。
  2. 悬浮窗控件类型不兼容
    • 若按钮使用 <text> 等非交互控件,可能导致点击事件绑定失败,间接触发逻辑错误。
  3. 线程阻塞或资源占用
    • 主程序中执行耗时操作未使用 threads.start(),可能导致 UI 线程阻塞,触发 ANR(无响应错误)。
  4. 对象释放或状态异常
    • 悬浮窗或相关对象被提前关闭或释放,后续操作触发空指针异常。

解决方案

1. 确保主程序逻辑安全

  • 主程序() 中添加异常捕获,防止崩溃。
function 主程序() {
    try {
        toast("主程序运行");
        // 你的业务逻辑
    } catch (e) {
        toast("主程序异常: " + e);
    }
}

2. 使用标准按钮控件

  • 替换 <text><button>,确保点击事件正常触发:
var window = floaty.window(
    <frame>
        <button id="but" bg="#FF0000" w="300px" h="150px" text="开始" />
    </frame>
);

3. 检查悬浮窗生命周期

  • 确保在点击事件中未提前关闭悬浮窗(如调用 window.close())。
  • 若需关闭悬浮窗,应在退出前移除所有事件监听器。

4. 异步执行耗时任务

  • 主程序中涉及循环、网络请求、文件读写等操作时,必须放入子线程:
function 主程序() {
    threads.start(function () {
        // 耗时操作放在这里
        sleep(1000); // 示例:模拟耗时操作
        toast("操作完成");
    });
}

5. 调试日志定位崩溃点

  • 在关键位置添加日志,确定崩溃发生的位置:
ad.setClick(function() {
    log("点击事件触发");
    主程序();
});

修正后的完整代码示例

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

var 悬块 = function(window, view) {
    var self = this;

    this.x = 0;
    this.y = 0;
    this.windowX = 0;
    this.windowY = 0;
    this.downTime = 500;
    this.Timeout = 0;
    this.Click = function() {};
    this.LongClick = function() {};

    this.setClick = function(fun) {
        if (typeof fun === "function") {
            self.Click = fun;
        }
    };

    this.setLongClick = function(fun, ji) {
        if (typeof fun === "function") {
            self.LongClick = fun;
            if (parseInt(ji) <= 1000) {
                self.downTime = parseInt(ji);
            }
        }
    };

    view.setOnTouchListener(new android.view.View.OnTouchListener(function(view, event) {
        switch (event.getAction()) {
            case event.ACTION_DOWN:
                self.x = event.getRawX();
                self.y = event.getRawY();
                self.windowX = window.getX();
                self.windowY = window.getY();
                self.Timeout = setTimeout(function() {
                    self.LongClick();
                    self.Timeout = 0;
                }, self.downTime);
                return true;

            case event.ACTION_MOVE:
                if (Math.abs(event.getRawY() - self.y) > 5 && Math.abs(event.getRawX() - self.x) > 5) {
                    if (self.Timeout) {
                        clearTimeout(self.Timeout);
                        self.Timeout = 0;
                    }
                    window.setPosition(self.windowX + (event.getRawX() - self.x), self.windowY + (event.getRawY() - self.y));
                }
                return true;

            case event.ACTION_UP:
                if (self.Timeout) {
                    clearTimeout(self.Timeout);
                    self.Timeout = 0;
                    self.Click();
                }
                return true;
        }
        return true;
    }));
};

var window = floaty.window(
    <frame>
        <button id="but" bg="#FF0000" w="300px" h="150px" text="开始" />
ℹ️ 本回答由 AI 生成,内容仅供参考