回答
要实现一个可隐藏/展开的控制器,可以使用浮动窗口和按钮控件的交互逻辑。以下是具体实现方案:
// 文档地址 https://docs.hamibot.com/
var controlWindow = floaty.window(
<vertical padding="10dp" bg="#FFFFFF">
<!-- 控制器标题栏 -->
<text id="title" text="控制器面板" textColor="#000000" textSize="16sp" gravity="center"/>
<!-- 内容区域 -->
<frame id="content" visibility="gone" margin="0dp 10dp">
<button id="btn1" text="功能按钮1" w="*" margin="0dp 5dp"/>
<button id="btn2" text="功能按钮2" w="*" margin="0dp 5dp"/>
</frame>
<!-- 展开/收起按钮 -->
<button id="toggleBtn" text="▶ 展开" w="auto" h="40" bg="#E0E0E0"/>
</vertical>
);
// 设置窗口位置和大小
controlWindow.setPosition(500, 1000);
controlWindow.setSize(300, -1);
// 按钮点击事件绑定
controlWindow.toggleBtn.click(() => {
var isVisible = controlWindow.content.getVisibility() == android.view.View.VISIBLE;
if (isVisible) {
// 收起时动画
ui.run(() => {
controlWindow.content.setVisibility(android.view.View.GONE);
controlWindow.toggleBtn.setText("▶ 展开");
});
} else {
// 展开时动画
ui.run(() => {
controlWindow.content.setVisibility(android.view.View.VISIBLE);
controlWindow.toggleBtn.setText("▼ 收起");
});
}
});
// 添加窗口关闭监听(可选)
controlWindow.setOnKeyListener(function(v, keyCode, event){
if(keyCode == android.view.KeyEvent.KEYCODE_BACK){
controlWindow.close();
return true;
}
return false;
});
功能说明:
-
界面结构包含:
- 标题栏
title
:显示控制器标题 - 内容区域
content
:初始隐藏的按钮组 - 控制按钮
toggleBtn
:切换显隐状态
- 标题栏
-
交互逻辑:
- 点击 ▶/▼ 图标时,通过
setVisibility()
切换内容区域的可见性 - 使用
ui.run()
确保UI操作在主线程执行 - 提供展开/收起的视觉反馈(按钮文字变化)
- 点击 ▶/▼ 图标时,通过
-
增强功能建议:
// 添加透明度渐变动画
function animateAlpha(view, start, end, duration=300) {
var animation = new android.view.animation.AlphaAnimation(start, end);
animation.setDuration(duration);
view.startAnimation(animation);
}
// 示例:展开时执行动画
animateAlpha(controlWindow.content, 0, 1);
扩展应用场景:
- 可作为自动化脚本的快捷操作面板
- 集成到无障碍服务中作为功能开关中心
- 实现分层式UI交互(主面板+子功能组)
注意:使用前需确保已申请悬浮窗权限(android.permission.SYSTEM_ALERT_WINDOW
)。若需持久化状态,建议结合storage
模块保存显示状态。
补充回答 1
针对悬浮窗显示不稳定的问题,可按照以下方案优化:
// 文档地址 https://docs.hamibot.com/
// 优化版悬浮窗控制器
var controlWindow = floaty.window(
<vertical id="container" bg="#FFFFFF" w="280">
<view id="header" h="40" bg="#66BBCC" gravity="center">
<text id="title" text="浮动控制器" textColor="#FFFFFF" textSize="16sp"/>
</view>
<vertical id="content" padding="10" visibility="visible">
<!-- 功能按钮组 -->
<button id="btn1" text="功能1" h="50" bg="#EEEEEE" margin="0 5"/>
<button id="btn2" text="功能2" h="50" bg="#EEEEEE" margin="0 5"/>
</vertical>
</vertical>
);
// 设置悬浮窗基础属性
controlWindow.setPosition(500, 1000);
controlWindow.setSize(280, -1); // 固定宽度避免重绘
// 防抖动更新方法
function safeUpdateUI(updateFunc) {
if (!this._updatePending) {
this._updatePending = true;
ui.post(() => {
try {
updateFunc();
} finally {
this._updatePending = false;
}
}, 100);
}
}
// 拖拽移动逻辑
var dragging = false;
var startX = 0, startY = 0;
controlWindow.header.setOnTouchListener(function(view, event) {
switch(event.getAction()) {
case android.view.MotionEvent.ACTION_DOWN:
dragging = true;
startX = event.getRawX();
startY = event.getRawY();
break;
case android.view.MotionEvent.ACTION_MOVE:
if (dragging) {
var x = controlWindow.getX() + (event.getRawX() - startX);
var y = controlWindow.getY() + (event.getRawY() - startY);
safeUpdateUI(() => {
controlWindow.setPosition(x, y);
});
startX = event.getRawX();
startY = event.getRawY();
}
break;
case android.view.MotionEvent.ACTION_UP:
dragging = false;
break;
}
return true;
});
// 状态同步优化
function syncState() {
// 使用post保证UI更新顺序
ui.post(() => {
// 同步逻辑
});
}
问题定位与解决方案:
-
悬浮窗绘制优化
- 将宽度设为固定值(
w="280"
),避免wrap_content
导致的测量冲突 - 使用
floaty.window()
替代floaty.rawWindow()
减少层级嵌套
- 将宽度设为固定值(
-
线程安全控制
- 所有UI操作通过
ui.run()
或ui.post()
提交到主线程 - 添加防抖动逻辑(
safeUpdateUI
),防止高频触发导致卡顿
- 所有UI操作通过
-
系统限制绕过方案
// 添加常驻通知(保持进程活跃) threads.start(function() { while(true) { // 每5秒发送前台通知 notify("脚本运行中", {id: 1}); sleep(5000); } });
- 在
manifest.json
中添加:
{ "foreground_service": true, "screen_on": true }
- 在
-
设备特异性处理(MIUI示例)
// 强制保持屏幕常亮 device.keepScreenOn = true; // 检查MIUI电池保护 if (device.sdkInt >= 23) { var powerManager = context.getSystemService(android.content.Context.POWER_SERVICE); if (!powerManager.isIgnoringBatteryOptimizations(context.getPackageName())) { toast("请在省电设置中允许本应用常驻"); } }
-
内存管理优化
- 使用对象复用:
// 避免重复创建视图,改为显隐控制 function showSection(section) { ["content1", "content2"].forEach(id => { controlWindow[id].visibility = id === section ? View.VISIBLE : View.GONE; }); }
调试建议:
- 使用
layout inspector
查看层级结构 - 监控内存使用情况:
var startUsage = java.lang.Runtime.getRuntime().totalMemory();
// 执行操作后
toast("内存消耗: " + (java.lang.Runtime.getRuntime().totalMemory() - startUsage)/1024 + "KB");
ℹ️ 本回答由 AI 生成,内容仅供参考