Floaty
floaty 模块提供了悬浮窗的相关函数,可以在屏幕上显示自定义悬浮窗,控制悬浮窗大小、位置等。
悬浮窗在脚本停止运行时会自动关闭,因此,要保持悬浮窗不被关闭,可以用一个空的 setInterval 来实现,例如:
setInterval(() => {}, 1000);
检测 Hamibot 是否获得了悬浮窗权限,可使用:
if (android.provider.Settings.canDrawOverlays(context)) {
toastLog('已获得悬浮窗权限');
} else {
toastLog('未获得悬浮窗权限');
}
floaty.window(layout)
layout
<xml> | <View> 悬浮窗界面的 XML 或者 View
指定悬浮窗的布局,创建并显示一个悬浮窗,返回一个FloatyWindow
对象。
该悬浮窗自带关闭、调整大小、调整位置按键,可根据需要调用setAdjustEnabled()
函数来显示或隐藏。
其中 layout 参数可以是 xml 布局或者一个 View,更多信息参见 ui 模块的说明。
例子:
var w = floaty.window(
<frame gravity="center" bg="#FF0000">
<text id="text">悬浮文字</text>
</frame>
);
setTimeout(() => {
w.close();
}, 5000);
这段代码运行后将会在屏幕上显示悬浮文字,并在五秒后消失。
另外,因为脚本运行的线程不是 UI 线程,而所有对控件的修改操作需要在 UI 线程执行,此时需要用ui.run
,例如:
ui.run(function () {
w.text.setText('文本');
});
有关返回的FloatyWindow
对象的说明,参见下面的FloatyWindow
章节。
floaty.rawWindow(layout)
layout
<xml> | <View> 悬浮窗界面的 XML 或者 View
指定悬浮窗的布局,创建并显示一个原始悬浮窗,返回一个FloatyRawWindow
对象。
与floaty.window()
函数不同的是,该悬浮窗不会增加任何额外设施(例如调整大小、位置按钮),您可以根据自己需要编写任何布局。
而且,该悬浮窗支持完全全屏,可以覆盖状态栏,因此可以做护眼模式之类的应用。
var w = floaty.rawWindow(
<frame gravity="center" bg="#FF0000">
<text id="text">悬浮文字</text>
</frame>
);
w.setPosition(500, 500);
setTimeout(() => {
w.close();
}, 5000);
这段代码运行后将会在屏幕上显示悬浮文字,并在五秒后消失。
有关返回的FloatyRawWindow
对象的说明,参见下面的FloatyRawWindow
章节。
floaty.closeAll()
关闭所有本脚本的悬浮窗。
FloatyWindow
悬浮窗对象,可通过FloatyWindow.{id}
获取悬浮窗界面上的元素。例如, 悬浮窗 window 上一个控件的 id 为 aaa, 那么window.aaa
即可获取到该控件,类似于 ui。
window.setAdjustEnabled(enabled)
enabled
<Boolean> 是否启用悬浮窗调整(大小、位置)
如果 enabled 为 true,则在悬浮窗左上角、右上角显示可供位置、大小调整的标示,就像控制台一样; 如果 enabled 为 false,则隐藏上述标示。
FloatyRawWindow
原始悬浮窗对象,可通过window.{id}
获取悬浮窗界面上的元素。例如, 悬浮窗 window 上一个控件的 id 为 aaa, 那么window.aaa
即可获取到该控件,类似于 ui。
window.setTouchable(touchable)
touchable
<Boolean> 是否可触摸
设置悬浮窗是否可触摸,如果为 true, 则悬浮窗将接收到触摸、点击等事件并且无法继续传递到悬浮窗下面;如果为 false, 悬浮窗上的触摸、点击等事件将被直接传递到悬浮窗下面。处于安全考虑,被悬浮窗接收的触摸事情无法再继续传递到下层。
可以用此特性来制作护眼模式脚本。
var w = floaty.rawWindow(<frame gravity="center" bg="#44ffcc00" />);
w.setSize(-1, -1);
w.setTouchable(false);
setTimeout(() => {
w.close();
}, 5000);
window.setPosition(x, y)
x
<Number> x 坐标y
<Number> y 坐标
设置悬浮窗位置。
window.getX()
返回悬浮窗位置的 X 坐标。
window.getY()
返回悬浮窗位置的 Y 坐标。
window.setSize(width, height)
width
<Number> 宽度height
<Number> 高度
设置悬浮窗宽高。
特别地,如果设置为-1,则为占满全屏;设置为-2 则为根据悬浮窗内容大小而定。例如:
var w = floaty.rawWindow(
<frame gravity="center" bg="#77ff0000">
<text id="text">悬浮文字</text>
</frame>
);
w.setSize(-1, -1);
setTimeout(() => {
w.close();
}, 5000);
window.getWidth()
返回悬浮窗宽度。
window.getHeight()
返回悬浮窗高度。
window.close()
关闭悬浮窗。如果悬浮窗已经是关闭状态,则此函数将不执行任何操作。
被关闭后的悬浮窗不能再显示。
window.exitOnClose()
使悬浮窗被关闭时自动结束脚本运行。
示例:按钮点击事件监听
更多参考 UI
// 创建悬浮窗
var floatyWin = floaty.window(
<vertical>
<button id="startBtn" text="开始" />
<button id="pauseBtn" text="暂停" />
<button id="exitBtn" text="退出" />
</vertical>
);
// 按钮点击事件监听
floatyWin.startBtn.click(function () {
toast('开始执行');
// 执行你的开始逻辑
});
floatyWin.pauseBtn.click(function () {
toast('暂停执行');
// 执行你的暂停逻辑
});
floatyWin.exitBtn.click(function () {
toast('退出脚本');
floatyWin.close();
hamibot.exit();
});
// 保持悬浮窗不被关闭
setInterval(function () {}, 1000);
示例:多个按钮共享事件
// 创建悬浮窗
var floatyWin = floaty.window(
<vertical>
<button id="startBtn" text="开始" />
<button id="pauseBtn" text="暂停" />
<button id="exitBtn" text="退出" />
</vertical>
);
function handleClick(view) {
toast(view.getText() + '被点击');
}
floatyWin.startBtn.click(handleClick);
floatyWin.pauseBtn.click(handleClick);
floatyWin.exitBtn.click(function () {
toast('退出脚本');
floatyWin.close();
hamibot.exit();
});
// 保持悬浮窗不被关闭
setInterval(function () {}, 1000);
示例:根据参数控制按钮启用
var handlers = {
running: false,
start: function () {
if (this.running) return;
this.running = true;
toast('任务开始');
// 执行任务代码
},
stop: function () {
if (!this.running) return;
this.running = false;
toast('任务停止');
// 停止任务代码
},
};
var floatyWin = floaty.window(
<vertical padding="16">
<button id="startBtn" text="开始任务" bg="#4CAF50" />
<button id="stopBtn" text="停止任务" bg="#F44336" enabled="false" />
<button id="exitBtn" text="退出脚本" bg="#607D8B" />
</vertical>
);
floatyWin.startBtn.click(function () {
handlers.start();
ui.run(function () {
floatyWin.startBtn.enabled = false;
floatyWin.stopBtn.enabled = true;
});
});
floatyWin.stopBtn.click(function () {
handlers.stop();
ui.run(function () {
floatyWin.startBtn.enabled = true;
floatyWin.stopBtn.enabled = false;
});
});
floatyWin.exitBtn.click(function () {
handlers.stop();
floatyWin.close();
hamibot.exit();
});
setInterval(function () {}, 1000);
示例:主要功能测试
这个示例覆盖了 Floaty 模块的所有主要功能:
- 基础悬浮窗:使用 floaty.window()创建带有调整按钮的悬浮窗
- 原始悬浮窗:使用 floaty.rawWindow()创建不包含额外控件的悬浮窗
- 动态修改内容:通过 ui.run()在 UI 线程修改悬浮窗内容
- 触摸控制:使用 setTouchable()控制悬浮窗是否接收触摸事件
- 位置控制:使用 setPosition()、getX()和 getY()控制悬浮窗位置
- 大小控制:使用 setSize()、getWidth()和 getHeight()控制悬浮窗大小
- 自动大小调整:使用-2 参数让悬浮窗根据内容自动调整大小
- 关闭所有:使用 floaty.closeAll()关闭所有悬浮窗
// 文档地址 https://docs.hamibot.com/
// 1. 基础悬浮窗示例
var simpleWindow = floaty.window(
<frame gravity="center" bg="#FF0000">
<text id="text">基础悬浮窗</text>
</frame>
);
// 5秒后关闭
setTimeout(function () {
simpleWindow.close();
}, 5000);
// 2. 原始悬浮窗示例
var rawWindow = floaty.rawWindow(
<frame gravity="center" bg="#00FF00">
<text id="rawText">原始悬浮窗</text>
</frame>
);
rawWindow.setPosition(300, 300);
rawWindow.setSize(200, 100);
// 5秒后关闭
setTimeout(function () {
rawWindow.close();
}, 10000);
// 3. 动态修改悬浮窗内容
var dynamicWindow = floaty.window(
<frame gravity="center" bg="#009900">
<text id="dynamicText">初始文本</text>
</frame>
);
dynamicWindow.setPosition(300, 500);
// 在UI线程修改内容
ui.run(function () {
dynamicWindow.dynamicText.setText('修改后的文本');
});
// 5秒后关闭
setTimeout(function () {
dynamicWindow.close();
}, 15000);
// 4. 设置悬浮窗触摸性
var touchWindow = floaty.rawWindow(
<frame gravity="center" bg="#44ffcc00">
<text>透明不可触摸悬浮窗</text>
</frame>
);
touchWindow.setSize(-1, -1); // 全屏
touchWindow.setTouchable(false); // 不可触摸
// 5秒后关闭
setTimeout(function () {
touchWindow.close();
}, 20000);
// 5. 获取和设置悬浮窗位置
var posWindow = floaty.window(
<frame gravity="center" bg="#FF00FF">
<text id="posText">位置测试</text>
</frame>
);
log('初始位置: X=' + posWindow.getX() + ', Y=' + posWindow.getY());
posWindow.setPosition(100, 200);
log('新位置: X=' + posWindow.getX() + ', Y=' + posWindow.getY());
// 5秒后关闭
setTimeout(function () {
posWindow.close();
}, 25000);
// 6. 设置悬浮窗大小
var sizeWindow = floaty.window(
<frame gravity="center" bg="#FFFF00">
<text id="sizeText">大小测试</text>
</frame>
);
sizeWindow.setPosition(200, 400);
sizeWindow.setSize(300, 150);
log('宽度: ' + sizeWindow.getWidth() + ', 高度: ' + sizeWindow.getHeight());
// 5秒后关闭
setTimeout(function () {
sizeWindow.close();
}, 30000);
// 7. 自动调整大小
var autoSizeWindow = floaty.window(
<frame gravity="center" bg="#00FFFF">
<text id="autoSizeText">自动调整大小</text>
</frame>
);
autoSizeWindow.setSize(-2, -2); // 根据内容自动调整
// 5秒后关闭
setTimeout(function () {
autoSizeWindow.close();
}, 35000);
// 8. 关闭所有悬浮窗
setTimeout(function () {
floaty.closeAll();
hamibot.exit();
}, 40000);