2. 事件类型详解(上):LV_EVENT_CLICKED、LV_EVENT_PRESSED、LV_EVENT_RELEASED
好,咱们直接进入正题。上一章我们聊了事件驱动的基本概念,说白了就是「发生了什么事,然后去处理」。这一章,我把触摸事件里最常用的三个家伙拎出来,一个一个拆开看。
我个人习惯把这三个事件叫做「触摸三兄弟」:按下(PRESSED)、松开(RELEASED)、点击(CLICKED)。它们的关系很微妙,搞不清楚的话,你的按钮要么点不动,要么乱触发。我在项目里见过不少新手在这上面翻车。
2.1 LV_EVENT_PRESSED —— 手指刚碰上的那一刻
这个事件触发得最早。只要用户的手指(或触摸笔)碰到了屏幕上的对象,LV_EVENT_PRESSED 就会立刻被发送。注意,是「碰到」就触发,不需要等待任何时间或位移。
什么时候用?举个例子,我做的一个智能家居面板,用户按下灯光按钮时,我希望按钮立刻变暗,给一个「我被按了」的视觉反馈。这时候用 PRESSED 事件就非常合适。
static void btn_event_handler(lv_event_t * e) {
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * btn = lv_event_get_target(e);
if(code == LV_EVENT_PRESSED) {
// 按下时,按钮变暗,给用户触感反馈
lv_obj_set_style_bg_color(btn, lv_color_hex(0x666666), 0);
lv_obj_set_style_opa(btn, LV_OPA_80, 0);
LV_LOG_USER("按钮被按下");
}
}
2.2 LV_EVENT_RELEASED —— 手指抬起来的那一瞬间
这个事件在用户手指离开屏幕时触发。注意,它不关心你手指抬起来之前做了什么——哪怕你按下去之后滑到了对象外面再松开,LV_EVENT_RELEASED 依然会触发。
为什么会这样?因为 LVGL 的设计逻辑是:只要你在该对象上按下去,松开时不管你在哪里,都会通知这个对象「我被松开了」。这跟 CLICKED 不一样,后面会讲。
我建议把 RELEASED 用于「松开后立即生效」的操作。比如音量滑块,用户按住拖动,松开时确认最终音量值。
if(code == LV_EVENT_RELEASED) {
// 松开时,恢复按钮颜色
lv_obj_set_style_bg_color(btn, lv_color_hex(0x2196F3), 0);
lv_obj_set_style_opa(btn, LV_OPA_100, 0);
LV_LOG_USER("按钮被松开");
}
2.3 LV_EVENT_CLICKED —— 真正的「点击」
这是大家最熟悉的事件。但它的触发条件比你想的要严格:
- 必须先在该对象上触发过 PRESSED
- 松开时手指必须在对象内部(或在一个可接受的滑动范围内)
- 没有触发长按、拖动等其他手势
说白了,CLICKED 是一个「干净利落的点击」。用户按下去,没有乱滑,没有拖拽,然后松开,这才算一次点击。
我见过有人把 CLICKED 和 RELEASED 混用,结果发现有时候按下去再滑出去松开,RELEASED 触发了但 CLICKED 没触发。嗯,这就是两者的本质区别。
if(code == LV_EVENT_CLICKED) {
// 真正的点击事件
LV_LOG_USER("按钮被点击,执行主要操作");
// 比如切换页面、提交表单等
}
2.4 三者的关系与触发顺序
为了让你更直观地理解,我画了一张流程图。这张图展示了从手指触摸到松开,三个事件是如何依次产生的。
2.5 三个事件的对比表格
我把它们的核心差异整理成了一张表,方便你查阅和对比:
| 事件 | 触发时机 | 触发条件 | 典型用途 | 是否受滑动影响 |
|---|---|---|---|---|
| PRESSED | 手指接触屏幕瞬间 | 触摸点在对象区域内 | 视觉反馈、触觉反馈、开始计时 | 否(立即触发) |
| RELEASED | 手指离开屏幕瞬间 | 之前在该对象上按过 | 恢复状态、确认操作、清理资源 | 否(总会触发) |
| CLICKED | 手指离开屏幕后 | 按过 + 未滑动 + 松开时在区域内 | 主要操作、页面跳转、数据提交 | 是(滑动则取消) |
2.6 实际项目中的组合用法
光讲理论没意思,我分享一个我在智能门锁项目里用过的实际场景。门锁的解锁按钮需要三种反馈:
- 按下时:按钮颜色变深,同时蜂鸣器短响一声(PRESSED)
- 松开时:按钮颜色恢复,蜂鸣器停止(RELEASED)
- 点击成功时:执行解锁逻辑,显示解锁动画(CLICKED)
static void unlock_btn_cb(lv_event_t * e) {
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * btn = lv_event_get_target(e);
if(code == LV_EVENT_PRESSED) {
// 按下:视觉 + 声音反馈
lv_obj_set_style_bg_color(btn, lv_color_hex(0x004d40), 0);
buzzer_on();
}
else if(code == LV_EVENT_RELEASED) {
// 松开:恢复外观,关闭蜂鸣器
lv_obj_set_style_bg_color(btn, lv_color_hex(0x00796b), 0);
buzzer_off();
}
else if(code == LV_EVENT_CLICKED) {
// 点击成功:执行解锁
door_unlock();
lv_label_set_text(ui_status_label, "门已解锁");
}
}
2.7 常见误区总结
最后,我把新手最容易踩的几个坑列出来,你写代码的时候可以对照检查:
- 误区一:在 PRESSED 里做耗时操作。这会导致触摸反馈延迟,用户会觉得屏幕卡顿。
- 误区二:在 RELEASED 里删除触发事件的对象。这会导致野指针或内存泄漏。
- 误区三:认为 CLICKED 一定会触发。记住,只要手指滑出对象区域,CLICKED 就不会触发,但 RELEASED 依然会。
- 误区四:在 CLICKED 里判断触摸坐标。没必要,CLICKED 本身就保证了触摸点在对象内部。
嗯,这一章的内容就到这里。这三个事件是触摸交互的基石,搞清楚了它们,后面的长按、双击、拖动事件理解起来就轻松多了。