Skip to content

Floaty

floaty 模块提供了悬浮窗的相关函数,可以在屏幕上显示自定义悬浮窗,控制悬浮窗大小、位置等。

悬浮窗在脚本停止运行时会自动关闭,因此,要保持悬浮窗不被关闭,可以用一个空的 setInterval 来实现,例如:

js
setInterval(() => {}, 1000);

检测 Hamibot 是否获得了悬浮窗权限,可使用:

js
if (android.provider.Settings.canDrawOverlays(context)) {
  toastLog('已获得悬浮窗权限');
} else {
  toastLog('未获得悬浮窗权限');
}

floaty.window(layout)

  • layout <xml> | <View> 悬浮窗界面的 XML 或者 View

指定悬浮窗的布局,创建并显示一个悬浮窗,返回一个FloatyWindow对象。

该悬浮窗自带关闭、调整大小、调整位置按键,可根据需要调用setAdjustEnabled()函数来显示或隐藏。

其中 layout 参数可以是 xml 布局或者一个 View,更多信息参见 ui 模块的说明。

例子:

js
var w = floaty.window(
  <frame gravity="center" bg="#FF0000">
    <text id="text">悬浮文字</text>
  </frame>
);
setTimeout(() => {
  w.close();
}, 5000);

这段代码运行后将会在屏幕上显示悬浮文字,并在五秒后消失。

另外,因为脚本运行的线程不是 UI 线程,而所有对控件的修改操作需要在 UI 线程执行,此时需要用ui.run,例如:

js
ui.run(function () {
  w.text.setText('文本');
});

有关返回的FloatyWindow对象的说明,参见下面的FloatyWindow章节。

floaty.rawWindow(layout)

  • layout <xml> | <View> 悬浮窗界面的 XML 或者 View

指定悬浮窗的布局,创建并显示一个原始悬浮窗,返回一个FloatyRawWindow对象。

floaty.window()函数不同的是,该悬浮窗不会增加任何额外设施(例如调整大小、位置按钮),您可以根据自己需要编写任何布局。

而且,该悬浮窗支持完全全屏,可以覆盖状态栏,因此可以做护眼模式之类的应用。

js
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, 悬浮窗上的触摸、点击等事件将被直接传递到悬浮窗下面。处于安全考虑,被悬浮窗接收的触摸事情无法再继续传递到下层。

可以用此特性来制作护眼模式脚本。

js
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 则为根据悬浮窗内容大小而定。例如:

js
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

js
// 创建悬浮窗
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);

示例:多个按钮共享事件

js
// 创建悬浮窗
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);

示例:根据参数控制按钮启用

js
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()关闭所有悬浮窗
js
// 文档地址 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);