dialogs 对话框
dialogs 对话框
dialogs 是 ScriptX 里这一套可编排对话框模块。它支持两种用法:
- 直接调高层函数,比如
dialogs.alert(...)、dialogs.confirm(...) - 先
dialogs.build(options)拿到一个对话框实例,再慢慢挂监听、改进度、手动show()
先记住这 13 条
- 这组能力现在的全局对象名是
dialogs。 dialogs.prompt(...)只是dialogs.input(...)的别名。- 如果你给
alert / confirm / input / select ...传了回调,它们会立刻返回null,然后异步等结果。 - 如果你不传回调:普通脚本工作线程会阻塞并直接返回结果;
ui页面或 Android 主线程会返回原生Promise,避免把主线程卡死。 dialogs.build(options)只负责创建实例,不会自动弹出来;要么你自己dialog.show(),要么再awaitResult()。itemsSelectMode当前可识别的值只有select、single、multi这 3 类;single_choice、multichoice这种只是同义写法。dialog.getActionButton(action)/dialog.setActionButton(action, title)里的action现在只认positive、negative、neutral这 3 组名字和它们的常见别名。type/setDefaultDialogType(...)会真正参与宿主选择,并且非法值会立即抛错,不再静默回退。overlay会先尝试当前进程自己的系统悬浮窗权限;不可用时再尝试已经连接的无障碍服务窗口。stubborn: true会强制把cancelable和canceledOnTouchOutside都改成false。rawInput(...)和input(...)当前在脚本层的返回行为基本一致,都会在确认时返回输入框文本。dialog.emit(...)只会触发你挂在这个实例上的 JS 监听器,不会代替真正的按钮点击。- 异步结果是 Rhino 原生
Promise,支持instanceof Promise、链式then / catch / finally和 thenable 展开,不只是长得像 Promise 的包装对象。
ui 页面里的返回行为
这一条很重要,因为很多人会在普通脚本和 ui.layout(...) 页面之间来回切。
普通脚本环境
不传回调时,dialogs.confirm(...)、dialogs.input(...) 这些会:
- 先弹出对话框
- 当前脚本线程等待结果
- 最后直接把结果返回给你
也就是这种写法可以成立:
const confirmed = dialogs.confirm("删除", "确定继续吗?");
if (confirmed) {
log("继续");
}
ui 页面或 Android 主线程
如果当前页面来自 ui.layout(...),或者调用发生在 Android 主线程,源码会返回原生 Promise,而不是阻塞主线程。
也就是更稳的写法应该像这样:
dialogs.confirm("删除", "确定继续吗?").then(function (confirmed) {
if (confirmed) {
log("继续");
}
});
为什么文档要单独强调这件事
因为这会直接影响你写页面事件回调时的代码风格:
- 普通脚本里可以先调对话框,再立刻拿返回值
ui页面里更推荐then(...)/ 回调版
如果你把普通脚本的同步写法原样搬进 ui 页面,就容易出现“为什么这里拿到的不是布尔值”的困惑。
dialogs.setDefaultDialogType(value)
设置后续 dialogs.alert(...)、dialogs.confirm(...) 这些快捷 API 默认使用的对话框展示模式。
参数
| 参数 | 类型 | 可填值 | 默认值 |
|---|---|---|---|
value | string | app、overlay、foreground-or-overlay、app-or-overlay | app-or-overlay |
返回值
dialogs
这是一个可链式调用的方法。
真实说明
当前源码会把值标准化成下面 4 个之一:
| 你传入的值 | 最终保存值 |
|---|---|
app | app |
overlay | overlay |
foreground-or-overlay | foreground-or-overlay |
| 空值 | app-or-overlay |
除表中 4 个值外,其他字符串会抛出:
dialogs type 必须是 app、overlay、foreground-or-overlay 或 app-or-overlay
然后 show() 时会真的按这个值去挑宿主:
| 标准化后的值 | 当前真实行为 |
|---|---|
app | 强制走 Activity 弹窗;没有可用 Activity 就直接报错 |
overlay | 不使用 Activity:先尝试当前进程系统悬浮窗,失败后尝试无障碍 overlay |
foreground-or-overlay | 如果当前 Activity 在前台,就走 app;否则退到 overlay |
app-or-overlay | 当前实现同样只接受前台 Activity;不在前台时退到 overlay |
所以这已经不只是“保存一个 type 标记”了,而是会直接影响弹窗到底挂在哪种宿主上。
示例
dialogs.setDefaultDialogType("app");
dialogs.alert("提示", "后面的快捷弹窗都会继承这个默认 type 标记");
dialogs.alert(title?, content?, callback?)
最简单的提示框。
参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
title | string | 空 | 标题 |
content | string | 空 | 正文 |
callback | function | 不传 | 传了就走异步 |
返回值
| 用法 | 返回值 |
|---|---|
| 不传回调,普通脚本环境 | boolean |
不传回调,ui 页面环境 | Promise 风格对象 |
| 传回调 | null |
结果规则
| 操作 | 返回结果 |
|---|---|
| 点正按钮 | true |
| 取消 / 关闭 / 点返回 | false |
示例
const ok = dialogs.alert("提示", "脚本已经启动");
log(ok); // true 或 false
dialogs.alert("提示", "异步回调版", function () {
log("用户点掉了 alert");
});
dialogs.confirm(title?, content?, callback?)
确认框。
返回值
| 用法 | 返回值 |
|---|---|
| 不传回调 | boolean |
| 传回调 | null |
结果规则
| 操作 | 返回结果 |
|---|---|
| 点“确定” | true |
| 点“取消”、关闭、返回 | false |
示例
const confirmed = dialogs.confirm("删除", "确定要继续吗?");
if (confirmed) {
log("继续执行");
}
dialogs.confirm("删除", "确定要继续吗?").then(function (confirmed) {
if (confirmed) {
log("继续执行");
}
});
dialogs.rawInput(title?, prefill?, callback?)
弹一个输入框,确认时返回当前输入文本。
参数
| 参数 | 类型 | 说明 |
|---|---|---|
title | string | 标题 |
prefill | string | 预填文字 |
callback | function | 异步回调 |
返回值
| 用法 | 返回值 |
|---|---|
| 不传回调,普通脚本环境 | string | null |
不传回调,ui 页面环境 | Promise 风格对象 |
| 传回调 | null |
结果规则
- 点“确定”:返回输入框内容
- 取消 / 关闭:返回
null
示例
const text = dialogs.rawInput("请输入手机号", "138");
log(text);
dialogs.rawInput("请输入手机号", "138").then(function (text) {
log(text);
});
dialogs.input(title?, prefill?, callback?)
输入框快捷入口。
返回值
当前脚本层实现里,它和 dialogs.rawInput(...) 的结果规则一致:
- 确认时返回字符串
- 取消时返回
null
在 ui 页面里,不传回调时同样会返回 Promise 风格对象。
示例
const keyword = dialogs.input("搜索", "微信");
if (keyword != null) {
log(`keyword=${keyword}`);
}
dialogs.input("搜索", "微信").then(function (keyword) {
if (keyword != null) {
log(`keyword=${keyword}`);
}
});
dialogs.prompt(title?, prefill?, callback?)
dialogs.input(...) 的别名。
示例
const name = dialogs.prompt("昵称", "ScriptX");
dialogs.select(title?, items, callback?)
单次点击即完成的列表选择框。
参数
| 参数 | 类型 | 说明 |
|---|---|---|
title | string | 标题 |
items | array | 列表项 |
callback | function(index, item) | 可选 |
返回值
| 用法 | 返回值 |
|---|---|
| 不传回调 | number |
| 传回调 | null |
结果规则
| 情况 | 返回值 |
|---|---|
| 点中某项 | 该项索引 |
| 取消 / 关闭 | -1 |
回调参数
如果你传了回调,当前会回调:
function (index, item) {}
示例
const index = dialogs.select("请选择模式", ["正常", "快速", "调试"]);
log(index);
dialogs.select("请选择模式", ["正常", "快速"], function (index, item) {
log(`index=${index}, item=${item}`);
});
dialogs.singleChoice(title, items, selectedIndex?, callback?)
带单选态的列表框。
参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
title | string | 空 | 标题 |
items | array | [] | 列表项 |
selectedIndex | number | -1 | 初始选中项 |
callback | function(index, item) | 不传 | 异步回调 |
返回值
| 用法 | 返回值 |
|---|---|
| 不传回调 | number |
| 传回调 | null |
结果规则
- 点“确定”:返回当前选中的索引
- 取消 / 关闭:返回
-1
示例
const index = dialogs.singleChoice("主题", ["亮色", "暗色"], 1);
log(index);
dialogs.multiChoice(title, items, selectedIndices?, callback?)
多选列表框。
参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
title | string | 空 | 标题 |
items | array | [] | 列表项 |
selectedIndices | array<number> | [] | 初始选中索引 |
callback | function(indices, items) | 不传 | 异步回调 |
返回值
| 用法 | 返回值 |
|---|---|
| 不传回调 | number[] |
| 传回调 | null |
结果规则
- 点“确定”:返回排序后的索引数组
- 取消 / 关闭:返回空数组
[]
示例
const indices = dialogs.multiChoice("选择导出项", ["日志", "截图", "配置"], [0, 2]);
log(JSON.stringify(indices));
dialogs.build(options)
创建一个更可控的对话框实例。
参数总表
| 字段 | 类型 | 可填值 / 默认值 | 说明 |
|---|---|---|---|
title / heading | string | 空 | 标题 |
content / message / body | string | 空 | 正文 |
positive | string | 空 | 正按钮文案 |
negative | string | 空 | 负按钮文案 |
neutral | string | 空 | 中立按钮文案 |
titleColor | 颜色值 | 可解析颜色 | 标题颜色 |
contentColor | 颜色值 | 可解析颜色 | 正文颜色 |
itemsColor | 颜色值 | 可解析颜色 | 列表项文字颜色;已有子项和后续加入的子项都会应用 |
positiveColor | 颜色值 | 可解析颜色 | 正按钮文字色 |
negativeColor | 颜色值 | 可解析颜色 | 负按钮文字色 |
neutralColor | 颜色值 | 可解析颜色 | 中立按钮文字色 |
contentLineSpacing | number | 空 | 最终传给 TextView.setLineSpacing(add, 1f) 的附加间距 |
icon | Drawable | number | Image | Bitmap | string | 空 | 资源 id、位图、图片对象、或本地图片路径 |
items | array | [] | 列表项 |
itemsSelectMode | string | select / single / multi | 列表选择模式 |
itemsSelectedIndex | number | -1 | 单选初值 |
itemsSelectedIndices / selectedIndices | array<number> | [] | 多选初值 |
cancelable | boolean | true | 是否可取消 |
canceledOnTouchOutside | boolean | 默认跟随 cancelable | 点外部是否关闭 |
autoDismiss | boolean | true | 结果触发后是否自动关闭 |
inputHint | string | 空 | 输入框提示 |
inputPrefill | string | 空 | 输入框默认内容 |
checkBoxPrompt | string | 空 | 复选框文案 |
checkBoxChecked | boolean | false | 复选框默认状态 |
progress | number | object | 空 | 可直接传进度值,也可传 { progress, max, indeterminate } |
maxProgress | number | 空 | 最大进度 |
progressIndeterminate | boolean | false | 是否不定进度 |
customView | View | 空 | 自定义 Android View |
wrapInScrollView | boolean | false | 只包裹 customView |
stubborn | boolean | false | 为 true 时强制不可取消 |
type | string | app / overlay / foreground-or-overlay / app-or-overlay | 对话框类型标记 |
progress 对象写法适合把进度配置放在一起:
const dialog = dialogs.build({
title: "下载中",
progress: {
progress: 25,
max: 100,
indeterminate: false
}
});
其中 maxProgress 也可作为对象内 max 的别名。对象外的 maxProgress / progressIndeterminate 只在 progress 不是对象时读取。
type 到底怎么选
| 值 | 更适合什么 |
|---|---|
app | 你明确知道当前就有可用页面宿主,想把弹窗绑定在页面里 |
overlay | 你不想依赖前台 Activity,而是准备直接走悬浮窗 |
foreground-or-overlay | 有前台页面就贴页面,没有就退悬浮窗 |
app-or-overlay | 有可用 Activity 就优先 app,否则才退 overlay;这是默认值 |
type 需要配合什么前提
| 值 | 前提 |
|---|---|
app | 必须有可用 Activity |
overlay | 当前进程有 SYSTEM_ALERT_WINDOW,或 ScriptX 无障碍服务已连接并可创建无障碍窗口 |
foreground-or-overlay | 有前台 Activity 时走 app;否则必须满足上面的任一 overlay 条件 |
app-or-overlay | 当前实现和 foreground-or-overlay 的宿主选择相同 |
Hook 目标进程不能直接复用 ScriptX 主应用获得的系统悬浮窗权限,因为悬浮窗权限按 UID 授权。目标进程没有权限时,已连接的无障碍服务是可用的第二条显示路径。
itemsSelectMode 到底认哪些值
| 你传的值 | 最终模式 |
|---|---|
single / single_choice / singlechoice | single |
multi / multi_choice / multichoice | multi |
select / 空值 | select |
其他值会抛出 dialogs itemsSelectMode 必须是 select、single 或 multi,不会自动猜成 select。
布尔字段认哪些值
当前 dialogs 里的布尔解析比较宽:
| 写法 | 会被当成 |
|---|---|
true / 1 / "true" / "1" / "yes" / "on" | true |
false / 0 / "false" / "0" / "no" / "off" | false |
icon 支持哪些形式
| 写法 | 说明 |
|---|---|
| 资源 id 数字 | 当 Android drawable 资源 id 用 |
Drawable | 直接使用 |
Image | 取图片位图包装成 BitmapDrawable |
Bitmap | 直接包装成 BitmapDrawable |
| 本地路径字符串 | 文件存在才会加载,否则忽略 |
返回值
dialog
也就是一个 ScriptX 包装过的对话框实例对象。
示例
const dialog = dialogs.build({
title: "下载进度",
content: "正在准备资源",
positive: "完成",
negative: "取消",
progress: 10,
maxProgress: 100,
cancelable: false
});
对话框实例对象
下面这些方法都挂在 dialogs.build(...) 返回的 dialog 实例上。
dialog.show()
显示对话框。
返回值
dialog
注意
显示宿主由 type 决定。app 必须拿到前台 Activity;其余模式可以按上面的规则使用系统悬浮窗或无障碍 overlay。
dialog.dismiss()
关闭对话框。
返回值
dialog
dialog.isShowing()
当前是否还在显示。
返回值
boolean
dialog.getTitleView()
拿标题 View。
返回值
View | null
dialog.getContentView()
拿正文或你设置的自定义内容 View。
返回值
View | null
dialog.getInputEditText()
拿输入框控件。
返回值
EditText | null
dialog.getItems()
拿你传进去的 items 副本。
返回值
array
dialog.getProgress()
读当前进度值。
返回值
number
dialog.setProgress(value)
改当前进度。
参数
非数字会按 0 处理,小于 0 会被抬到 0。
返回值
dialog
dialog.getMaxProgress()
读最大进度。
返回值
number
dialog.setMaxProgress(value)
改最大进度。
返回值
dialog
dialog.setActionButton(action, title)
动态改按钮文案。
action 可填值
| 可填值 | 实际按钮 |
|---|---|
positive / ok / confirm | 正按钮 |
negative / cancel | 负按钮 |
neutral | 中立按钮 |
返回值
dialog
dialog.getActionButton(action)
拿某个按钮的真实 View。
返回值
View | null
dialog.on(eventName, listener)
给对话框实例挂一个持久监听。
返回值
dialog
dialog.once(eventName, listener)
挂一个一次性监听。
返回值
dialog
dialog.emit(eventName, ...args)
手动触发实例上的 JS 监听器。
返回值
boolean
说明
- 有监听器被触发时返回
true - 没监听器时返回
false
dialog.removeListener(eventName, listener)
移除某个监听器。
返回值
dialog
dialog.off(eventName, listener)
removeListener(...) 的别名。
dialog.removeAllListeners(eventName?)
移除全部监听器。
返回值
dialog
dialog.onResult(listener)
在最终结果落地时收到一次回调。
返回值
dialog
dialog.awaitResult()
阻塞等待最终结果。
返回值
结果类型取决于你构建的对话框内容和触发方式。实例还没 show() 时,awaitResult() 会先自动显示。
不能在 Android 主线程调用;源码会直接抛出:
dialog.awaitResult() 不能在 Android 主线程调用;请使用 onResult() 或快捷 API 返回的 Promise
如果一个 build() 对话框同时包含输入框、列表、复选框中的两种或更多,点正按钮时会返回组合对象,而不是只返回其中一个值:
{
action: "positive",
input: "输入内容",
selectedIndices: [0, 2],
checked: true
}
单选/普通列表使用 selectedIndex;多选使用 selectedIndices。只有一个交互区域时仍返回最直接的字符串、索引、索引数组或布尔值。
dialog 实例里常见的事件名
| 事件名 | 什么时候触发 | 参数 |
|---|---|---|
show | 对话框显示后 | 无 |
cancel | 被取消时 | 无 |
dismiss | 被关闭时 | 无 |
positive | 点正按钮时 | 无 |
negative | 点负按钮时 | 无 |
neutral | 点中立按钮时 | 无 |
item_select | select 模式点某项时 | index, item |
single_choice | 单选列表选择变化时 | index, item |
multi_choice | 多选变化时 | indices, items |
input_change | 输入框内容变化时 | text |
check | 复选框变化时 | boolean |
result | 最终结果确定时 | value |
例子
const dialog = dialogs.build({
title: "测试",
inputHint: "请输入",
positive: "确定",
negative: "取消"
});
dialog.on("input_change", function (text) {
log(`now=${text}`);
});
dialog.on("result", function (value) {
log(`result=${value}`);
});
dialog.show();
一段完整示例
const dialog = dialogs.build({
title: "导出设置",
content: "请选择需要导出的内容",
items: ["日志", "截图", "配置"],
itemsSelectMode: "multi",
itemsSelectedIndices: [0, 2],
positive: "开始导出",
negative: "取消",
progress: 0,
maxProgress: 100,
autoDismiss: false
});
dialog.on("multi_choice", function (indices, items) {
log(`indices=${JSON.stringify(indices)} items=${JSON.stringify(items)}`);
});
dialog.on("positive", function () {
dialog.setProgress(30);
});
dialog.onResult(function (result) {
log(`final=${JSON.stringify(result)}`);
});
dialog.show();
生命周期与高频输入边界
- 同一实例的一次显示周期只会结算一次结果;按钮、取消和关闭不会重复触发
result。 once(...)监听器在首次派发前就会从监听列表移除,避免异步回调尚未执行时被重复调度。input_change是“只保留最新值”的合并事件:输入很快时,中间值可能被合并,最终值不会丢。适合做预览或校验,不应把它当逐字符审计日志。- 脚本运行时停止时,仍在显示或等待中的对话框会被统一释放;之后再调用
show()/awaitResult()会报告运行时已停止。
