解决auto.js控件查找的难题

在写auto.js很容易碰到的问题是查找控件的问题,往往是控件查找不到或查找到的点击失败,导致一连串的操作失败,这里出问题的往往是菜单是动态加载的,而查找的时候可能是页面并未加载完成,导致查找到的控件是加载过程中的,去点击的时候就会出错。为了解决这个问题,我的思路是第一次查找到后,等500毫秒再查找一次,如果两次的位置相同,那就可以判断是最终加载完成的控件,此时点击就不会失败。代码见下
 

/**
 * 查找控件并执行指定动作。
 * @param {object|string} selector - 控件选择器对象或包含文本的字符串。
 * @param {number} timeout - 查找控件的超时时间(毫秒)。
 * @param {string|object} action - 要执行的动作,可以是字符串或对象。
 * @returns {boolean|object} - 返回查找到的控件,找不到返回false,如果有动作,返回操作是否成功的布尔值。
 */
function findControl(selector, timeout, action) {
    log("进入findControl函数,参数selector, timeout, action分别为", selector.toString(), timeout, action);
    timeout = typeof timeout === 'number' && timeout >= 2000 ? timeout : 3000;
    if (timeout === 3000) log("超时时间被设置为默认值3000ms");

    var element = null;
    var startTime = Date.now();
    var previousBounds = null;

    // 同步查找控件
    while (Date.now() - startTime < timeout) {
        if (selector && typeof selector.findOne === 'function') {
            element = selector.findOne(timeout);
        } else if (typeof selector === 'string') {
            element = textContains(selector).findOne(timeout);
        } else if (typeof selector === 'object' && selector !== null) {
            if (selector.className) {
                element = className(selector.className).findOne(timeout);
            } else if (selector.id) {
                element = id(selector.id).findOne(timeout);
            } else if (selector.text) {
                element = text(selector.text).findOne(timeout);
            }
        } else {
            log("错误:findControl 输入的selector参数不正确", selector.toString());
            errCount++;
            //handleError("输入的selector参数不正确" + selector.toString());
            return false;
        }

        if (element !== null && previousBounds !== null && element.bounds().equals(previousBounds)) {
            // 边界一致,说明加载完成,跳出循环
            break;
        }

        if (element === null) {
            sleep(500);
        } else if (previousBounds !== null && !element.bounds().equals(previousBounds)) {
            //log('提示:还真遇到了弹出式菜单未加载完成的情况,所以找到的控件是未加载完成的', previousBounds, element.bounds());
            previousBounds = element.bounds();
            sleep(500);
        } else {
            previousBounds = element.bounds();
            sleep(500);
        }
    }

    if (element === null) {
        log("错误:查找控件超时,未找到控件。");
        return false;
    }
    if (action) {
        // 执行动作
        return performAction(element, action);
    } else {
        return element;
    }
}

/**
 * 对找到的控件执行指定动作。
 * @param {object} control - 找到的控件对象。
 * @param {string|object} action - 要执行的动作。
 * @returns {boolean} - 返回操作是否成功的布尔值。
 */
function performAction(control, action) {
    log("进入performAction,执行动作:", JSON.stringify(action));
    try {
        if (action == 'click') {
            //log("对控件采用点击操作");
            if (control.clickable()) {
                //log("找到控件并点击");
                return (control.click());
            } else {
                //log("找到的控件不可点击,使用坐标点击");
                return (click(control.bounds().centerX(), control.bounds().centerY()));
            }
        } else if (action == 'longclick') {
            //log("对控件采用长按操作");
            if (control.longClickable()) {
                //log("找到控件并长按");
                return (control.longClick());
            } else {
                //log("找到的控件不可长按,使用坐标长按");
                return (longClick(control.bounds().centerX(), control.bounds().centerY()));
            }
        } else if (typeof action == 'object' && action !== null) {
            if (typeof action.setText !== 'undefined') {
                //log("执行setText动作,文本为:", action.setText);
                // 执行setText动作的代码逻辑
                if (control.setText(action.setText)) {
                    //log("setText动作执行成功。");
                    return true;
                } else {
                    log("错误:setText动作执行失败。");
                    return false;
                };
            } else if (typeof action.input !== 'undefined') {
                //log("执行input动作,文本为:", action.input);
                // 执行input动作的代码逻辑
                setClip(action.input);
                // 找到可点击的父控件
                let parent = control;
                while (parent && !parent.clickable()) {
                    parent = parent.parent();
                }
                if (parent && parent.click()) {
                    sleep(500); // 等待输入框响应
                    // 尝试粘贴文本,这通常需要文本框已经获得焦点
                    paste();
                    //log("input动作已执行。");
                    return true;
                } else {
                    log("错误:无法点击控件输入文本" + action.input + "或控件不存在。");
                    return false;
                }
            } else if (action.swipe && Array.isArray(action.swipe) && action.swipe.length === 5) {
                //log("执行swipe动作,参数为:", action.swipe);
                // 执行swipe动作的代码逻辑
                if (swipe(action.swipe[0], action.swipe[1], action.swipe[2], action.swipe[3], action.swipe[4])) {
                    //log("swipe动作执行成功。");
                    return true;
                } else {
                    log("错误:swipe动作执行失败。");
                    return false;
                };
            } else {
                log("提示:未识别的动作参数:", JSON.stringify(action));
                return false;
            }
        } else {
            log("提示:没有传入动作参数,直接返回找到的控件");
            return control;
        }
    } catch (error) {
        errCount++;
        log("错误:执行动作时发生错误: " + error.name + error.message + error.stack);
        //handleError("错误:执行动作时发生错误: ", error);
        return false;
    }
}

以上代码在auto.js4.1.1版本中运行通过,没有遇到过异常。

  • 8
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
autojs的apk Auto.js使用JavaScript作为脚本语言,目前使用Rhino 1.7.7.2作为脚本引擎,支持ES5与部分ES6特性。 因为Auto.js是基于JavaScript的,学习Auto.js的API之前建议先学习JavaScript的基本语法和内置对象,可以使用教程前面的两个JavaScript教程链接来学习。 如果您想要使用TypeScript来开发,目前已经有开发者公布了一个可以把使用TypeScript进行Auto.js开发的工具,参见Auto.js DevTools。 如果想要在电脑而不是手机上开发Auto.js,可以使用VS Code以及相应的Auto.js插件使得在 电脑上编辑的脚本能推送到手机运行,参见Auto.js-VSCode-Extension。 本文档的章节大致上是以模块来分的,总体上可以分成"自动操作"类模块(控件操作、触摸模拟、按键模拟等)和其他类模块(设备、应用、界面等)。 "自动操作"的部分又可以大致分为基于控件和基于坐标的操作。基于坐标的操作是传统按键精灵、触摸精灵等脚本软件采用的方式,通过屏幕坐标来点击、长按指定位置模拟操作,从而到达目的。例如click(100, 200), press(100, 200, 500)等。这种方式在游戏类脚本中比较有可行性,结合色、坐标放缩功能也能达到较好的兼容性。但是,这种方式对一般软件脚本却难以达到想要的效果,而且这种方式需要安卓7.0版本以上或者root权限才能执行。所以对于一般软件脚本(例如批量添加联系人、自动提取短信验证码等等),我们采用基于控件的模拟操作方式,结合通知事情、按键事情等达成更好的工作流。这些部分的文档参见基于控件的操作和基于坐标的操作。 其他部分主要包括: app: 应用。启动应用,卸载应用,使用应用查看、编辑文件、访问网页,发送应用间广播等。 console: 控制台。记录运行的日志、错误、信息等。 device: 设备。获取设备屏幕宽高、系统版本等信息,控制设备音量、亮度等。 engines: 脚本引擎。用于启动其他脚本。 events: 事件与监听。按键监听,通知监听,触摸监听等。 floaty: 悬浮窗。用于显示自定义的悬浮窗。 files: 文件系统。文件创建、获取信息、读写。 http: HTTP。发送HTTP请求,例如GET, POST等。 images, colors: 图片和图色处理。截图,剪切图片,色,读取保存图片等。 keys: 按键模拟。比如音量键、Home键模拟等。 shell: Shell命令。 threads: 多线程支持。 ui: UI界面。用于显示自定义的UI界面,和用户交互。 除此之外,Auto.js内置了对Promise。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值