制作一个控制器 可以让他隐藏或展开

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

解决控制显示不稳定的问题

回答

Hamibot AI
1
· 2 个月前

要实现一个可隐藏/展开的控制器,可以使用浮动窗口和按钮控件的交互逻辑。以下是具体实现方案:

// 文档地址 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;
});

功能说明:

  1. 界面结构包含:

    • 标题栏 title:显示控制器标题
    • 内容区域 content:初始隐藏的按钮组
    • 控制按钮 toggleBtn:切换显隐状态
  2. 交互逻辑:

    • 点击 ▶/▼ 图标时,通过 setVisibility() 切换内容区域的可见性
    • 使用 ui.run() 确保UI操作在主线程执行
    • 提供展开/收起的视觉反馈(按钮文字变化)
  3. 增强功能建议:

// 添加透明度渐变动画
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(() => {
        // 同步逻辑
    });
}

问题定位与解决方案:

  1. 悬浮窗绘制优化

    • 将宽度设为固定值(w="280"),避免wrap_content导致的测量冲突
    • 使用floaty.window()替代floaty.rawWindow()减少层级嵌套
  2. 线程安全控制

    • 所有UI操作通过ui.run()ui.post()提交到主线程
    • 添加防抖动逻辑(safeUpdateUI),防止高频触发导致卡顿
  3. 系统限制绕过方案

    // 添加常驻通知(保持进程活跃)
    threads.start(function() {
        while(true) {
            // 每5秒发送前台通知
            notify("脚本运行中", {id: 1});
            sleep(5000);
        }
    });
    
    • manifest.json中添加:
    {
      "foreground_service": true,
      "screen_on": true
    }
    
  4. 设备特异性处理(MIUI示例)

    // 强制保持屏幕常亮
    device.keepScreenOn = true;
    
    // 检查MIUI电池保护
    if (device.sdkInt >= 23) {
        var powerManager = context.getSystemService(android.content.Context.POWER_SERVICE);
        if (!powerManager.isIgnoringBatteryOptimizations(context.getPackageName())) {
            toast("请在省电设置中允许本应用常驻");
        }
    }
    
  5. 内存管理优化

    • 使用对象复用:
    // 避免重复创建视图,改为显隐控制
    function showSection(section) {
        ["content1", "content2"].forEach(id => {
            controlWindow[id].visibility = 
                id === section ? View.VISIBLE : View.GONE;
        });
    }
    

调试建议:

  1. 使用layout inspector查看层级结构
  2. 监控内存使用情况:
var startUsage = java.lang.Runtime.getRuntime().totalMemory();
// 执行操作后
toast("内存消耗: " + (java.lang.Runtime.getRuntime().totalMemory() - startUsage)/1024 + "KB");
ℹ️ 本回答由 AI 生成,内容仅供参考