// 非UI模式,直接运行
let isEditing = false;
let param1 = "", param2 = "", param3 = "";
// 创建悬浮窗
let w = floaty.window(
<vertical bg="#ffffff" padding="10" corner="8" w="180">
<horizontal>
<button id="btn_setting" text="设置" w="80" h="40" margin="2"/>
<button id="btn_run" text="运行" w="80" h="40" margin="2"/>
</horizontal>
<input id="input1" hint="参数1" w="160" margin="2" visibility="gone"/>
<input id="input2" hint="参数2" w="160" margin="2" visibility="gone"/>
<input id="input3" hint="参数3" w="160" margin="2" visibility="gone"/>
</vertical>
);
let btn_setting = w.btn_setting;
let btn_run = w.btn_run;
let input1 = w.input1;
let input2 = w.input2;
let input3 = w.input3;
// 设置 / 保存
btn_setting.click(() => {
if (!isEditing) {
input1.setVisibility(0);
input2.setVisibility(0);
input3.setVisibility(0);
btn_setting.setText("保存");
input1.setText(param1);
input2.setText(param2);
input3.setText(param3);
isEditing = true;
} else {
param1 = input1.text().trim();
param2 = input2.text().trim();
param3 = input3.text().trim();
input1.setVisibility(8);
input2.setVisibility(8);
input3.setVisibility(8);
btn_setting.setText("设置");
isEditing = false;
}
});
// 运行
btn_run.click(() => {
toast("参数1:" + param1 + "\n参数2:" + param2 + "\n参数3:" + param3);
});
// 保活
setInterval(() => {}, 1000);
这段代码为什么点击在文本输入框的时候,不出现键盘,导致无法输入?
如果我还是想在悬浮窗设置输入,有方法解决吗?有代码例子?
如何写一个悬浮窗,然后悬浮窗提供选择,选择是还是否,然后点保存,存入a值,输出a。
回答
这是 Android 系统安全机制导致的限制。悬浮窗(Overlay 窗口)出于安全考虑(防止恶意应用窃取密码),系统通常禁止其唤起软键盘,导致 <input> 控件无法获得输入焦点。
在 Hamibot 中,建议通过 dialogs.input 来获取用户输入,而不是直接在悬浮窗中放置输入框。悬浮窗更适合用于显示状态和提供操作按钮。
此外,你提供的代码中使用了箭头函数 () => {} 和 let 关键字。Hamibot 的脚本引擎基于 Rhino 1.7.15,对 ES6 新特性支持有限。为了保证兼容性,建议使用 function() {} 和 var。
以下是修改后的代码方案,使用弹窗进行参数设置,并确保语法兼容:
// 文档地址 https://docs.hamibot.com/
// 非 UI 模式,直接运行
var isEditing = false;
var param1 = "";
var param2 = "";
var param3 = "";
// 创建悬浮窗
var w = floaty.window(
<vertical bg="#ffffff" padding="10" corner="8" w="180">
<horizontal>
<button id="btn_setting" text="设置" w="80" h="40" margin="2"/>
<button id="btn_run" text="运行" w="80" h="40" margin="2"/>
</horizontal>
<text id="status_text" text="未配置参数" w="160" margin="2" gravity="center"/>
</vertical>
);
var btn_setting = w.btn_setting;
var btn_run = w.btn_run;
var status_text = w.status_text;
// 设置 / 保存
btn_setting.click(function() {
// 使用 dialogs.input 获取输入,兼容性好且能正常唤起键盘
var p1 = dialogs.input("请输入参数 1", param1);
if (p1 === null) return; // 用户取消
var p2 = dialogs.input("请输入参数 2", param2);
if (p2 === null) return;
var p3 = dialogs.input("请输入参数 3", param3);
if (p3 === null) return;
param1 = p1.trim();
param2 = p2.trim();
param3 = p3.trim();
// 更新悬浮窗显示状态
ui.run(function() {
status_text.setText("已配置参数");
});
toast("参数保存成功");
});
// 运行
btn_run.click(function() {
toast("参数 1:" + param1 + "\n参数 2:" + param2 + "\n参数 3:" + param3);
});
// 保活,防止脚本停止导致悬浮窗关闭
setInterval(function() {}, 1000);
修改要点说明:
- 移除输入框:去掉了悬浮窗布局中的
<input>控件,改为<text>显示状态,避免无法输入的问题。 - 使用
dialogs.input:点击“设置”按钮时,通过系统原生对话框获取输入,确保键盘能正常弹出。 - 语法兼容:将所有箭头函数
() => {}替换为function() {},将let替换为var,确保在 Rhino 引擎下稳定运行。 - UI 线程安全:修改悬浮窗控件内容时,使用
ui.run包裹,确保操作在 UI 线程执行。
这样既能实现参数配置功能,又能保证悬浮窗的稳定性和兼容性。
补充回答 1
在 Hamibot 的 floaty 模式下,输入框无法唤起键盘是 Android 系统的底层限制,无法通过脚本代码可靠解决。
原因说明
从 Android 8.0 开始,系统出于安全考虑(防止恶意应用通过悬浮窗窃取密码),禁止了 SYSTEM_ALERT_WINDOW 权限的窗口(即悬浮窗)唤起输入法编辑器(IME)。这意味着无论代码如何设置焦点,系统都不会显示软键盘。这不是 Hamibot 的缺陷,而是 Android 系统的安全机制。
解决方案
如果你必须让用户输入文本,正确的做法是使用 ui 模块(Activity 模式),而不是 floaty 模块。ui 模式会创建一个正常的应用界面,完全支持输入框和键盘。
以下是使用 ui 模式实现相同功能的代码示例。该脚本会显示一个正常窗口,支持输入,且兼容 Hamibot 的 Rhino 引擎。
// 文档地址 https://docs.hamibot.com/
"ui";
// 定义界面布局
ui.layout(
<vertical bg="#ffffff" padding="10">
<horizontal>
<button id="btn_setting" text="设置参数" w="*" h="50" margin="2"/>
<button id="btn_run" text="运行任务" w="*" h="50" margin="2"/>
</horizontal>
<linear>
<text text="参数 1:" w="auto" gravity="center"/>
<input id="input1" hint="请输入参数 1" w="*" margin="2"/>
</linear>
<linear>
<text text="参数 2:" w="auto" gravity="center"/>
<input id="input2" hint="请输入参数 2" w="*" margin="2"/>
</linear>
<linear>
<text text="参数 3:" w="auto" gravity="center"/>
<input id="input3" hint="请输入参数 3" w="*" margin="2"/>
</linear>
<text id="status_text" text="就绪" gravity="center" margin="10" textSize="14sp"/>
</vertical>
);
var param1 = "";
var param2 = "";
var param3 = "";
// 设置按钮点击事件
ui.btn_setting.click(function() {
// 直接获取输入框内容
param1 = ui.input1.getText().trim();
param2 = ui.input2.getText().trim();
param3 = ui.input3.getText().trim();
ui.status_text.setText("参数已保存");
toast("参数保存成功");
});
// 运行按钮点击事件
ui.btn_run.click(function() {
// 再次获取确保最新
var p1 = ui.input1.getText().trim();
var p2 = ui.input2.getText().trim();
var p3 = ui.input3.getText().trim();
toast("参数 1:" + p1 + "\n参数 2:" + p2 + "\n参数 3:" + p3);
});
// 保持窗口显示,ui 模式默认会保持运行直到退出
关键区别与注意事项
- 模式切换:代码开头必须添加
"ui";声明,这会启动 UI 模式,脚本将作为一个 Activity 运行,而不是后台脚本。 - 输入支持:
ui模式下的<input>控件可以正常唤起键盘,支持中文输入、粘贴等所有原生功能。 - 语法兼容:
- 使用
var而不是let。 - 使用
function() {}而不是箭头函数() => {}。 - 这是为了兼容 Hamibot 使用的 Rhino 1.7.15 引擎,避免脚本报错。
- 使用
- 界面形态:
ui模式是独立窗口,不能像floaty那样悬浮在其他应用之上。如果需要悬浮操作,建议采用“悬浮窗控制 + 弹窗输入”的混合模式(即第一次回答中的方案)。
总结:悬浮窗无法输入是系统铁律,涉及输入请务必使用 ui 模式。
补充回答 2
实现该功能需要使用 floaty 模块创建悬浮窗,并通过 XML 布局定义界面元素。以下是一个完整的示例脚本,实现了悬浮窗显示、选择是/否、保存并输出变量 a 的功能。
脚本代码
// 文档地址 https://docs.hamibot.com/
"ui";
var a = "";
// 创建悬浮窗布局
var w = floaty.window(
<vertical>
<text id="status" text="请选择是或否" gravity="center" textSize="16sp" margin="10"/>
<linear gravity="center">
<button id="btnYes" text="是" margin="5" w="auto"/>
<button id="btnNo" text="否" margin="5" w="auto"/>
</linear>
<button id="btnSave" text="保存" margin="10" w="auto"/>
</vertical>
);
// 设置悬浮窗位置
w.setPosition(500, 500);
// 点击“是”按钮
w.btnYes.setOnClickListener(function() {
a = "是";
ui.run(function() {
w.status.setText("已选择:是");
});
});
// 点击“否”按钮
w.btnNo.setOnClickListener(function() {
a = "否";
ui.run(function() {
w.status.setText("已选择:否");
});
});
// 点击“保存”按钮
w.btnSave.setOnClickListener(function() {
if (a === "") {
toast("请先选择是或否");
} else {
toast("保存成功,a 的值为:" + a);
log("a = " + a);
// 保存后关闭悬浮窗
w.close();
}
});
代码说明
- 界面布局:使用 XML 定义了一个垂直布局,包含状态文本、两个选择按钮(是/否)和一个保存按钮。ID 属性(如
id="btnYes")允许在代码中直接通过w.btnYes访问控件。 - 变量存储:定义全局变量
a用于存储用户的选择结果。 - 线程安全:虽然点击事件通常在 UI 线程执行,但为了确保控件修改的稳定性,代码中使用了
ui.run包裹界面更新操作,符合 Hamibot 的线程规范。 - 兼容性注意:脚本使用的是 Rhino 1.7.15 引擎,因此所有函数均使用
function()写法,未使用箭头函数=>,确保兼容旧版 JavaScript 语法。 - 输出结果:点击保存后,通过
toast在屏幕上显示结果,并通过log在控制台输出变量a的值,最后关闭悬浮窗。
使用建议
- 确保脚本拥有“悬浮窗权限”,否则窗口无法显示。
- 如果需要在脚本其他部分使用变量
a,请确保在悬浮窗关闭后再访问,以免数据未同步。 - 可以通过
w.setAdjustEnabled(false)隐藏悬浮窗自带的调整大小和位置按钮,使界面更简洁。