AutoJs6 中文文档 下载 App

floaty 自定义悬浮窗 — 打造专属悬浮按钮

floaty 模块定位

AutoJs6 自带浮动按钮(内置功能),floaty 模块让你可以创建自己的悬浮窗——完全自定义位置、大小、内容和交互。自定义悬浮窗可以作为脚本的快捷入口、信息显示面板、甚至是一个迷你 UI。

┌────────────┐     ┌───────────────┐     ┌────────┐
│ AutoJs6    │     │ 自定义信息面板  │     │ 迷你UI │
│ 内置浮动按钮│     │ floaty.rawWindow│     │ floaty │
└────────────┘     └───────────────┘     └────────┘
    预制功能            自定义布局           迷你完整界面

两种悬浮窗

floaty.rawWindow — 原始悬浮窗

直接用 E4X XML 定义布局,最灵活但需要手动管理:

// 原始悬浮窗 —— 简单圆形按钮
let w = floaty.rawWindow(
    <frame gravity="center">
        <text text="悬浮" textColor="#FFF" textSize="14sp" bg="#FF5722" w="50" h="50" gravity="center"/>
    </frame>
);

// 设置悬浮窗位置
w.setPosition(500, 800);

// 点击事件
w.click(() => {
    toast("悬浮窗被点击");
});

floaty.window — 完整窗口

类似迷你应用窗口,有标题栏、调整大小、关闭等:

// 完整悬浮窗 —— 带标题栏
let win = floaty.window(
    <vertical bg="#FFF">
        <horizontal bg="#FF5722" padding="8">
            <text text="我的悬浮窗" textColor="#FFF" w="*"/>
            <button id="closeBtn" text="×" textColor="#FFF" bg="#FF5722"/>
        </horizontal>
        <text id="content" text="这是悬浮窗内容" padding="16" w="*"/>
    </vertical>
);

// 设置初始位置和大小
win.setPosition(100, 200);
win.setSize(300, 400);

win.closeBtn.click(() => win.close());

拖动移动

单指拖动

让悬浮窗可以随手指拖动:

let w = floaty.rawWindow(
    <frame bg="#FF5722" w="60" h="60" gravity="center">
        <text text="拖我" textColor="#FFF" textSize="12sp"/>
    </frame>
);

// 设置可拖动(默认全屏范围)
w.setTouchable(true);
let isDragging = false;
let initialX, initialY;
let wX, wY;

w.setOnTouchListener(function(view, event) {
    let action = event.getAction();
    switch (action) {
        case event.ACTION_DOWN:
            isDragging = true;
            initialX = event.getRawX();
            initialY = event.getRawY();
            wX = w.getPosition().x;
            wY = w.getPosition().y;
            return true;
            
        case event.ACTION_MOVE:
            if (isDragging) {
                let dx = event.getRawX() - initialX;
                let dy = event.getRawY() - initialY;
                w.setPosition(wX + dx, wY + dy);
            }
            return true;
            
        case event.ACTION_UP:
        case event.ACTION_CANCEL:
            isDragging = false;
            // 可选:松手后吸附到屏幕边缘
            snapToEdge(w);
            return true;
    }
    return false;
});

// 边缘吸附
function snapToEdge(window) {
    let screenWidth = device.width;
    let half = screenWidth / 2;
    let currentX = window.getPosition().x;
    if (currentX < half) {
        window.setPosition(0, window.getPosition().y);
    } else {
        window.setPosition(screenWidth - 60, window.getPosition().y);
    }
}

autoMove — 简化拖动

// AutoJs6 提供了简化的拖动实现
let w = floaty.rawWindow(...);

// 让整个窗口都能拖动(无需手动处理触摸)
floaty.enableDragging(w);

// 只让某个控件作为拖拽手柄
let w2 = floaty.rawWindow(
    <frame>
        <text id="handle" text="拖这里" bg="#888" w="*" h="40"/>
        <text text="窗口其他内容" textColor="#FFF" marginTop="40"/>
    </frame>
);
floaty.enableDragging(w2, w2.handle);  // 只 handle 可拖

悬浮窗属性

获取与设置

let w = floaty.rawWindow(...);

// 位置
let pos = w.getPosition();
console.log("位置: (" + pos.x + ", " + pos.y + ")");
w.setPosition(100, 200);

// 大小
w.setSize(300, 400);
let size = w.getSize();
console.log("尺寸: " + size.width + "x" + size.height);

// 可见性
w.show();
w.hide();
w.close();  // 关闭并销毁

// 置顶
w.setTouchable(true);     // 能否接收触摸事件
w.setTouchable(false);    // 透明穿透(下面的控件仍可操作)

// 悬浮窗类型
// "application" — 默认,需要悬浮窗权限
// "system" — 系统级悬浮(需要 Root 或特殊权限)

控件属性访问

let w = floaty.rawWindow(
    <vertical>
        <text id="title" text="标题"/>
        <button id="action" text="执行"/>
    </vertical>
);

// 直接访问子控件
w.title.setText("新标题");
w.action.click(() => {
    toast("执行了");
});

实战:实时信息显示面板

/**
 * 实时显示脚本运行状态的悬浮面板
 */
function createStatusPanel() {
    let panel = floaty.window(
        <vertical bg="#CC000000" padding="10">
            <horizontal bg="#FF5722" padding="6">
                <text text="任务状态" textColor="#FFF" textSize="12sp" w="*"/>
                <button id="close" text="×" textColor="#FFF" bg="#FF5722" textSize="12sp"/>
            </horizontal>
            <vertical padding="8">
                <text id="status" text="⏳ 等待启动" textColor="#FFF" textSize="12sp"/>
                <text id="count" text="完成: 0" textColor="#8BC34A" textSize="12sp" marginTop="4"/>
                <text id="time" text="运行时长: 0s" textColor="#90CAF9" textSize="12sp" marginTop="4"/>
            </vertical>
        </vertical>
    );
    
    panel.setPosition(100, 300);
    panel.close.click(() => panel.close());
    
    let startTime = Date.now();
    let completedCount = 0;
    
    // 更新函数
    function updateStatus(text) {
        panel.status.setText(text);
        panel.count.setText("完成: " + completedCount);
        
        let elapsed = Math.floor((Date.now() - startTime) / 1000);
        panel.time.setText("运行时长: " + elapsed + "s");
    }
    
    // 定时刷新运行时间
    let timer = setInterval(() => updateStatus(panel.status.text()), 1000);
    
    // 返回控制对象
    return {
        panel: panel,
        update: (text) => {
            completedCount++;
            updateStatus(text);
        },
        close: () => {
            clearInterval(timer);
            panel.close();
        }
    };
}

// 使用
let status = createStatusPanel();
status.update("✓ 签到完成");
status.update("✓ 清理完成");

注意事项

悬浮窗权限

Android 6.0+ 需要动态请求悬浮窗权限:

if (!floaty.checkPermission()) {
    // 跳转到系统设置页授权
    floaty.requestPermission();
} else {
    // 有权限,正常创建悬浮窗
    let w = floaty.rawWindow(...);
}

多个悬浮窗

// 可以同时创建多个悬浮窗
let btn = floaty.rawWindow(<frame bg="#FF0000" w="50" h="50"/>);
let panel = floaty.window(<vertical>...</vertical>);

btn.setPosition(100, 500);
panel.setPosition(200, 300);

// 各自独立管理
btn.close();
panel.close();

清理资源

// 脚本结束时记得关闭所有悬浮窗
events.on("exit", () => {
    floaty.closeAll();  // 一键关闭全部
});

与内置浮动按钮的关系

AutoJs6 的浮动按钮本身也是一个 floaty 悬浮窗,只是由 AutoJs6 内置管理。你创建的自定义悬浮窗和内置的可以共存。