事件类型详解:LV_EVENT_CLICKED、LV_EVENT_PRESSING、LV_EVENT_VALUE_CHANGED
事件类型,说白了就是告诉系统「用户刚才干了什么」。LVGL 里定义了二十多种事件,但日常开发中,80% 的场景只用得到其中五六个。今天我就挑三个最常用的,掰开揉碎讲清楚。
我个人习惯把事件分成两类:物理操作事件和逻辑状态事件。CLICKED 和 PRESSING 属于前者,VALUE_CHANGED 属于后者。搞懂这个分类,你写代码时思路会清晰很多。
LV_EVENT_CLICKED:点击完成
这是最基础、最常用的事件。用户按下按钮再松开,如果松手时手指还在按钮范围内,就会触发 CLICKED。
核心要点:CLICKED 不等于「按下」,而是「按下 + 松开 + 位置有效」的完整组合。很多新手在这里翻车,以为按下就算点击。
我在项目中遇到过一个问题:用户快速点击按钮,有时候 CLICKED 没触发。后来查文档才发现,LVGL 默认有个短按时间阈值(LV_INDEV_DEF_SHORT_PRESS_TIME),按得太快反而会被忽略。嗯,这里要注意。
// 典型用法:按钮点击回调
static void btn_event_cb(lv_event_t * e) {
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED) {
LV_LOG_USER("按钮被点击了");
// 执行你的业务逻辑
}
}
// 创建按钮并绑定事件
lv_obj_t * btn = lv_btn_create(lv_scr_act());
lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, NULL);
小技巧:如果你需要区分「短按」和「长按」,不要只用 CLICKED。配合 LV_EVENT_LONG_PRESSED 一起用,效果更好。我一般会在长按事件里做「确认删除」这类危险操作,短按做常规触发。
LV_EVENT_PRESSING:正在按压
这个事件有点意思。它不是触发一次就完事,而是在用户按住不放的整个过程中持续触发。你想想看,这适合做什么?
进度条拖拽、音量滑块调节、游戏中的蓄力条……这些需要实时反馈的场景,PRESSING 就是你的好帮手。
// 实时反馈:按压过程中更新进度条
static void slider_event_cb(lv_event_t * e) {
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_PRESSING) {
lv_obj_t * slider = lv_event_get_target(e);
int32_t val = lv_slider_get_value(slider);
// 实时更新旁边的数值显示
lv_label_set_text_fmt(label, "%d%%", val);
}
}
我曾经踩过一个坑:在 PRESSING 回调里做了大量计算,结果界面卡成 PPT。为什么?因为 PRESSING 的触发频率很高(默认每 20ms 一次),你在里面写复杂逻辑,CPU 根本扛不住。后来我改成只更新 UI,计算放到定时器里做,问题就解决了。
避坑指南:PRESSING 回调里不要做:
- 文件读写
- 网络请求
- 大量内存分配
- 复杂的数学运算
只做轻量级的 UI 更新,比如改个颜色、调个位置、更新个文本。
LV_EVENT_VALUE_CHANGED:值变了
这个事件跟物理操作无关,它关注的是控件的逻辑状态。比如滑块拖到 50、开关从关变成开、下拉列表选中了第三项——这些都会触发 VALUE_CHANGED。
你可能会问:CLICKED 和 VALUE_CHANGED 有什么区别?我刚开始学的时候也搞混过。直接看对比表:
| 事件 | 触发时机 | 典型控件 | 触发次数 |
|---|---|---|---|
| LV_EVENT_CLICKED | 按下并松开后 | 按钮、面板 | 一次 |
| LV_EVENT_PRESSING | 按住不放期间 | 滑块、进度条 | 多次(持续触发) |
| LV_EVENT_VALUE_CHANGED | 值发生变化时 | 滑块、开关、下拉列表 | 值每变化一次触发一次 |
举个例子:你拖拽滑块从 0 到 100。PRESSING 会触发几十次,VALUE_CHANGED 也会触发几十次。但如果你用代码 lv_slider_set_value(slider, 50, LV_ANIM_ON) 直接设置值,只有 VALUE_CHANGED 会触发,PRESSING 不会。
// 监听开关状态变化
static void switch_event_cb(lv_event_t * e) {
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_VALUE_CHANGED) {
lv_obj_t * sw = lv_event_get_target(e);
bool is_on = lv_obj_has_state(sw, LV_STATE_CHECKED);
if (is_on) {
LV_LOG_USER("开关已打开");
// 开启某个功能
} else {
LV_LOG_USER("开关已关闭");
// 关闭某个功能
}
}
}
我的习惯:对于滑块、开关这类控件,我优先用 VALUE_CHANGED 而不是 CLICKED。因为 CLICKED 只在松开时触发一次,而 VALUE_CHANGED 能捕捉到每一次值变化,逻辑更干净。你想想看,用户拖到 50 又拖回 0,CLICKED 只触发一次,VALUE_CHANGED 却能告诉你整个过程。
三种事件的协作关系
实际项目中,这三种事件经常配合使用。我画了一张图,帮你理清它们的关系:
从这张图能看出来:PRESSING 是「进行时」,CLICKED 是「完成时」,VALUE_CHANGED 是「逻辑变化时」。三者各司其职,但可以共存于同一个控件上。
实际项目中的选择策略
说了这么多,到底什么时候用哪个?我总结了几条经验:
- 按钮、菜单项 → 用 CLICKED。用户点一下,你执行一次操作,简单直接。
- 滑块、旋钮、拖拽条 → 用 PRESSING 做实时反馈,用 VALUE_CHANGED 做最终结果保存。
- 开关、复选框、下拉列表 → 用 VALUE_CHANGED。这些控件的核心就是「状态切换」,CLICKED 反而多余。
- 需要区分短按和长按 → CLICKED + LONG_PRESSED 组合,不要自己写计时器。
曾经踩过的坑:我在一个智能家居项目里,用 CLICKED 监听开关按钮。结果用户按住开关不放,松手时触发了 CLICKED,但开关状态已经变了三次。后来改成 VALUE_CHANGED,问题解决。记住:CLICKED 只告诉你「用户点完了」,不告诉你「值变了几次」。
嗯,这三种事件搞明白,LVGL 事件系统你就入门了一半。剩下的就是多写、多试、多踩坑。代码写多了,自然就知道什么时候该用哪个。