"ui";
// 显示控制台的悬浮窗
console.show();
// 隐藏输入框和确定按钮
ui.run(function() {
let mConsoleViewField = runtime.console.getClass().getDeclaredField('mConsoleView');
mConsoleViewField.setAccessible(true);
let refConsoleView = mConsoleViewField.get(runtime.console);
let mConsoleView = refConsoleView.get();
mConsoleView.findViewById(com.stardust.autojs.R.id.input_container).visibility = android.view.View.GONE;
});
/**
-
显示配置对话框 - 优化版
*/
function showConfigDialog() {try {
log("在UI线程中显示配置对话框");
// 创建一个自定义布局的对话框
var d = dialogs.build({
title: "请选择并输入",
view: ui.layout(
<vertical padding="16">
<!-- 文本输入框 -->
<text text="请输入内容:" marginBottom="8" />
<edittext id="inputText" hint="在此输入文本..." marginBottom="16" /><!-- 单选框组 --> <text text="请选择一项:" marginBottom="8" /> <radiogroup id="radioGroup" orientation="vertical"> <radiobutton text="选项A1" id="optionA1" /> <radiobutton text="选项B1" id="optionB1" /> <radiobutton text="选项C1" id="optionC1" /> <radiobutton text="选项D1" id="optionD1" /> </radiogroup> </vertical> ), positive: "确定", negative: "取消", onPositive: function() { // 获取文本输入框的值 var inputValue = d.getView().findViewById("inputText").getText(); // 获取选中的单选框的值 var radioGroup = d.getView().findViewById("radioGroup"); var checkedId = radioGroup.getCheckedRadioButtonId(); var selectedOption = ""; if (checkedId != -1) { var radioButton = d.getView().findViewById(checkedId); selectedOption = radioButton.getText(); } // 显示结果 toast("输入内容: " + inputValue + "\n选择选项: " + selectedOption); } }); // 显示对话框 d.show()
} catch (e) {
log("显示配置对话框时出错: " + e.message);
}
}
/**
-
主函数
*/
function main() {
// 检查无障碍服务是否开启
if (!auto.service) {
log("无障碍服务未开启,请先开启");
auto.waitFor();
return;
}// 创建悬浮窗
var floatyWin = floaty.window(
<vertical padding="5" backgroundColor="#33000000" gravity="center">
<horizontal spacing="0">
<button
id="startBtn"
text="开始"
width="60"
height="30"
textSize="12sp"
margin="0"
padding="2"
/>
<button
id="configBtn"
text="配置"
width="60"
height="30"
textSize="12sp"
margin="0"
padding="2"
/>
<button
id="exitBtn"
text="退出"
width="60"
height="30"
textSize="12sp"
margin="0"
padding="2"
/>
</horizontal>
</vertical>
);// 设置悬浮窗位置
floatyWin.setPosition(device.width - 500, 100);// 开始运行脚本
floatyWin.startBtn.click(function() {
log("开始执行脚本...");
});// 配置按钮 - 在UI线程中显示配置对话框
floatyWin.configBtn.click(function() {
log("打开配置界面...");
showConfigDialog();
});// 退出按钮
floatyWin.exitBtn.click(function() {
log("退出脚本");
floatyWin.close();
hamibot.exit();
});// 保持悬浮窗不被关闭
setInterval(function() {}, 1000);
}
// 执行主函数
main();
为什么最上层只要加上"ui"; 脚本运行页面就无法正常加载卡死,不加"ui";就会提示ui.layout需要再ui模式下运行才能使用
<vertical padding="16">
<!-- 文本输入框 -->
<text text="请输入内容:" marginBottom="8" />
<edittext id="inputText" hint="在此输入文本..." marginBottom="16" />
<!-- 单选框组 -->
<text text="请选择一项:" marginBottom="8" />
<radiogroup id="radioGroup" orientation="vertical">
<radiobutton text="选项A1" id="optionA1" />
<radiobutton text="选项B1" id="optionB1" />
<radiobutton text="选项C1" id="optionC1" />
<radiobutton text="选项D1" id="optionD1" />
</radiogroup>
</vertical> 那住部分代码用ui.run该怎么写
问题2中你给的场景2中,虽然不需要用到"ui";,但你仍然使用了ui.layout,不还是会造成冲突么,这并没有解决我说的问题
回答
这个问题是因为在脚本中同时使用了两种不同的UI模式(模块模式和非模块模式)导致的冲突。我来解释原因和解决方案:
原因分析:
"ui";
声明会导致脚本进入模块模式(module mode),此时所有UI操作都必须在ui.run()
里执行- 你代码中的
ui.layout()
和floaty.window()
等操作都在主线程直接调用,与模块模式的要求冲突
解决方案(二选一):
方法一:移除"ui";
声明,保持非模块模式(推荐)
// 文档地址 https://docs.hamibot.com/
// 直接使用非模块模式
// 显示控制台的悬浮窗
console.show();
// 隐藏输入框和确定按钮
ui.run(function() {
let mConsoleViewField = runtime.console.getClass().getDeclaredField('mConsoleView');
mConsoleViewField.setAccessible(true);
let refConsoleView = mConsoleViewField.get(runtime.console);
let mConsoleView = refConsoleView.get();
mConsoleView.findViewById(com.stardust.autojs.R.id.input_container).visibility = android.view.View.GONE;
});
// 剩下的代码保持不变
// ...
hamibot.exit();
方法二:保持"ui";
声明,将所有UI操作放入ui.run()
"ui";
// 文档地址 https://docs.hamibot.com/
// 显示控制台的悬浮窗
console.show();
// 隐藏输入框和确定按钮
ui.run(function() {
let mConsoleViewField = runtime.console.getClass().getDeclaredField('mConsoleView');
mConsoleViewField.setAccessible(true);
let refConsoleView = mConsoleViewField.get(runtime.console);
let mConsoleView = refConsoleView.get();
mConsoleView.findViewById(com.stardust.autojs.R.id.input_container).visibility = android.view.View.GONE;
// 将主函数中的所有UI操作移到这里
main();
});
function main() {
// 所有UI操作代码...
}
hamibot.exit();
关键区别:
- 非模块模式更灵活,可以直接在主线程调用UI操作
- 模块模式更严格,所有UI操作必须在
ui.run()
中执行
建议使用第一种方案(移除"ui";
),因为你的脚本中已经使用了ui.run()
来处理需要UI线程的部分,这样能保持代码结构更清晰。
补充回答 1
要将这部分XML布局代码用ui.run
包装,可以采用以下方式。这里有两种常见场景的写法:
场景1:在对话框中使用(如原代码示例)
// 文档地址 https://docs.hamibot.com/
var dialogView = ui.run(function() {
return ui.layout(
<vertical padding="16">
<!-- 文本输入框 -->
<text text="请输入内容:" marginBottom="8" />
<edittext id="inputText" hint="在此输入文本..." marginBottom="16" />
<!-- 单选框组 -->
<text text="请选择一项:" marginBottom="8" />
<radiogroup id="radioGroup" orientation="vertical">
<radiobutton text="选项A1" id="optionA1" />
<radiobutton text="选项B1" id="optionB1" />
<radiobutton text="选项C1" id="optionC1" />
<radiobutton text="选项D1" id="optionD1" />
</radiogroup>
</vertical>
);
});
// 然后在对话框中引用这个View
var d = dialogs.build({
title: "请选择并输入",
view: dialogView,
positive: "确定",
negative: "取消",
// 其他回调...
});
d.show();
场景2:直接作为主界面使用
"ui";
// 文档地址 https://docs.hamibot.com/
ui.run(function() {
ui.layout(
<vertical padding="16">
<!-- 文本输入框 -->
<text text="请输入内容:" marginBottom="8" />
<edittext id="inputText" hint="在此输入文本..." marginBottom="16" />
<!-- 单选框组 -->
<text text="请选择一项:" marginBottom="8" />
<radiogroup id="radioGroup" orientation="vertical">
<radiobutton text="选项A1" id="optionA1" />
<radiobutton text="选项B1" id="optionB1" />
<radiobutton text="选项C1" id="optionC1" />
<radiobutton text="选项D1" id="optionD1" />
</radiogroup>
</vertical>
);
// 可以在这里添加控件事件处理
ui.inputText.on("key", function(event) {
if(event.action == KeyEvent.ACTION_UP && event.keyCode == KeyEvent.KEYCODE_ENTER) {
toast("你输入了: " + ui.inputText.getText());
}
});
});
// 保持脚本运行
setInterval(function(){}, 1000);
hamibot.exit();
关键要点:
- 如果使用
"ui";
声明,则所有UI操作(包括ui.layout()
)必须在ui.run()
中执行 - 如果作为对话框内容使用,可以先用
ui.run
生成View对象,再传递给对话框 - 事件绑定也应该在
ui.run
中进行
注意:在实际使用中,更推荐第一种方式(非模块模式),除非你确实需要使用模块模式的特定功能。
补充回答 2
你说得对,确实我在场景2的处理方式上存在矛盾。让我们再重新梳理这个问题,并给出完全正确的解决方案。
问题的本质
- 冲突原因:当使用
"ui";
声明时,整个脚本会进入模块模式,此时 所有UI操作(包括ui.layout
和控件访问)都 必须 在ui.run()
中执行 - 你的需求:既想使用
ui.layout
,又不想因为"ui";
导致脚本卡死
完全正确的解决方案(三种写法)
方案1:完全不使用 "ui";
(推荐)
// 文档地址 https://docs.hamibot.com/
// 直接使用ui.layout(非模块模式)
var layout = ui.layout(
<vertical padding="16">
<text text="请输入内容:" marginBottom="8" />
<edittext id="inputText" hint="在此输入文本..." marginBottom="16" />
<radiogroup id="radioGroup" orientation="vertical">
<radiobutton text="选项A1" id="optionA1" />
<radiobutton text="选项B1" id="optionB1" />
<radiobutton text="选项C1" id="optionC1" />
<radiobutton text="选项D1" id="optionD1" />
</radiogroup>
</vertical>
);
// 控件操作也不需要ui.run
ui.inputText.setText("默认文本");
// 保持运行
setInterval(function(){}, 1000);
hamibot.exit();
方案2:使用 "ui";
但正确封装(适用于复杂UI)
"ui";
// 文档地址 https://docs.hamibot.com/
ui.run(function(){
// 所有UI操作必须在这里面
ui.layout(
<vertical padding="16">
<text text="请输入内容:" marginBottom="8" />
<edittext id="inputText" hint="在此输入文本..." marginBottom="16" />
<radiogroup id="radioGroup" orientation="vertical">
<radiobutton text="选项A1" id="optionA1" />
<radiobutton text="选项B1" id="optionB1" />
<radiobutton text="选项C1" id="optionC1" />
<radiobutton text="选项D1" id="optionD1" />
</radiogroup>
</vertical>
);
// 事件绑定也在这里
ui.inputText.on("key", function(e){
if(e.keyCode == 66){ // 回车键
toast(ui.inputText.getText());
}
});
});
// 非UI操作可以放在外面
log("界面已加载");
hamibot.exit();
方案3:对话框场景专用写法(不需要模块声明)
// 文档地址 https://docs.hamibot.com/
// 直接创建布局(不需要ui.run)
var view = ui.layout(
<vertical padding="16">
<!-- 你的布局内容 -->
</vertical>
);
// 用于对话框
dialogs.build({
title: "配置",
view: view,
positive: "确定",
negative: "取消"
}).show();
hamibot.exit();
使用建议
- 简单脚本:用方案1(不声明
"ui";
) - 复杂UI交互:用方案2(声明
"ui";
并严格封装) - 对话框使用:用方案3(最简洁)
特别说明:当不使用"ui";
时,ui.layout
和控件访问可以直接执行,这是完全合法的用法,不会造成冲突。只有在使用"ui";
声明后,才需要严格遵守ui.run
的封装要求。