4、LVGL基础控件(上):标签、按钮、进度条、滑块
好,咱们今天开始啃LVGL的基础控件。说实话,这部分是每个界面都绕不开的硬骨头。我个人习惯先把最常用的四个控件讲透——标签、按钮、进度条、滑块。你想想看,一个嵌入式设备界面,80%的场景无非就是显示文字、点击操作、显示进度、调节参数。把这四个玩明白了,后面再学其他控件就轻松多了。
4.1 标签(lv_label)——最基础的显示单元
标签控件,说白了就是用来显示文本的。我在项目中遇到过最典型的场景:设备状态显示、数值读数、菜单标题。别看它简单,用不好照样出问题。
4.1.1 创建与基本设置
/* 创建一个标签 */
lv_obj_t *label = lv_label_create(lv_scr_act());
/* 设置文本 */
lv_label_set_text(label, "Hello, LVGL!");
/* 设置长文本模式 */
lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR);
/* 设置对齐方式 */
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
嗯,这里要注意:长文本模式是个坑。我曾经有个项目,显示设备序列号,字符串特别长,结果默认模式直接截断了。后来才发现有LV_LABEL_LONG_SCROLL_CIRCULAR这个循环滚动模式,完美解决。
长文本模式一览:
| 模式 | 说明 | 适用场景 |
|---|---|---|
| LV_LABEL_LONG_WRAP | 自动换行 | 多行文本显示 |
| LV_LABEL_LONG_DOT | 超出显示省略号 | 单行截断提示 |
| LV_LABEL_LONG_SCROLL | 水平滚动 | 跑马灯效果 |
| LV_LABEL_LONG_SCROLL_CIRCULAR | 循环滚动 | 长文本循环展示 |
| LV_LABEL_LONG_CLIP | 直接裁剪 | 严格限制显示区域 |
4.1.2 文本格式化与动态更新
实际项目中,标签内容经常需要动态变化。比如显示传感器读数、系统运行时间。我建议用lv_label_set_text_fmt,它支持格式化字符串,比手动拼接方便多了。
/* 动态更新数值 */
char buffer[32];
snprintf(buffer, sizeof(buffer), "温度: %.1f°C", temperature);
lv_label_set_text(label, buffer);
/* 或者直接用格式化函数 */
lv_label_set_text_fmt(label, "湿度: %d%%", humidity);
避坑指南:我曾经在RTOS任务里频繁更新标签文本,结果发现内存碎片越来越严重。后来查资料才知道,每次lv_label_set_text都会重新分配内存。解决方案是:先用lv_label_set_text_static设置静态字符串,或者用lv_label_set_text_fmt配合局部缓冲区。
4.2 按钮(lv_btn)——交互的入口
按钮是用户与设备交互的核心。你想想看,不管是确认、取消、开始、停止,最终都要落到按钮上。我个人习惯把按钮分为三类:普通按钮、开关按钮、矩阵按钮。
4.2.1 创建与事件绑定
/* 创建按钮 */
lv_obj_t *btn = lv_btn_create(lv_scr_act());
/* 设置按钮大小和位置 */
lv_obj_set_size(btn, 120, 50);
lv_obj_center(btn);
/* 在按钮上添加标签 */
lv_obj_t *label = lv_label_create(btn);
lv_label_set_text(label, "确认");
lv_obj_center(label);
/* 绑定点击事件 */
lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, NULL);
/* 事件回调函数 */
static void btn_event_cb(lv_event_t *e) {
LV_LOG_USER("按钮被点击了");
/* 在这里处理业务逻辑 */
}
重要提醒:事件回调函数里不要做耗时操作!我见过有人直接在回调里写延时、做复杂计算,结果界面卡死。正确的做法是:在回调里只发送消息或设置标志位,真正的处理交给RTOS任务去执行。
4.2.2 按钮状态与样式
按钮有几种状态:默认、按下、禁用、聚焦。每个状态都可以单独设置样式。我在项目中做过一个温控器界面,按下按钮时颜色变深、加阴影,用户体验特别好。
/* 设置按钮不同状态的样式 */
lv_obj_set_style_bg_color(btn, lv_color_hex(0x2196F3), LV_STATE_DEFAULT);
lv_obj_set_style_bg_color(btn, lv_color_hex(0x1976D2), LV_STATE_PRESSED);
lv_obj_set_style_bg_color(btn, lv_color_hex(0x9E9E9E), LV_STATE_DISABLED);
/* 启用/禁用按钮 */
lv_obj_add_state(btn, LV_STATE_DISABLED); /* 禁用 */
lv_obj_clear_state(btn, LV_STATE_DISABLED); /* 启用 */
4.3 进度条(lv_bar)——直观的进度反馈
进度条这东西,说白了就是给用户一个心理预期。下载进度、加载进度、任务完成度,都需要它。我记得第一次用进度条时,以为只是显示百分比,后来才发现它还能做范围指示器。
4.3.1 基本用法
/* 创建进度条 */
lv_obj_t *bar = lv_bar_create(lv_scr_act());
/* 设置范围 */
lv_bar_set_range(bar, 0, 100);
/* 设置当前值 */
lv_bar_set_value(bar, 50, LV_ANIM_ON);
/* 设置动画时间 */
lv_bar_set_anim_time(bar, 500);
关键参数说明:
lv_bar_set_range:设置最小值、最大值。不一定是0-100,可以是0-255、0-1000等lv_bar_set_value:第三个参数控制是否启用动画过渡lv_bar_set_anim_time:动画持续时间,单位毫秒
4.3.2 模式与样式定制
进度条有三种模式:普通模式、对称模式、范围模式。我做过一个音频均衡器,用对称模式显示正负值,效果很酷。
/* 设置模式 */
lv_bar_set_mode(bar, LV_BAR_MODE_SYMMETRICAL); /* 对称模式,以0为中心 */
/* 自定义样式 */
lv_obj_set_style_bg_color(bar, lv_color_hex(0xE0E0E0), LV_PART_MAIN);
lv_obj_set_style_bg_color(bar, lv_color_hex(0x4CAF50), LV_PART_INDICATOR);
lv_obj_set_style_anim_time(bar, 300, 0);
避坑指南:我曾经在进度条更新时,直接在RTOS任务里每10ms调用一次lv_bar_set_value。结果发现CPU占用率飙升,界面反而卡顿。后来改成每100ms更新一次,配合动画过渡,既流畅又省资源。
4.4 滑块(lv_slider)——交互式调节
滑块和进度条长得很像,但本质不同。进度条是显示进度,滑块是调节参数。亮度调节、音量控制、温度设定,这些场景都离不开滑块。
4.4.1 创建与事件处理
/* 创建滑块 */
lv_obj_t *slider = lv_slider_create(lv_scr_act());
/* 设置范围 */
lv_slider_set_range(slider, 0, 100);
/* 设置当前值 */
lv_slider_set_value(slider, 50, LV_ANIM_ON);
/* 绑定值改变事件 */
lv_obj_add_event_cb(slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
static void slider_event_cb(lv_event_t *e) {
lv_obj_t *slider = lv_event_get_target(e);
int32_t value = lv_slider_get_value(slider);
LV_LOG_USER("滑块值: %d", value);
/* 在这里更新实际参数 */
}
4.4.2 高级特性
滑块有几个实用特性:拖动反馈、步进调节、左右对称。我做过一个调光器,用滑块控制LED亮度,配合步进功能,用户拖动时手感特别好。
/* 设置步进值 */
lv_slider_set_range(slider, 0, 100);
/* 步进为5,用户拖动时值会跳到0,5,10,15... */
/* 需要在事件回调中手动取整 */
static void slider_event_cb(lv_event_t *e) {
lv_obj_t *slider = lv_event_get_target(e);
int32_t value = lv_slider_get_value(slider);
int32_t stepped = (value / 5) * 5;
lv_slider_set_value(slider, stepped, LV_ANIM_OFF);
}
/* 自定义滑块样式 */
lv_obj_set_style_bg_color(slider, lv_color_hex(0xE0E0E0), LV_PART_MAIN);
lv_obj_set_style_bg_color(slider, lv_color_hex(0x2196F3), LV_PART_INDICATOR);
lv_obj_set_style_bg_color(slider, lv_color_hex(0xFFFFFF), LV_PART_KNOB);
重要提醒:滑块事件LV_EVENT_VALUE_CHANGED在拖动过程中会频繁触发。我建议在回调里加一个防抖处理,或者只在松开手指时(LV_EVENT_RELEASED)才执行真正的参数更新。否则,每次微小的拖动都会触发大量计算,浪费CPU资源。
4.5 实战经验总结
好了,这四个控件讲完了。我最后分享几个实战中的心得:
- 标签更新频率:不要超过50ms一次,人眼根本看不出来,反而浪费CPU
- 按钮防抖:机械按键需要硬件或软件防抖,触摸屏按钮不需要
- 进度条动画:动画时间建议200-500ms,太短显得生硬,太长让人着急
- 滑块精度:根据实际需求设置范围,不要盲目用0-1000,精度越高拖动越卡
下一章我们会继续讲剩下的基础控件:开关、复选框、下拉列表、文本框。这些控件在表单设置、参数配置场景中特别常用。到时候我会结合一个完整的设置界面案例,把今天学的和下一章的内容串起来。
嗯,今天就到这里。如果你在实际项目中遇到什么问题,欢迎随时交流。记住,控件是死的,思路是活的。多动手、多调试,慢慢就有感觉了。