UI 事件绑定 — click、change、touch、focus 详解
事件绑定方式
AutoJs6 提供两种事件绑定方式:便捷方法(常用事件)和 on() 方法(通用事件)。
// 方式一:便捷方法(推荐,代码简洁)
ui.myBtn.click(() => {
// 点击处理
});
// 方式二:通用 on() 方法
ui.myBtn.on("click", () => {
// 点击处理
});
两种方式效果相同。便捷方法本质是 on() 的语法糖。
全部事件类型
基础交互事件
| 事件 | 触发时机 | 参数 | 便捷方法 |
|---|---|---|---|
click | 点击控件 | view | ✅ .click(fn) |
longClick | 长按控件 | view | ✅ .longClick(fn) |
touch | 触摸屏幕 | view, MotionEvent | ❌ 用 .on("touch", fn) |
doubleClick | 双击 | view | ❌ |
输入控件事件
| 事件 | 触发时机 | 参数 | 便捷方法 |
|---|---|---|---|
change | 控件值变化 | newValue | ✅ .on("change", fn) |
focus | 焦点变化 | hasFocus | ✅ .on("focus", fn) |
blur | 失去焦点 | - | ❌ |
textChange | 文本变化 | newText | ❌ |
滑动与选择事件
| 事件 | 触发时机 | 参数 | 适用控件 |
|---|---|---|---|
progressChange | 进度变化 | progress | seekbar, progressbar |
checkedChange | 选中状态变化 | checked | checkbox, switch |
selectedChange | 选中项变化 | item | spinner |
事件绑定示例
click — 点击
ui.layout(<button id="btn" text="点击我"/>);
// 无参数
ui.btn.click(() => {
toast("被点击了");
});
// 接收 view 参数
ui.btn.click((view) => {
console.log("点击了: " + view.id);
});
longClick — 长按
ui.layout(<button id="btn" text="长按我"/>);
ui.btn.longClick(() => {
toast("长按触发");
});
// 长按和点击可以同时绑定
ui.btn.click(() => toast("点击"));
ui.btn.longClick(() => toast("长按"));
touch — 触摸(多点触控)
ui.layout(<vertical id="touchArea" w="*" h="300" bg="#EEE"/>);
ui.touchArea.on("touch", (view, event) => {
let action = event.getAction();
let x = event.getRawX();
let y = event.getRawY();
// action 类型
// 0 = ACTION_DOWN(按下)
// 1 = ACTION_UP(抬起)
// 2 = ACTION_MOVE(移动)
if (action === 0) {
console.log("按下: (" + x + ", " + y + ")");
} else if (action === 2) {
console.log("移动到: (" + x + ", " + y + ")");
} else if (action === 1) {
console.log("抬起");
}
});
change — 值变化
ui.layout(
<vertical>
<input id="field" hint="输入点什么"/>
<switch id="enable" checked="false"/>
<checkbox id="agree" text="同意"/>
<seekbar id="slider" max="100"/>
</vertical>
);
// 输入框内容变化
ui.field.on("change", (text) => {
console.log("输入: " + text);
});
// 开关切换
ui.enable.on("change", (checked) => {
console.log("开关: " + (checked ? "开" : "关"));
});
// 复选框勾选
ui.agree.on("change", (checked) => {
console.log("同意: " + checked);
});
// 滑动条值变化
ui.slider.on("change", (progress) => {
console.log("当前值: " + progress);
});
focus — 焦点变化
ui.layout(
<vertical>
<input id="name" hint="姓名"/>
<input id="phone" hint="电话"/>
</vertical>
);
ui.name.on("focus", (hasFocus) => {
if (hasFocus) {
console.log("姓名框获得焦点");
} else {
console.log("姓名框失去焦点");
}
});
ui.phone.on("focus", (hasFocus) => {
if (hasFocus) {
console.log("电话框获得焦点");
}
});
一次绑定多个事件
ui.layout(<button id="btn" text="多功能按钮"/>);
// 可以同时绑定多种事件
ui.btn.click(() => toast("点击"));
ui.btn.longClick(() => toast("长按"));
ui.btn.on("touch", (v, e) => {
// 触摸事件也可以和点击共存
});
移除事件监听
v6.7 新增了部分移除监听能力:
// 保存监听函数引用
let onBtnClick = () => {
toast("执行操作");
};
ui.btn.click(onBtnClick);
// 移除指定监听(需测试各事件是否支持)
// ui.btn.removeListener("click", onBtnClick);
如果移除方法不可用,可用"空函数覆盖"或"重建界面"替代。
防重复点击
// 简单的防抖(快速连点只触发一次)
let lastClickTime = 0;
ui.submitBtn.click(() => {
let now = Date.now();
if (now - lastClickTime < 1000) {
console.log("点击过快,忽略");
return;
}
lastClickTime = now;
// 实际逻辑...
toast("提交成功");
});
异步处理
// 点击事件中执行异步操作
ui.loginBtn.click(async () => {
// 显示"登录中"状态
ui.loginBtn.setEnabled(false);
ui.loginBtn.setText("登录中...");
try {
let result = await api.login(email, password);
toast("登录成功");
} catch (err) {
toast("登录失败: " + err.message);
}
// 恢复按钮状态
ui.loginBtn.setEnabled(true);
ui.loginBtn.setText("登录");
});
注意:Rhino 对 async/await 支持有限,建议用回调或 Promise 链。
实战:表单验证
"ui";
ui.layout(
<vertical padding="16">
<input id="username" hint="用户名(3-16位)"/>
<input id="email" hint="邮箱地址"/>
<input id="age" hint="年龄" inputType="number"/>
<button id="submit" text="提交表单" marginTop="24"/>
<text id="errorMsg" textColor="#F44336" textSize="12sp" visibility="gone"/>
</vertical>
);
function validate(username, email, age) {
let errors = [];
if (username.length < 3 || username.length > 16) {
errors.push("用户名需 3-16 位");
}
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
errors.push("邮箱格式不正确");
}
let ageNum = parseInt(age);
if (isNaN(ageNum) || ageNum < 1 || ageNum > 150) {
errors.push("年龄需在 1-150 之间");
}
return errors;
}
ui.submit.click(() => {
let username = ui.username.getText();
let email = ui.email.getText();
let age = ui.age.getText();
let errors = validate(username, email, age);
if (errors.length > 0) {
ui.errorMsg.setText(errors.join("\n"));
ui.errorMsg.attr("visibility", "visible");
} else {
ui.errorMsg.attr("visibility", "gone");
toast("表单验证通过");
}
});
// 输入变化时实时清除错误提示
ui.username.on("change", () => ui.errorMsg.attr("visibility", "gone"));
ui.email.on("change", () => ui.errorMsg.attr("visibility", "gone"));
ui.age.on("change", () => ui.errorMsg.attr("visibility", "gone"));