UI 示例项目模板
更新: 2025/7/20 字数: 0 字 时长: 0 分钟
组件和功能齐全 UI 界面模板(推荐)
在手机端 Bot.js 创建脚本项目时,你可以直接选择内置的 组件和功能齐全 UI 界面模板,快速搭建完整的 UI 页面。
✅ 快速上手 📦 组件丰富 🔧 可复用逻辑 🚀 高效开发
该模板包含:
- 常用 UI 组件(按钮、输入框、开关等)
- 各类弹窗(提示、确认)
- 权限申请与检查
- 悬浮窗菜单
- UI 配置持久化(自动保存/读取)
- 脚本运行日志输出
所有子模块都已封装完毕,可直接套用,无需重复造轮子。
项目创建时选择示意图
请确保你已在 Bot.js 中打开“新建项目”页面,并选择对应模板。
示例代码
项目根目录的脚本入口文件main.js
代码如下
js
"ui";
/* 导入 Java 类 */
importClass("android.content.pm.PackageManager");
importClass("android.provider.Settings");
importClass("android.widget.AdapterView");
/* 创建本地存储 用来保存数据 */
var 本地存储 = storages.create("UI模板");
/* 设置默认值 */
if (本地存储.get("floatyWindow") == undefined) {
本地存储.put("floatyWindow", false);
}
if (本地存储.get("windowX") == undefined) {
本地存储.put("windowX", -20);
}
if (本地存储.get("windowY") == undefined) {
本地存储.put("windowY", device.height / 2);
}
if (本地存储.get("QQ") == undefined) {
本地存储.put("QQ", "2874662633");
}
if (本地存储.get("单行文本输入框") == undefined) {
本地存储.put("单行文本输入框", "我是单行文本输入框的默认文本");
}
if (本地存储.get("多行文本输入框") == undefined) {
本地存储.put("多行文本输入框", "我是多行文本输入框的默认文本\n我是第二行文本");
}
if (本地存储.get("数字输入框") == undefined) {
本地存储.put("数字输入框", 888);
}
if (本地存储.get("密码输入框") == undefined) {
本地存储.put("密码输入框", "");
}
if (本地存储.get("下拉菜单") == undefined) {
本地存储.put("下拉菜单", 0);
}
if (本地存储.get("单选框1") == undefined) {
本地存储.put("单选框1", false);
}
if (本地存储.get("单选框2") == undefined) {
本地存储.put("单选框2", true);
}
if (本地存储.get("单选框3") == undefined) {
本地存储.put("单选框3", false);
}
if (本地存储.get("复选框1") == undefined) {
本地存储.put("复选框1", true);
}
if (本地存储.get("复选框2") == undefined) {
本地存储.put("复选框2", true);
}
if (本地存储.get("复选框3") == undefined) {
本地存储.put("复选框3", false);
}
if (本地存储.get("进度条") == undefined) {
本地存储.put("进度条", 100);
}
var window;
var isRun = null;
ui.layout(
<vertical>
<appbar bg="#000000">
<toolbar id="toolbar" title="Bot.js系统-专业的自动化开发工具" />
</appbar>
<viewpager id="viewPager" layout_weight="1">
<frame>
<vertical>
<ScrollView h="auto" layout_weight="25">
<vertical h="auto" padding="10 5" layout_weight="25">
<card margin="3 1" cardElevation="0" cardCornerRadius="10" cardBackgroundColor="#e9e9e9" alpha="1">
<card margin="1 1" w="*" cardElevation="0" cardCornerRadius="10" cardBackgroundColor="#e9e9e9">
<vertical margin="5 10 5 10">
<text text="基本信息" padding="10 4" textSize="22" textColor="#222222" marginBottom="5px" />
<horizontal padding="10 4">
<text
text="屏幕宽度"
textColor="#222222"
textSize="14"
h="*"
w="400px"
gravity="left|center"
layout_weight="1"
/>
<text
id="屏幕宽度"
text=""
textColor="#222222"
textSize="14"
h="*"
w="*"
gravity="left|center"
layout_weight="2"
/>
</horizontal>
<horizontal padding="10 4">
<text
text="屏幕高度"
textColor="#222222"
textSize="14"
h="*"
w="400px"
gravity="left|center"
layout_weight="1"
/>
<text
id="屏幕高度"
text=""
textColor="#222222"
textSize="14"
h="*"
w="*"
gravity="left|center"
layout_weight="2"
/>
</horizontal>
<horizontal padding="10 4">
<text
text="当前版本号"
textColor="#222222"
textSize="14"
h="*"
w="400px"
gravity="left|center"
layout_weight="1"
/>
<text
id="当前版本号"
text=""
textColor="#222222"
textSize="14"
h="*"
w="*"
gravity="left|center"
layout_weight="2"
/>
</horizontal>
</vertical>
</card>
</card>
<card margin="3 1" cardElevation="0" cardCornerRadius="10" cardBackgroundColor="#e9e9e9" alpha="1">
<card margin="1 1" w="*" cardElevation="0" cardCornerRadius="10" cardBackgroundColor="#e9e9e9">
<vertical margin="5 10 5 10">
<text text="权限设置" padding="10 4" textSize="22" textColor="#222222" marginBottom="5px" />
<horizontal padding="10 4">
<vertical layout_weight="1">
<text text="无障碍" textColor="#222222" textStyle="bold" textSize="13" />
<text text="提供自动操作(点击,长按,滑动等)" textColor="#222222" textSize="10" marginTop="2" />
</vertical>
<Switch id="autoService" textSize="13" checked="true" marginRight="-2" marginTop="3" />
</horizontal>
<horizontal padding="10 4">
<vertical layout_weight="1">
<text text="悬浮窗权限" textColor="#222222" textSize="13" textStyle="bold" />
<text text="软件运行的必开权限" textColor="#222222" textSize="10" marginTop="2" />
</vertical>
<Switch id="floatyPermission" checked="true" marginRight="-2" marginTop="3" />
</horizontal>
<horizontal padding="10 4">
<vertical layout_weight="1">
<text text="忽略电池优化" textColor="#222222" textSize="13" textStyle="bold" />
<text text="软件运行的必开权限(不优化)" textColor="#222222" textSize="10" marginTop="2" />
</vertical>
<Switch id="battery" checked="true" marginRight="-2" marginTop="3" />
</horizontal>
<horizontal padding="10 4">
<vertical layout_weight="1">
<text text="稳定模式" textColor="#222222" textSize="13" textStyle="bold" />
<text text="切换后需要重写打开无障碍" textColor="#222222" textSize="10" marginTop="2" />
</vertical>
<Switch id="stableMode" textSize="13" checked="true" marginRight="-2" marginTop="3" />
</horizontal>
<horizontal padding="10 4">
<vertical layout_weight="1">
<text text="控制台" textColor="#222222" textSize="13" textStyle="bold" />
<text
text="调试输出,悬浮窗显示日志(推荐使用可以手势穿透的悬浮窗日志)"
textColor="#222222"
textSize="10"
marginTop="2"
/>
</vertical>
<Switch id="screenCapturePermission" textSize="13" checked="false" marginRight="-2" marginTop="3" />
</horizontal>
<horizontal padding="10 4">
<vertical layout_weight="1">
<text text="悬浮菜单" textColor="#222222" textSize="13" textStyle="bold" />
<text text="控制脚本任务的运行和结束" textColor="#222222" textSize="10" marginTop="2" />
</vertical>
<Switch id="floatyWindow" textSize="13" checked="true" marginRight="-2" marginTop="3" />
</horizontal>
<horizontal padding="10 4">
<vertical layout_weight="1">
<text text="前台服务" textColor="#222222" textSize="13" textStyle="bold" />
<text text="保证脚本不被杀掉(前台服务)" textColor="#222222" textSize="10" marginTop="2" />
</vertical>
<Switch id="foregroundService" textSize="13" checked="true" marginRight="-2" marginTop="3" />
</horizontal>
<horizontal padding="10 4">
<vertical id="VideoTutorials" layout_weight="1">
<text text="使用说明" textColor="#222222" textSize="13" textStyle="bold" />
<text text="看第一代api的视频教程进行修改" textColor="#222222" textSize="10" marginTop="2" />
</vertical>
<img
w="23"
h="23"
layout_gravity="right|center"
src="@drawable/ic_keyboard_arrow_right_black_48dp"
tint="#222222"
marginLeft="-4"
/>
</horizontal>
</vertical>
</card>
</card>
</vertical>
</ScrollView>
</vertical>
</frame>
<frame>
<vertical>
<ScrollView h="auto" layout_weight="25">
<vertical margin="5">
<horizontal h="auto" w="*">
<text text="单行文本输入框:" textColor="black" textSize="15sp" gravity="center" w="auto" />
<input id="单行文本输入框" gravity="left" textSize="15sp" layout_weight="1" lines="1" hint="请输入单行文本……" />
</horizontal>
<vertical h="auto" w="*">
<text text="多行文本输入框:" textColor="black" textSize="15sp" gravity="center" w="auto" />
<input
id="多行文本输入框"
w="*"
h="80"
maxLines="5"
hint="请输入文本……"
background="#ffffff"
textColor="#1c1c1c"
textColorHint="#afb0ad"
padding="5 0 5 0"
gravity="left "
margin="2"
textSize="15sp"
layout_weight="1"
/>
</vertical>
<horizontal h="auto" w="*">
<text text="数字输入框:" textColor="black" textSize="15sp" gravity="center" w="auto" />
<input
id="数字输入框"
inputType="number"
text="600"
gravity="left"
textSize="15sp"
hint="请输入数字……"
layout_weight="1"
/>
</horizontal>
<horizontal h="auto" w="*">
<text text="密码输入框:" textColor="black" textSize="15sp" gravity="center" w="auto" />
<input id="密码输入框" gravity="left" textSize="15sp" layout_weight="1" hint="请输入密码……" password="true" />
</horizontal>
<horizontal>
<text text="下拉菜单组件:" textColor="black" textSize="15sp" gravity="center" w="auto" />
<spinner
id="下拉菜单"
entries="下拉菜单选项1|下拉菜单选项2|下拉菜单选项3"
layout_weight="1"
spinnerMode="dialog"
/>
</horizontal>
<radiogroup id="单选框组合">
<radio id="单选框1" text="单选框1" />
<radio id="单选框2" text="单选框2" />
<radio id="单选框3" text="单选框3" />
</radiogroup>
<horizontal h="auto">
<text text="复选框组件:" textColor="black" textSize="15sp" marginTop="2" w="auto" />
<checkbox id="复选框1" text="复选框1" textSize="15sp" checked="true" />
<checkbox id="复选框2" text="复选框2" textSize="15sp" checked="false" />
<checkbox id="复选框3" text="复选框3" textSize="15sp" checked="true" />
</horizontal>
<horizontal h="*">
<text text="进度条组件:" textColor="black" textSize="15sp" marginTop="2" w="auto" />
<seekbar id="进度条" max="200" min="10" w="*" />
</horizontal>
<card
w="*"
h="auto"
marginTop="10"
padding="5"
cardElevation="0"
cardCornerRadius="10"
cardBackgroundColor="#e9e9e9"
>
<vertical h="auto" gravity="center" marginTop="1" padding="10">
<text text="这只是一个常用UI组件模板" textColor="red" textSize="12sp" gravity="center" />
<text
text="包含组件的点击事件、初始化配置、保存配置、执行脚本、终止脚本、悬浮菜单控制脚本执行……"
textColor="blue"
textSize="12sp"
gravity="left"
/>
<text
text="你要做的就是修改、删除、添加即可,将自己的代码封装成一个函数,放在主程序函数体里"
textColor="blue"
textSize="12sp"
gravity="left"
/>
</vertical>
</card>
<horizontal h="30"></horizontal>
</vertical>
</ScrollView>
<horizontal w="*" h="auto" layout_gravity="bottom">
<button id="start" text="开始" layout_weight="1" style="Widget.AppCompat.Button.Colored" />
<button id="save" text="保存配置" layout_weight="1" style="Widget.AppCompat.Button.Colored" />
<button id="stop" text="停止" layout_weight="1" style="Widget.AppCompat.Button.Colored" />
</horizontal>
</vertical>
</frame>
<frame>
<globalconsole id="globalconsole" w="*" h="*" />
<fab
id="fab"
src="@drawable/ic_close_black_48dp"
w="auto"
h="auto"
layout_gravity="bottom|right"
margin="16"
tint="#ffffff"
/>
</frame>
</viewpager>
<bottomnaviagtion id="navigation" bg="#ffffff" />
</vertical>
);
ui.globalconsole.setColor("D", "#000000");
/* 设置底部导航栏的内容 */
let menuItems = [];
let menu = ui.navigation.menu;
menuItems.push(buildMenuItem(menu, "权限设置", ui.R.drawable.ic_build_black_48dp));
menuItems.push(buildMenuItem(menu, "脚本配置", ui.R.drawable.ic_view_comfy_black_48dp));
menuItems.push(buildMenuItem(menu, "运行日志", ui.R.drawable.ic_description_black_48dp));
/* 当底部按钮被选中时,切换ViewPager页面为相应位置的页面 */
ui.navigation.setOnNavigationItemSelectedListener(function (item) {
ui.viewPager.currentItem = menuItems.indexOf(item);
return true;
});
/* 当ViewPager页面切换时,切换底部按钮的状态 */
ui.viewPager.addOnPageChangeListener(
new Packages.androidx.viewpager.widget.ViewPager.OnPageChangeListener({
onPageSelected: function (position) {
menuItems[position].setChecked(true);
},
})
);
/* fab点击时清空全局日志界面 */
ui.fab.on("click", () => {
ui.globalconsole.clear();
});
/* 创建选项菜单(右上角) */
ui.emitter.on("create_options_menu", (menu) => {
menu.add("清空日志文件");
menu.add("官网");
menu.add("帮助");
menu.add("关于");
menu.add("客服");
});
/* 监听选项菜单点击 */
ui.emitter.on("options_item_selected", (e, item) => {
switch (item.getTitle()) {
case "清空日志文件":
console.clear();
ui.globalconsole.clear();
break;
case "官网":
/*跳转 浏览器打开网址 */
app.openUrl("http://botdevhub.com");
break;
case "帮助":
alert("帮助", "遇到问题联系客服");
break;
case "关于":
$dialogs.setDefaultDialogType("foreground-or-overlay");
dialogs
.build({
title: "关于UI",
content: "CloudControl Pro 内测版",
positive: "确定",
titleColor: "#000000",
positiveColor: "#000000",
})
.on("negative", function () {
console.log("negative");
})
.show();
break;
case "客服":
try {
/* 此处应该先判断手机有没有安装QQ应用,有QQ应用再跳转 否则会报错*/
var appName = "QQ";
/* 判断手机上是否已经安装了这个应用 */
if (app.getPackageName(appName) != null) {
app.startActivity({
action: "android.intent.action.VIEW",
data: "mqq://im/chat?chat_type=wpa&version=1&src_type=web&uin=" + 本地存储.get("QQ"),
packageName: "com.tencent.mobileqq",
});
} else {
/*每安装,就弹出提示对话框 */
alert("温馨提示", "你未安装此应用,请先下载安装:" + appName + ",然后就可以联系QQ客服了");
console.log("请下载安装" + appName);
}
} catch (error) {
console.error(error);
}
break;
}
e.consumed = true;
});
activity.setSupportActionBar(ui.toolbar);
function buildMenuItem(menu, title, icon) {
let menuItem = menu.add(title);
menuItem.setIcon(icon);
return menuItem;
}
/* *************************************下面代码是权限开关按钮的点击事件监听******************************************* */
refreshState();
/* 当用户回到本界面时,resume事件会被触发 */
ui.emitter.on("resume", function () {
refreshState();
});
/* 刷新UI权限状态 */
function refreshState() {
ui.autoService.checked = auto.service != null;
ui.floatyPermission.checked = floaty.checkPermission();
ui.foregroundService.checked = $settings.isEnabled("foreground_service");
ui.stableMode.checked = $settings.isEnabled("stable_mode");
try {
let pm = context.getSystemService(context.POWER_SERVICE);
ui.battery.checked = pm.isIgnoringBatteryOptimizations(context.getPackageName());
} catch (e) {
toastLog("当前系统无法读取是否忽略省电策略,请在系统设置中设置即可");
ui.battery.checked = false;
}
}
ui.autoService.on("check", function (checked) {
setAutoService(checked);
});
ui.floatyPermission.on("check", (isChecked) => {
if (isChecked && !new android.provider.Settings().canDrawOverlays(context)) {
importClass(android.content.Intent);
importClass(android.net.Uri);
var intent = new Intent(
android.provider.Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + context.getPackageName())
);
app.startActivity(intent);
}
if (isChecked && new android.provider.Settings().canDrawOverlays(context)) {
// toastLog("请手动关闭");
}
});
ui.battery.on("check", function (checked) {
if (checked) {
/* 跳转到电池优化设置界面 */
let intent = new Intent();
intent.setAction("android.settings.IGNORE_BATTERY_OPTIMIZATION_SETTINGS");
intent.putExtra("extra_pkgname", context.getPackageName());
app.startActivity(intent);
}
});
ui.stableMode.on("check", function (checked) {
if (checked) {
/*这个设置后需要重启下无障碍 */
$settings.setEnabled("stable_mode", checked);
toastLog("需重新打开无障碍");
ui.autoService.setChecked(false);
}
});
ui.screenCapturePermission.on("check", function (checked) {
threads.start(function () {
if (checked) {
console.show();
} else {
console.hide();
}
});
});
ui.floatyWindow.on("check", function (checked) {
本地存储.put("floatyWindow", checked);
if (checked) {
if (window) {
window.close();
}
threads.start(function () {
ShowFrame();
});
} else if (window) {
window.close();
window = null;
}
});
ui.foregroundService.on("check", function (checked) {
$settings.setEnabled("foreground_service", checked);
});
ui.VideoTutorials.on("click", () => {
/* 对话框 随意修改颜色 内容 按钮事件 */
dialogs
.build({
title: "Bot.js有全套的视频教程🎈🎈✨🎉",
titleColor: "#b2f400",
content: "教程不断更新\n" + "🏃🏃🏄🏊💃🚶🏀🏈🎾🏁 \n" + "Bot.js App 永久免费使用!\n遇到问题联系客服",
contentColor: "#12f8bf",
cancelable: true,
positive: "确认",
positiveColor: "#2a7fff",
neutral: "取消",
neutralColor: "#d1d1d1",
negative: "其他",
negativeColor: "#2bff44",
})
.on("positive", () => {
toast("确认的事件处理");
console.log("打开 Bot.js 官网");
app.openUrl("http://jsdevhub.com");
})
.on("negative", () => {
toastLog("其他的事件处理");
})
.on("neutral", () => {
toastLog("取消的事件处理");
})
.show();
});
/*在UI线程不能进行阻塞操作,涉及到阻塞的操作务必在子线程中进行 */
threads.start(function () {
if (本地存储.get("floatyWindow")) {
ShowFrame();
}
/* 开启子线程判断是否有悬浮窗权限,没有的话就跳转并终止脚本 */
if (!$floaty.checkPermission()) {
toastLog("本软件需要悬浮窗权限来显示悬浮窗,请在随后的界面中允许并重新运行本软件。");
$floaty.requestPermission();
exit();
}
/* 网络操作也需要在子线程中进行,比如初始化配置时,有的配置数据需要从服务器获取数据,然后更新到UI上 */
});
/* *************************************在UI线程中更新配置,每次运行脚本时将之前保存的配置更新到UI界面中******************************************* */
ui.run(() => {
/* 通过组件的ID修改值的几种方式 */
ui.floatyWindow.checked = 本地存储.get("floatyWindow");
ui.屏幕宽度.attr("text", device.width);
ui.屏幕高度.attr("text", device.height);
ui.当前版本号.attr("text", app.versionCode);
ui.单行文本输入框.attr("text", 本地存储.get("单行文本输入框"));
ui.多行文本输入框.setText(本地存储.get("多行文本输入框"));
ui.数字输入框.setText(String(本地存储.get("数字输入框"))); /* 如果是数字类型 需要转换为字符串类型 */
ui.密码输入框.setText(本地存储.get("密码输入框"));
ui.下拉菜单.setSelection(本地存储.get("下拉菜单"));
ui.单选框1.checked = 本地存储.get("单选框1");
ui.单选框2.checked = 本地存储.get("单选框2");
ui.单选框3.checked = 本地存储.get("单选框3");
ui.复选框1.checked = 本地存储.get("复选框1");
ui.复选框2.checked = 本地存储.get("复选框2");
ui.复选框3.attr("checked", 本地存储.get("复选框3"));
ui.进度条.setProgress(本地存储.get("进度条"));
});
/* *************************************下面代码是各种组件的事件监听******************************************* */
/* 下拉菜单组件的监听事件 */
ui.下拉菜单.setOnItemSelectedListener(
new AdapterView.OnItemSelectedListener({
onItemSelected: function (parent, view, pos, id) {
console.log("你选择了:" + view.attr("text"));
},
onNothingSelected: function (parent) {
console.log("detached" + parent);
},
})
);
/* 单选框组件的监听事件 */
ui.单选框组合.setOnCheckedChangeListener((group, checkedId) => {
// 根据整数id获取勾选的radio控件
let checkedRadio = ui.单选框组合.findViewById(checkedId);
switch (checkedRadio) {
case ui["单选框1"]:
toastLog("单选框1被勾选");
break;
case ui["单选框2"]:
toastLog("单选框2被勾选");
break;
case ui["单选框3"]:
toastLog("单选框3被勾选");
break;
default:
toastLog("没有任何单选框被勾选");
break;
}
});
/* 监听单个单选组件的选中 */
ui.单选框1.on("check", (checked) => {
if (checked) {
toastLog("单选框1被勾选了");
} else {
toastLog("单选框1被取消勾选了");
}
});
ui.单选框2.on("check", (checked) => {
if (checked) {
toastLog("单选框2被勾选了");
} else {
toastLog("单选框2被取消勾选了");
}
});
ui.单选框3.on("check", (checked) => {
if (checked) {
toastLog("单选框3被勾选了");
} else {
toastLog("单选框3被取消勾选了");
}
});
/* 复选框组件的监听事件 */
ui.复选框1.on("check", (checked) => {
if (checked) {
toast("复选框1被勾选了");
} else {
toast("复选框1被取消勾选了");
}
});
ui.复选框2.on("check", (checked) => {
if (checked) {
toast("复选框2被勾选了");
} else {
toast("复选框2被取消勾选了");
}
});
ui.复选框3.on("check", (checked) => {
if (checked) {
toast("复选框3被勾选了");
} else {
toast("复选框3被取消勾选了");
}
});
/* 进度条组件的监听事件 */
ui.进度条.setOnSeekBarChangeListener({
onProgressChanged: (seekbar, progress, fromUser) => {
console.log("进度: ", progress);
},
});
/* 开始按钮的点击事件 */
ui.start.on("click", (c) => {
start();
});
/* 保存按钮的点击事件 */
ui.save.on("click", (c) => {
var res = 配置效验并保存();
if (res) {
toastLog("配置保存成功");
} else {
toastLog("配置保存失败");
}
});
/* 停止按钮的点击事件 */
ui.stop.on("click", (c) => {
stop();
});
function 配置效验并保存() {
try {
/* 通过组件的ID获取值 并进行效验 是否符合开发者的要求*/
if (ui.单行文本输入框.text().length === 0) {
toastLog("单行文本输入框必须要输入内容");
return false;
}
if (ui.数字输入框.text().length === 0) {
toastLog("数字输入框必须要输入数字");
return false;
}
/* 获取单选框组合勾选的单选框ID */
let checkedId = ui.单选框组合.getCheckedRadioButtonId();
if (checkedId === -1) {
toastLog("没有任何单选框被勾选,必须勾选一个");
return false;
} else {
/*根据id获取勾选的单选框 */
let checkedRadio = ui.单选框组合.findViewById(checkedId);
/*获取勾选的位置 */
let checkedIndex = ui.单选框组合.indexOfChild(checkedRadio);
toastLog("当前勾选的单选框的文本: " + checkedRadio.getText().toString() + ", 位置: " + checkedIndex);
}
/* 通过组件的ID获取值的几种方式 并将值保存到本地存储*/
本地存储.put("单行文本输入框", ui.单行文本输入框.text());
本地存储.put("多行文本输入框", ui.多行文本输入框.text());
var 数字输入框 = parseInt(ui.数字输入框.text()); /* text()的到的是字符串,需要将字符串转换为整数 */
if (!isNaN(数字输入框)) {
本地存储.put("数字输入框", 数字输入框); /* 保存数字类型 */
} else {
console.log("非数字字符串,不保存");
return false;
}
本地存储.put("密码输入框", ui.密码输入框.attr("text"));
本地存储.put("下拉菜单", ui.下拉菜单.getSelectedItemPosition());
本地存储.put("单选框1", ui.单选框1.checked);
本地存储.put("单选框2", ui.单选框2.checked);
本地存储.put("单选框3", ui.单选框3.checked);
本地存储.put("复选框1", ui.复选框1.checked);
本地存储.put("复选框2", ui.复选框2.checked);
本地存储.put("复选框3", ui.复选框3.checked);
本地存储.put("进度条", ui.进度条.getProgress());
return true;
} catch (error) {
/* 处理异常的代码块 */
console.error(error);
return null;
}
}
function start() {
var res = 配置效验并保存();
if (!res) {
console.log("配置效验失败,不符合要求");
return false;
}
toastLog("如果你的程序以无障碍模式实现自动化 ,请需要在程序开始运行之前判断无障碍服务");
if (auto.service == null) {
toastLog("请先开启无障碍服务!");
app.startActivity({
action: "android.settings.ACCESSIBILITY_SETTINGS",
});
return;
}
/* 判断是否已经运行,如果已经运行了就先结束后再开始运行 */
if (isRun) {
console.log("已经运行,先结束后再开始运行");
isRun.interrupt();
console.hide();
threads.shutDownAll();
}
/*在UI线程不能进行阻塞操作,涉及到阻塞的操作务必在子线程中进行 所以需要开启一个子线程运行 */
isRun = threads.start(function () {
/* 在子线程里启动主程序 */
主程序();
});
}
function stop() {
if (isRun) {
toastLog("停止运行");
isRun.interrupt();
console.hide();
threads.shutDownAll();
isRun = null;
} else {
toastLog("未运行,无需终止");
}
}
/*注册broadcast广播 接收悬浮窗发来的广播 */
events.broadcast.on("FloatingWindow", function (type) {
if (type == "开始运行") {
start();
} else {
stop();
}
});
/* *************************************主程序函数 将你的代码入口放入主程序内即可******************************************* */
function 主程序() {
toastLog("如果你的脚本需要截图和文字识别需要申请截图权限");
申请截图权限();
sleep(1000);
toastLog("你好");
sleep(1000);
toastLog("请把你的脚本代码放在此处");
sleep(1000);
toastLog("我运行完了");
}
/* *************************************最简单的一个悬浮窗按钮来控制脚本运行和结束******************************************* */
function ShowFrame() {
window = floaty.window(
<frame>
<com.google.android.material.floatingactionbutton.FloatingActionButton
layout_width="wrap_content"
backgroundTint="#000000"
tint="#FFFFFFFF"
id="action"
layout_height="wrap_content"
src="@drawable/ic_play_arrow_black_48dp"
/>
<img id="me" w="50" h="50" padding="16" src="@drawable/ic_play_arrow_black_48dp" tint="#FFFFFFFF" />
</frame>
);
/* 设置悬浮窗位置*/
window.setPosition(本地存储.get("windowX"), 本地存储.get("windowY"));
/* 记录按键被按下时的触摸坐标 */
var x = 0,
y = 0;
/* 记录按键被按下时的悬浮窗位置 */
var windowX, windowY;
/* 记录按键被按下的时间以便判断长按等动作 */
var downTime;
window.action.setAlpha(0.5);
window.action.setOnTouchListener(function (view, event) {
switch (event.getAction()) {
case event.ACTION_DOWN:
x = event.getRawX();
y = event.getRawY();
windowX = window.getX();
windowY = window.getY();
downTime = new Date().getTime();
return true;
case event.ACTION_MOVE:
//移动手指时调整悬浮窗位置
var positionX = windowX + (event.getRawX() - x);
var positionY = windowY + (event.getRawY() - y);
本地存储.put("windowX", positionX);
本地存储.put("windowY", positionY);
window.setPosition(positionX, positionY);
/* 如果按下的时间超过1.5秒判断为长按,退出脚本 */
if (new Date().getTime() - downTime > 1500) {
exit();
}
return true;
case event.ACTION_UP:
//手指弹起时如果偏移很小则判断为点击
if (Math.abs(event.getRawY() - y) < 5 && Math.abs(event.getRawX() - x) < 5) {
onClick();
}
return true;
}
return true;
});
}
function onClick() {
let getIcon = window.action.attr("src");
let itemColor = [
["#000000", "#FFFFFFFF"],
["#FF6666", "#FFFFFFFF"],
];
let itemIcon = ["@drawable/ic_play_arrow_black_48dp", "@drawable/ic_stop_black_48dp"];
let setIcon = itemIcon[getIcon == itemIcon[0] ? 1 : 0];
window.action.attr("src", setIcon);
window.action.attr("backgroundTint", itemColor[getIcon == itemIcon[0] ? 1 : 0][0]);
window.action.attr("tint", itemColor[getIcon == itemIcon[0] ? 1 : 0][1]);
if (getIcon == itemIcon[0] ? 1 : 0) {
/* 发送一个广播 */
events.broadcast.emit("FloatingWindow", "开始运行");
} else {
/* 发送一个广播 */
events.broadcast.emit("FloatingWindow", "停止运行");
}
}
/* *************************************下面代码无需修改******************************************* */
/*注意: 一个app里同时只能有一个js脚本文件使用截图权限*/
function 申请截图权限() {
images.stopScreenCapture();
if (!requestScreenCapture()) {
alert("请求截图失败,请重新开始");
console.hide();
threads.shutDownAll();
exit();
} else {
toastLog("请求截屏成功");
}
}
function setAutoService(checked) {
if (checked) {
if (checkPermission("android.permission.WRITE_SECURE_SETTINGS")) {
openAccessibility();
} else {
if ($shell.checkAccess("adb")) {
shell("pm grant " + context.getPackageName() + " android.permission.WRITE_SECURE_SETTINGS", {
adb: true,
});
toastLog("adb授权成功");
openAccessibility();
} else {
if ($shell.checkAccess("root")) {
shell("pm grant " + context.getPackageName() + " android.permission.WRITE_SECURE_SETTINGS", {
root: true,
});
toastLog("root授权成功");
openAccessibility();
} else {
console.info("\n也可使用WRITE_SECURE_SETTINGS权限开启无障碍服务\n授权代码已复制,使用adb激活");
setClip("adb shell pm grant " + context.getPackageName() + " android.permission.WRITE_SECURE_SETTINGS");
/* 用户勾选无障碍服务的选项时,跳转到页面让用户去开启 */
app.startActivity({
action: "android.settings.ACCESSIBILITY_SETTINGS",
});
}
}
}
} else if (auto.service != null) {
auto.service.disableSelf();
}
}
function openAccessibility() {
let mServices = ":" + context.getPackageName() + "/com.template.cn.AccessibilityService";
let enabledServices = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
enabledServices = enabledServices ? enabledServices.replace(new RegExp(mServices, "g"), "") : "";
Settings.Secure.putString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
Settings.Secure.putString(
context.getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
enabledServices + mServices
);
}
function checkPermission(permission) {
pm = context.getPackageManager();
return PackageManager.PERMISSION_GRANTED == pm.checkPermission(permission, context.getPackageName().toString());
}