牙叔教程 简单易懂
隐藏导航栏
-- 惜缘
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 详解
adb shell dumpsys 命令 查看内存
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;
}
或者用图片路径, 也可以获取所有像素点
-
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(需要重写的类,{重写的方法},参数...)
//参数决定使用哪个构造方法
autojs实现抽象类的继承
// 无障碍异步监听
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})
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})