牙叔教程 简单易懂
隐藏导航栏
-- 惜缘
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
隐藏标题栏
-- 大柒
activity.requestWindowFeature(Window.FEATURE_NO_TITLE);
img设置自带图标
let RID = resources.getIdentifier("ic_accessibility_black_48dp", "drawable", context.getPackageName()); view.setImageResource(RID);
全屏
-- 大柒
"ui"; importClass(android.view.WindowManager); full(true); //是否全屏 function full(enable) { if (enable) { //设置全屏 let lp = activity.getWindow().getAttributes(); lp.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; activity.getWindow().setAttributes(lp); activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); } else { //取消全屏 let attr = activity.getWindow().getAttributes(); attr.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN; activity.getWindow().setAttributes(attr); activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); } }
全屏
--惜缘
"ui"; importClass(android.view.WindowManager); importClass(android.view.View); activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); activity .getWindow() .getDecorView() .setSystemUiVisibility( android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR | android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | android.view.View.SYSTEM_UI_FLAG_FULLSCREEN ); ui.layout( <vertical> <img layout_gravity="center" id="img" w="*" h="*" bg="#0000ff" scaleType="centerCrop" src="file://./111.jpg" /> </vertical> );
彩图转灰度图
images.cvtColor(img, "RGBA2GRAY");
手机底部导航栏高度
-- 大柒
"ui"; var realPoint = new android.graphics.Point(); activity.getWindowManager().getDefaultDisplay().getRealSize(realPoint); var height = activity.getWindowManager().getDefaultDisplay().getHeight(); var navigation_bar_height = realPoint.y - height; console.log("navigation_bar_height", navigation_bar_height);
设置Material主题
-- ╰つ゛无名大姐
activity.theme.applyStyle(ui.R.style["Base.V14.Theme.Material3.Dark"], true);
"ui"; context.theme.applyStyle(ui.R.style["Base.V14.Theme.Material3.Dark"], true); ui.layout( <vertical> <vertical w="*" h="auto" id="ver"></vertical> </vertical> ); importClass("com.google.android.material.textfield.TextInputLayout"); aa = new TextInputLayout(context); aa.setHint("请输入用户名"); aa.addView(new android.widget.EditText(context)); ui.ver.addView(aa); aa = new TextInputLayout(context); aa.setHint("请输入密码"); aa.addView(new android.widget.EditText(context)); ui.ver.addView(aa);
修改按钮背景色
-- 抠脚
-- 大柒
view.getBackground().setColorFilter(colors.RED, PorterDuff$Mode.MULTIPLY); 或者 view.attr('backgroundTint',colorstr);
打开文件选择器
intent = new Intent(Intent.ACTION_GET_CONTENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("image/*"); activity.startActivityForResult(Intent.createChooser(intent, "选择图片"), 1);
指定参数类型
// 方法 指定参数类型 p["setShadowLayer(float,float,float,int)"](100, 0, 0, new java.lang.Long(Color.RED)); p["setShadowLayer(float,float,float,int)"](100, 0, 0, $colors.RED); // 构造函数 指定参数类型 let radialGradient = new RadialGradient["(float,float,float,int[],float[],android.graphics.Shader$TileMode)"]( centerX, centerY, radius, radialColors, positionList, Shader.TileMode.REPEAT );
判断手机32还是64位
importClass(java.lang.System); importClass(android.os.Build); let mCPU = Build.CPU_ABI == "arm64-v8a" ? "64位" : "32位"; let mCPU2 = System.getProperty("os.arch").indexOf("64") != -1 ? "64位" : "32位"; log(mCPU + " " + mCPU2);
java类是否有某个字段
function hasField(classFullName, fieldName) { classFullName = classFullName || "android.view.WindowManager$LayoutParams"; fieldName = fieldName || "LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES"; let clazz = java.lang.Class.forName(classFullName); let fields = clazz.getDeclaredFields(); let bool = false; for (let i = 0; i < fields.length; i++) { if (fields[i].getName().equals(fieldName)) { bool = true; break; } } return bool; }
内置图标转drawable
const resources = context.getResources(); let imgId = resources.getIdentifier("ic_keyboard_arrow_left_black_48dp", "drawable", context.getPackageName()); let drawable = resources.getDrawable(imgId);
获取当前包名
-- @Ai:下个版本修复不受限制的问题
const _current_pacakge = currentPackage; let currentPackage = function () { let start = new Date().getTime(); try { if (!runtime.getAccessibilityBridge()) { return _current_pacakge(); } // 通过windowRoot获取根控件的包名,理论上返回一个 速度较快 let windowRoots = runtime.getAccessibilityBridge().windowRoots(); if (windowRoots && windowRoots.size() > 0) { log(["windowRoots size: {}", windowRoots.size()]); for (let i = 0; i < windowRoots.size(); i++) { let root = windowRoots.get(i); if (root !== null && root.getPackageName()) { return root.getPackageName(); } } } // windowRoot获取失败了通过service.getWindows获取根控件的包名,按倒序从队尾开始获取 速度相对慢一点 let service = runtime.getAccessibilityBridge().getService(); let serviceWindows = service !== null ? service.getWindows() : null; if (serviceWindows && serviceWindows.size() > 0) { log(["windowRoots未能获取包名信息,尝试service window size: {}", serviceWindows.size()]); for (let i = serviceWindows.size() - 1; i >= 0; i--) { let window = serviceWindows.get(i); if (window && window.getRoot() && window.getRoot().getPackageName()) { return window.getRoot().getPackageName(); } } } log(["service.getWindows未能获取包名信息,通过currentPackage()返回数据"]); // 以上方法无法获取的,直接按原方法获取包名 return _current_pacakge(); } finally { log(["获取包名总耗时:{}ms", new Date().getTime() - start]); } }; log(currentPackage());
主动调用按钮点击事件
"ui"; ui.layout( <vertical> <button id="btn">按钮 </button> </vertical> ) ui.btn.click(function() { toastLog("牙叔教程\n简单易懂") }) // 注意要在UI线程中,执行点击代码。 ui.post( function() { ui.btn.performClick(); },2000 )
设置按钮三秒后才能点击
"ui"; ui.layout( <vertical> <button id="btn" text="按钮" /> </vertical> ); ui.btn.click(function () { toastLog("按钮被点击"); ui.btn.setText("按钮已被点击"); ui.btn.setEnabled(false); setTimeout(function () { ui.btn.setEnabled(true); ui.btn.setText("按钮"); }, 3000); });
一开始不要显示输入法
activity.getWindow().setSoftInputMode(android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
关闭输入法
"ui"; ui.layout( <vertical> <button id="btn">关闭输入法</button> <input w="*" id="input" /> </vertical> ); ui.btn.on("click", () => { //取消焦点代码 ui.input.clearFocus(); //隐藏键盘代码 let imm = ui.btn.getRootView().getContext().getSystemService(context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(ui.btn.getWindowToken(), android.view.inputmethod.InputMethodManager.HIDE_NOT_ALWAYS); });
状态栏高度
const resources = context.getResources(); const status_bar_height = resources.getDimensionPixelSize(resources.getIdentifier("status_bar_height", "dimen", "android"));
状态栏高度2
let rect = new android.graphics.Rect(); activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect); statusBarHeights = rect.top;
获取图片在ImageView中的坐标
function getImageBounds(imageView) { let bounds = new android.graphics.RectF(); drawable = imageView.getDrawable(); if (drawable != null) { imageView.getImageMatrix().mapRect(bounds, new android.graphics.RectF(drawable.getBounds())); } return bounds; }
java多线程
new java.lang.Thread( new java.lang.Runnable(function run() { toastLog("Thread Runnable"); }) ).start();
adb查看内存信息
参考:
adb shell dumpsys meminfo org.autojs.autojspro
查看单个应用最大内存限制
adb shell "getprop|grep heapgrowthlimit" // C:\Users\Administrator>adb shell "getprop|grep heapgrowthlimit" // [dalvik.vm.heapgrowthlimit]: [256m]
根据进程的名字使用 grep 指令过滤输出 "org.autojs.autojspro" 进程的信息
-d: 时间间隔, -d 3, 间隔3秒
参考: adb shell top 命令
adb shell top -d 3 | grep "org.autojs.autojspro"
下拉框spinner
--品尚名品
"ui"; ui.layout( <vertical padding="16"> <horizontal> <text textSize="16sp">下拉菜单</text> <spinner id="sp1" entries="aaa|bbb|ccc" /> </horizontal> <horizontal> <text textSize="16sp">对话框菜单</text> <spinner id="sp2" entries="ddd|eee|fff" spinnerMode="dialog" /> </horizontal> <button id="ok">确定</button> <button id="select3">设置指定选项</button> </vertical> ); ui.ok.on("click", () => { //获取选中项对应的索引值value var i = ui.sp1.getSelectedItemPosition(); var j = ui.sp2.getSelectedItemPosition(); toastLog("您的选择是选项" + i + "和选项" + j); var z = ui.sp1.getSelectedItem().toString(); var x = ui.sp2.getSelectedItem().toString(); toastLog("您的选择内容是" + z + "和" + x); }); ui.select3.on("click", () => { //根据索引值value设定选项 ui.sp1.setSelection(2); //获取ui.sp1 Adapter对象 ui.sp1.getAdapter(); //获取ui.sp1 下共有几个选项 ui.sp1.getAdapter().getCount(); //获取ui.sp1 对象下指定值的索引位置 ui.sp1.getAdapter().getPosition("ccc"); //根据值直接设置下拉菜单选中项 必须准确填写getPosition("")里的值 否则会出错 ui.sp1.setSelection(ui.sp1.getAdapter().getPosition("ccc")); //先判断值是否存在再设置 不存在的情况下不会出错 let 设定值 = "你好"; if (ui.sp1.getAdapter().getPosition(设定值) != -1) { ui.sp1.setSelection(ui.sp1.getAdapter().getPosition(设定值)); } else { toastLog("该选项不存在: " + 设定值 + " 无法完成设置!"); } });
drawable转Bitmap
--稻草人
importClass(android.graphics.Bitmap); importClass(android.graphics.Canvas); importClass(android.graphics.PixelFormat); function drawableToBitmap(drawable) { let bitmap = Bitmap.createBitmap( drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565); let canvas = new Canvas(bitmap); drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); drawable.draw(canvas); return bitmap; }
横竖屏判断
--抠脚本人
var a = context.resources.configuration.orientation; if (a === 1) { toastLog("这是竖屏!!"); } else { toastLog("这是横屏!!"); }
获取屏幕旋转的方向
var windowManager = context.getSystemService(Context.WINDOW_SERVICE); //by 板蓝根isatis function getRotation() { return windowManager.getDefaultDisplay().getRotation(); } //0 1 2 3分别对应 上 左 下 右
指定引擎运行代码
let src = new com.stardust.autojs.script.StringScriptSource("<code>", "toast(1)"); engines.myEngine().execute(src);
获取图片所有像素点的值
let img = images.read("/sdcard/1.png"); let r = getPixelsOfBitmap(img.bitmap); log(r); img.recycle(); function getPixelsOfBitmap(bitmap) { let width = bitmap.getWidth(); let height = bitmap.getHeight(); console.log("width=", width, " height=", height); let pixels = util.java.array("int", width * height); let offset = 0; let stride = width; let x = 0; let y = 0; bitmap.getPixels(pixels, offset, stride, x, y, width, height); return pixels; }
或者用图片路径, 也可以获取所有像素点
images.readPixels(path)
- path{string} 图片的地址
- 返回 {Object} 包括图片的像素数据和宽高,{data,width,height}
读取图片的像素数据和宽高。
保存画板canvas上的图片
-- @No evil ·火封
let bitmap = android.graphics.Bitmap.createBitmap(600, 600, android.graphics.Bitmap.Config.ARGB_8888); let canvas = new Canvas(bitmap); let paint = new Paint(); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(100); //设置画笔宽度 paint.setColor(colors.parseColor("#FF000000")); canvas.drawRect(100, 100, 500, 500, paint); let img = canvas.toImage(); let imgPath = "/sdcard/1.png"; img.saveTo(imgPath); app.viewFile(imgPath); img.recycle();
启用悬浮控制条,控制脚本worker.js
$engines.startFloatingController("./worker.js");
crash
-- @筱枫
app.startActivity({ packageName: "org.autojs.autojspro", className: "com.stardust.autojs.core.activity.CrashReportActivity", });
类增加属性
"nodejs"; require("rhino").install(); const ArrayList = java.util.ArrayList; const list = new ArrayList(); list.add(123); ArrayList.prototype.first = function() { return this.get(0); } console.log(list.first());
bitmap转Image对象
-- @I'm zz
var img = com.stardust.autojs.core.image.ImageWrapper.ofBitmap(bitmap);
let autojsMat = new com.stardust.autojs.core.opencv.Mat(mat1, new org.opencv.core.Rect(0, 0, w, h)); var img = com.stardust.autojs.core.image.ImageWrapper.ofMat(autojsMat);
选择图片
$images.select().on("result", img => {}) on("process") // 可以获取到URI
继承java抽象类
--李小波
JavaAdapter(需要重写的类,{重写的方法},参数...)
//参数决定使用哪个构造方法
// 无障碍异步监听 auto.registerEvent("view_clicked", (e) => { js += 'id("' + e.source.id() + '").text("' + e.source.text() + '").desc("' + e.source.desc() + '").findOne().click();\nsleep(500);\n'; });
// 判断稳定模式 toast(serviceFlag = auto.service.serviceInfo.flags == 122 ? "正常" : "稳定模式")
// 过滤窗口 function 查找指定app窗口(appName) { log(arguments.callee.name); let windowList = auto.windows; var len = windowList.length; for (var i = 0; i < len; i++) { let item = windowList[i]; if (item.title) { if (item.title === appName) { return item; } } } throw new Error("没有找到指定app窗口: " + appName); } function 获取当前页面内容(appName) { log(arguments.callee.name); let window = 查找指定app窗口(appName); let views = com.stardust.automator.UiObject.Companion.createRoot(window.getRoot()).find( idEndsWith("tv_title").visibleToUser(true).boundsInside(0, 0, device.width, device.height) ); if (views && views.length > 0) { let arr = []; var len = views.length; for (var i = 0; i < len; i++) { let item = views[i]; arr.push(item.text()); } return arr.join(", "); } else { return ""; } }
// 无障碍返回 auto.service.performGlobalAction(1);
// 获取自己的app的控件缓存 View.getDrawable().getBitmap()
// 清空图片缓存 $ui.imageCache.clearDiskCache(); $ui.imageCache.clearMemory();
// 设置光标位置 ui.授权码.setOnFocusChangeListener({ onFocusChange: function(view, hasFocus) { if (hasFocus) { view.setInputType(144); view.setSelection(view.getText().length()); } else { view.setInputType(129); } cardStr = view.text() } });
context.getSystemService(context.USAGE_STATS_SERVICE);
// 这个值就是ui绑定变量的上下文 runtime.ui.bindingContext
pool.shutdownNow(); pool.shutdown(); let status = pool.awaitTermination(1000, java.util.concurrent.TimeUnit.SECONDS);
new RootAutomator({adb: true})
runtime.gc()
console.error($debug.getStackTrace(e))
// 这行代码也许能刷新节点 auto.service.serviceInfo = auto.service.serviceInfo; auto.clearCache();
便签 = id("rich_editor").findOne() toast(便签.text()) sleep(2000) 便签.refresh() toast(便签.text())
// 当前包名 context.packageName
new java.util.Timer().schedule(new java.util.TimerTask({ run: () => { launch(context.getPackageName()) }, }), 2000); java.lang.Runtime.getRuntime().exit(0);
// 重启自己 "ui"; let args = $engines.myEngine().execArgv; let id = (args && args["id"]) || 0; ui.layout( <frame> <text text="页面{{id}}"/> </frame> ); if (id === 0) { toast("2秒后进入页面1"); ui.post(() => { $engines.execScriptFile("./main.js", { arguments: { id: id + 1 } }); }, 2000) } else if (id === 1) { toast("2秒后重启"); ui.post(() => { let Process = android.os.Process; Process.killProcess(Process.myPid()) }, 2000) }
requestScreenCaptureAsync(); // 返回promise
new Shell().exec("logcat -f /sdcard/脚本/log.txt");
var dcl=new Packages.dalvik.system.DexClassLoader(dexpath, dirpath, jnipath, java.lang.ClassLoader.getSystemClassLoader()); var cls=dcl.loadClass("com.jia.testso.Main"); var api=cls.newInstance(); api.init(context.getClass().getClassLoader());
$shell.isRootAvailable(); $shell.checkAccess("root");
$shell.setDefaultOptions({adb: true})
// 屏幕方向 context.resources.configuration.orientation