YYPOST群发软件 发表于 2026-9-29 06:42:09

自定义悬浮操作球可拖动小球点按展开



自定义悬浮操作球可拖动小球点按展开








// =============================================================
// 案例 ⑤悬浮操作球(可拖动小球 + 点按展开)+ 循环任务
// -------------------------------------------------------------
// 这是 floatUI 的「另一种」写法,专门和 H5HTML 那个「可拖动大控制台
// (开始/暂停/恢复/停止 + 状态 + 日志)」区分开,二者互为补充:
//   · H5HTML 控制台 = 固定大面板、多按钮、带日志/心跳;
//   · 本例 ⑤      = 一个小球,拖动移位、点一下展开成快捷操作、再点收起。
//
// 本例演示的要点:
//   ① 隐藏 AIWROK 系统自带浮标(圆形 A 球):new floatUI().hideSystemFloat()
//      —— 这是 floatUI 的实例方法(用户用官方 API 纠正:别用不存在的 closeAll)。
//      配套还有 showSystemFloat() / setSystemPosition(x,y)。
//   ② 小球拖动:importClass View/MotionEvent + OnTouchListener + getRawX/Y;
//      同一个小球上「按下→移动超阈值=拖动,几乎没动=点按」自动区分。
//   ③ 展开/收起:View.VISIBLE / View.GONE 切换面板。
//   ④ 循环任务放后台线程,进度用 runOnUiThread 回主线程刷小球文字。
//   ⑤ 关闭本脚本自己的浮窗:跟踪实例 + ui.close()(floatUI 没有 closeAll)。
// 用法:运行 → 屏幕出现可拖动小球 → 点小球展开 → 点「自动刷」循环上滑 → 「收工」关窗。
// 铁律:floatUI 根/控件只认原生 Android 类名(LinearLayout/FrameLayout/TextView/Button…)。
// =============================================================

// ① 隐藏系统自带浮标(floatUI 实例方法,启动即生效)
try { new floatUI().hideSystemFloat(); } catch (e) {}

try { importClass(android.view.View); } catch (e) {}
try { importClass(android.view.MotionEvent); } catch (e) {}

function resolveView() {
    if (typeof View !== 'undefined' && View) return View;
    try { if (android && android.view && android.view.View) return android.view.View; } catch (e) {}
    return null;
}
function resolveMotionEvent() {
    if (typeof MotionEvent !== 'undefined' && MotionEvent) return MotionEvent;
    try { if (android && android.view && android.view.MotionEvent) return android.view.MotionEvent; } catch (e) {}
    return null;
}
function rnd(a, b) { return a + Math.floor(Math.random() * (b - a + 1)); }
function sw() { try { return screen.getScreenWidth(); } catch (e) { return 1080; } }
function sh() { try { return screen.getScreenHeight(); } catch (e) { return 1920; } }
function sleepMs(ms) { try { sleep.millisecond(ms); } catch (e) { try { java.lang.Thread.sleep(ms); } catch (e2) {} } }

// 人类化上滑(复用案例③思路):起点终点各 ±8 抖动,更像真人
function humanSwipeUp() {
    var x = Math.round(sw() * 0.5);
    var y1 = rnd(Math.round(sh() * 0.78), Math.round(sh() * 0.82));
    var y2 = rnd(Math.round(sh() * 0.18), Math.round(sh() * 0.22));
    var d = rnd(420, 680);
    try { auto.swip(x, y1, x, y2, d, 0); }
    catch (e) { try { auto.swipe(x, y1, x, y2, d); } catch (e2) { printl(" 滑动失败: " + e2); } }
}

// ---------- 状态 ----------
var ui = null, ctrls = {};
var winX = 60, winY = 180;            // 小球初始位置
var dragging = false, dragLastX = 0, dragLastY = 0, downX = 0, downY = 0;
var running = false, round = 0;
var ROUNDS = 15;                      // 自动刷轮数(到点自动收工)
var worker = null;
var hbOn = true;                      // 保活心跳开关:关窗时置 false,脚本自然结束
var G_FLOATS = [];                  // 本脚本创建的 floatUI 实例(用于启动时清残留)

// ---------- 布局:可拖动小球 + 可展开面板(竖向 LinearLayout,面板默认隐藏) ----------
function buildXml() {
    return '' +
    '<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" ' +
    'android:layout_width="wrap_content" android:layout_height="wrap_content" ' +
    'android:orientation="vertical" android:padding="0dp" ' +
    'android:background="#E61A2B3C">' +
    // 小球(拖动把手 + 点按展开/收起)
    '<Button android:id="ball" android:layout_width="56dp" android:layout_height="56dp" ' +
    '    android:text="AI" android:textColor="#FFFFFF" android:textSize="16sp" ' +
    '    android:background="#16A34A" android:gravity="center"/>' +
    // 展开面板(默认 GONE)
    '<LinearLayout android:id="panel" android:layout_width="200dp" android:layout_height="wrap_content" ' +
    '    android:orientation="vertical" android:background="#E61A2B3C" android:padding="10dp" ' +
    '    android:visibility="gone">' +
    '    <TextView android:id="tip" android:layout_width="match_parent" android:layout_height="wrap_content" ' +
    '      android:text="点「自动刷」开始,循环上滑" android:textColor="#86EFAC" android:textSize="12sp" android:paddingBottom="8dp"/>' +
    '    <Button android:id="btnRun" android:layout_width="match_parent" android:layout_height="38dp" ' +
    '      android:text="▶ 自动刷" android:textColor="#FFFFFF" android:textSize="13sp" android:background="#2563EB" android:layout_marginBottom="6dp"/>' +
    '    <Button android:id="btnStop" android:layout_width="match_parent" android:layout_height="38dp" ' +
    '      android:text="⏹ 收工" android:textColor="#FFFFFF" android:textSize="13sp" android:background="#B91C1C"/>' +
    '</LinearLayout>' +
    '</LinearLayout>';
}

function moveTo(x, y) {
    var screenW = sw(), screenH = sh();
    if (x < 0) x = 0; if (y < 0) y = 0;
    if (x > screenW - 60) x = screenW - 60;
    if (y > screenH - 60) y = screenH - 60;
    winX = x; winY = y;
    try { ui.setPosition(x, y); } catch (e) {}
}

// 展开 / 收起面板
function togglePanel() {
    var V = resolveView();
    try {
      if (V && V.GONE !== undefined && ctrls.panel && ctrls.panel.setVisibility) {
            var vis = ctrls.panel.getVisibility();
            ctrls.panel.setVisibility(vis === V.VISIBLE ? V.GONE : V.VISIBLE);
            printl(vis === V.VISIBLE ? '&#128316; 面板已收起' : '&#128317; 面板已展开');
      }
    } catch (e) { printl('⚠ 展开/收起失败: ' + e); }
}

// 后台循环刷(子线程里跑,不直接碰 UI)
function loopSwipe() {
    while (running && round < ROUNDS) {
      round++;
      humanSwipeUp();
      sleepMs(rnd(900, 1400));
      // 回主线程刷小球 / 提示文字
      try {
            var t = new thread();
            t.runOnUiThread(function () {
                try { if (ctrls.ball) ctrls.ball.setText(round + '/' + ROUNDS); } catch (e) {}
                try { if (ctrls.tip) ctrls.tip.setText('刷到第 ' + round + ' 次'); } catch (e) {}
            });
      } catch (e) {}
    }
    // 收尾:回主线程提示,3 秒后自动关窗并结束脚本
    try {
      var t2 = new thread();
      t2.runOnUiThread(function () {
            try { if (ctrls.ball) ctrls.ball.setText('✔'); } catch (e) {}
            try { if (ctrls.tip) ctrls.tip.setText('已完成 ' + ROUNDS + ' 次,准备收工'); } catch (e) {}
            setRunEnabled(true);   // 完成后恢复按钮(3 秒内可再次点击)
            printl('&#127937; 自动刷完成,3 秒后关窗');
            try { runTime.setTimeout(function () { try { ui.close(); } catch (e) {} hbOn = false; }, 3000); } catch (e) {}
      });
    } catch (e) {}
    running = false;
}

// 切换「自动刷」按钮的启用/禁用与外观(运行时禁用并变灰,避免重复点击 / 给出明确反馈)
function setRunEnabled(on) {
    try {
      if (!ctrls.btnRun) return;
      ctrls.btnRun.setEnabled(on);
      ctrls.btnRun.setClickable(on);
      ctrls.btnRun.setText(on ? '▶ 自动刷' : '● 运行中');
      try { ctrls.btnRun.setBackgroundColor(on ? 0xFF2563EB : 0xFF555555); } catch (e2) {}
    } catch (e) {}
}

function cmdRun() {
    if (running) return;
    running = true; round = 0;
    setRunEnabled(false);            // 运行时禁用按钮 + 显示"运行中"
    printl('▶ 自动刷开始(共 ' + ROUNDS + ' 次上滑)');
    worker = new thread();
    worker.runJsCode(function () { loopSwipe(); }, '自动刷线程');
}
function cmdStop() {
    running = false; hbOn = false;
    setRunEnabled(true);             // 收工后恢复按钮(窗口随即关闭,留底防重开)
    printl('⏹ 收工,关闭窗口');
    try { if (worker && worker.isAlive()) worker.stop(); } catch (e) {}
    try { ui.close(); } catch (e) {}
}

// 拖动 + 点按 二合一(绑在小球上:移动超阈值=拖动,几乎没动=点按展开)
function setupDrag() {
    var V = resolveView(), M = resolveMotionEvent();
    if (!V || !M || !ctrls.ball || !ctrls.ball.setOnTouchListener) {
      printl('⚠ 拖拽不可用(缺 View/MotionEvent),小球用初始位置(仍可点按展开)');
      return;
    }
    try {
      ctrls.ball.setOnTouchListener(new V.OnTouchListener({
            onTouch: function (view, event) {
                try {
                  switch (event.getAction()) {
                        case M.ACTION_DOWN:
                            dragging = false;
                            downX = event.getRawX(); downY = event.getRawY();
                            dragLastX = downX; dragLastY = downY;
                            return true;
                        case M.ACTION_MOVE: {
                            var mx = event.getRawX(), my = event.getRawY();
                            if (!dragging && (Math.abs(mx - downX) > 8 || Math.abs(my - downY) > 8)) dragging = true;
                            if (dragging) {
                              moveTo(winX + (mx - dragLastX), winY + (my - dragLastY));
                              dragLastX = mx; dragLastY = my;
                            }
                            return true;
                        }
                        case M.ACTION_UP:
                            if (!dragging) togglePanel();   // 视为点按 → 展开/收起
                            dragging = false;
                            return true;
                  }
                } catch (e2) { return false; }
                return false;
            }
      }));
      printl('✔ 小球可拖动、可点按展开');
    } catch (e) { printl('⚠ 拖拽绑定失败: ' + e); }
}

// 保活心跳:主线程一空闲脚本就结束、窗口会被回收,故用定时器递归保活;
// running 时持续保活,收工/完成后置 hbOn=false 自然结束。
function heartbeat() {
    if (!hbOn) return;
    runTime.setTimeout(heartbeat, 1000);
}

function main() {
    printl('===== ⑤ 悬浮操作球(可展开)+ 循环任务 =====');

    // 启动前清掉上次残留的浮窗(跟踪本脚本创建的实例,逐个 ui.close())
    for (var i = 0; i < G_FLOATS.length; i++) { try { G_FLOATS.close(); } catch (e) {} }
    G_FLOATS = [];

    ui = new floatUI(); G_FLOATS.push(ui);
    ui.loadXML(buildXml());

    ctrls.ball   = ui.findViewById('ball');
    ctrls.panel= ui.findViewById('panel');
    ctrls.tip    = ui.findViewById('tip');
    ctrls.btnRun   = ui.findViewById('btnRun');
    ctrls.btnStop= ui.findViewById('btnStop');

    if (!ctrls.ball) { printl('✘ 小球控件未取到,设备可能不支持 floatUI'); return; }

    setupDrag();
    try { ctrls.btnRun.setOnClickListener(function () { try { cmdRun(); } catch (e) {} }); } catch (e) {}
    try { ctrls.btnStop.setOnClickListener(function () { try { cmdStop(); } catch (e) {} }); } catch (e) {}

    moveTo(winX, winY);
    heartbeat();   // 启动保活
    printl('小球已出现:拖动移位;点小球展开面板;点「自动刷」循环上滑;「收工」关窗');
}

main();


页: [1]
查看完整版本: 自定义悬浮操作球可拖动小球点按展开