3. LVGL 基础控件(上):标签、按钮、开关、进度条、滑块
各位工程师朋友,欢迎来到第三章。这一章我们开始真正动手,接触 LVGL 里最常用的五个基础控件。说实话,我刚开始用 LVGL 的时候,觉得控件嘛,不就是调几个 API 嘛。但真正做项目才发现,每个控件都有它的脾气。用好了,事半功倍;用不好,后面改起来想哭。
这五个控件——标签、按钮、开关、进度条、滑块——几乎覆盖了 80% 的交互场景。你想想看,哪个嵌入式界面能少了它们?
3.1 标签(lv_label)—— 最基础的文本显示
标签控件,说白了就是用来显示文字的。但别小看它,我见过太多项目因为标签没用好,导致界面显示乱七八糟。
3.1.1 创建与基本使用
/* 创建一个标签 */
lv_obj_t *label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Hello, LVGL!");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
就这么几行,一个标签就出来了。但实际项目中,我们往往需要更复杂的文本处理。
3.1.2 长文本处理
我记得有一次做智能家居面板,需要显示一大段设备状态信息。直接塞进去,结果文字溢出屏幕了。后来才发现,标签有个「长模式」设置。
| 长模式 | 宏定义 | 行为 |
|---|---|---|
| 自动换行 | LV_LABEL_LONG_WRAP | 超出宽度自动换行 |
| 点号省略 | LV_LABEL_LONG_DOT | 超出部分显示为 ... |
| 滚动显示 | LV_LABEL_LONG_SCROLL | 文字水平滚动 |
| 裁剪 | LV_LABEL_LONG_CLIP | 超出部分直接裁剪 |
| 扩展 | LV_LABEL_LONG_EXPAND | 自动扩展宽度 |
/* 设置自动换行 */
lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
lv_obj_set_width(label, 200); /* 设置宽度,触发换行 */
3.1.3 文本格式化
显示动态数值时,我喜欢用 lv_label_set_text_fmt(),它支持 printf 风格的格式化。
int temperature = 26;
lv_label_set_text_fmt(label, "当前温度:%d°C", temperature);
3.1.4 文本颜色与样式
/* 设置文本颜色 */
lv_obj_set_style_text_color(label, lv_color_hex(0x2196F3), 0);
/* 设置字体大小 */
lv_obj_set_style_text_font(label, &lv_font_montserrat_20, 0);
3.2 按钮(lv_btn)—— 交互的起点
按钮是用户与设备交互最直接的方式。一个按钮按下去,设备就要有反应。
3.2.1 创建按钮
lv_obj_t *btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn, 120, 50);
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);
嗯,按钮本身不显示文字。你需要往里面加一个标签。
lv_obj_t *label = lv_label_create(btn);
lv_label_set_text(label, "确认");
lv_obj_center(label);
3.2.2 事件处理
按钮的核心就是事件。我习惯用 LV_EVENT_CLICKED 来处理点击。
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_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, NULL);
3.2.3 按钮状态
按钮有几种状态:正常、按下、禁用、聚焦。你可以为每种状态设置不同的样式。
/* 禁用按钮 */
lv_obj_add_state(btn, LV_STATE_DISABLED);
/* 设置禁用时的背景色 */
lv_obj_set_style_bg_color(btn, lv_color_hex(0xCCCCCC), LV_STATE_DISABLED);
3.3 开关(lv_switch)—— 二选一的优雅方案
开关控件,就是那种可以拨动的滑块。开和关,一目了然。
3.3.1 创建与使用
lv_obj_t *sw = lv_switch_create(lv_scr_act());
lv_obj_align(sw, LV_ALIGN_CENTER, 0, 0);
开关默认是关闭状态。要打开它:
lv_obj_add_state(sw, LV_STATE_CHECKED);
3.3.2 状态监听
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) {
bool is_on = lv_obj_has_state(sw, LV_STATE_CHECKED);
if(is_on) {
LV_LOG_USER("开关已打开");
} else {
LV_LOG_USER("开关已关闭");
}
}
}
lv_obj_add_event_cb(sw, switch_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
3.3.3 自定义颜色
/* 开启状态的颜色 */
lv_obj_set_style_bg_color(sw, lv_palette_main(LV_PALETTE_GREEN), LV_STATE_CHECKED);
/* 关闭状态的颜色 */
lv_obj_set_style_bg_color(sw, lv_palette_main(LV_PALETTE_GREY), 0);
3.4 进度条(lv_bar)—— 进度可视化
进度条用来显示任务的完成度。比如下载进度、音量大小、电池电量。
3.4.1 创建与设置
lv_obj_t *bar = lv_bar_create(lv_scr_act());
lv_obj_set_size(bar, 200, 20);
lv_obj_align(bar, LV_ALIGN_CENTER, 0, 0);
/* 设置范围 */
lv_bar_set_range(bar, 0, 100);
/* 设置当前值 */
lv_bar_set_value(bar, 50, LV_ANIM_ON);
注意最后一个参数 LV_ANIM_ON,它会让进度条平滑过渡。我个人非常喜欢这个特性,用户体验瞬间提升。
3.4.2 模式设置
进度条有三种模式:
| 模式 | 宏定义 | 用途 |
|---|---|---|
| 普通模式 | LV_BAR_MODE_NORMAL | 单值显示,最常用 |
| 对称模式 | LV_BAR_MODE_SYMMETRICAL | 以零点为中心,正负值对称显示 |
| 范围模式 | LV_BAR_MODE_RANGE | 显示起始值和结束值之间的范围 |
/* 范围模式示例:显示已用和剩余 */
lv_bar_set_mode(bar, LV_BAR_MODE_RANGE);
lv_bar_set_start_value(bar, 20, LV_ANIM_OFF);
lv_bar_set_value(bar, 80, LV_ANIM_ON);
3.5 滑块(lv_slider)—— 可拖动的进度条
滑块和进度条长得很像,但滑块是可以拖动的。用户通过拖动来调整数值。
3.5.1 创建与配置
lv_obj_t *slider = lv_slider_create(lv_scr_act());
lv_obj_set_size(slider, 200, 20);
lv_obj_align(slider, LV_ALIGN_CENTER, 0, 0);
/* 设置范围 */
lv_slider_set_range(slider, 0, 100);
/* 设置当前值 */
lv_slider_set_value(slider, 30, LV_ANIM_ON);
3.5.2 拖动事件
static void slider_event_cb(lv_event_t *e) {
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_VALUE_CHANGED) {
int32_t val = lv_slider_get_value(slider);
LV_LOG_USER("滑块值:%d", val);
/* 更新其他控件,比如显示数值的标签 */
}
}
lv_obj_add_event_cb(slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
3.5.3 步进设置
有时候我们希望滑块按固定步长跳动,而不是连续变化。
/* 设置步长为 10 */
lv_slider_set_range(slider, 0, 100);
/* 在事件回调中处理步进 */
static void slider_event_cb(lv_event_t *e) {
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_VALUE_CHANGED) {
int32_t val = lv_slider_get_value(slider);
/* 四舍五入到最近的 10 的倍数 */
val = (val + 5) / 10 * 10;
lv_slider_set_value(slider, val, LV_ANIM_OFF);
}
}
3.5.4 滑块与进度条的区别
| 特性 | 进度条(lv_bar) | 滑块(lv_slider) |
|---|---|---|
| 用户交互 | 只读,不可拖动 | 可拖动,可交互 |
| 主要用途 | 显示进度、状态 | 参数调节、数值选择 |
| 事件 | 通常不需要事件 | 需要 VALUE_CHANGED 事件 |
| 样式 | 只有指示条 | 有滑块按钮(旋钮) |
3.6 综合示例:一个简单的设置界面
最后,我把这五个控件组合起来,做一个简单的设置界面。你可以在自己的项目里直接套用这个结构。
/* 创建设置界面 */
void settings_ui_create(void) {
/* 标题标签 */
lv_obj_t *title = lv_label_create(lv_scr_act());
lv_label_set_text(title, "设备设置");
lv_obj_set_style_text_font(title, &lv_font_montserrat_24, 0);
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 20);
/* 音量标签 */
lv_obj_t *vol_label = lv_label_create(lv_scr_act());
lv_label_set_text(vol_label, "音量");
lv_obj_align(vol_label, LV_ALIGN_TOP_LEFT, 20, 80);
/* 音量滑块 */
lv_obj_t *vol_slider = lv_slider_create(lv_scr_act());
lv_obj_set_size(vol_slider, 200, 20);
lv_slider_set_range(vol_slider, 0, 100);
lv_slider_set_value(vol_slider, 50, LV_ANIM_OFF);
lv_obj_align(vol_slider, LV_ALIGN_TOP_LEFT, 80, 75);
/* 开关标签 */
lv_obj_t *sw_label = lv_label_create(lv_scr_act());
lv_label_set_text(sw_label, "Wi-Fi");
lv_obj_align(sw_label, LV_ALIGN_TOP_LEFT, 20, 130);
/* Wi-Fi 开关 */
lv_obj_t *wifi_sw = lv_switch_create(lv_scr_act());
lv_obj_align(wifi_sw, LV_ALIGN_TOP_LEFT, 80, 125);
/* 进度条标签 */
lv_obj_t *prog_label = lv_label_create(lv_scr_act());
lv_label_set_text(prog_label, "存储");
lv_obj_align(prog_label, LV_ALIGN_TOP_LEFT, 20, 180);
/* 存储进度条 */
lv_obj_t *storage_bar = lv_bar_create(lv_scr_act());
lv_obj_set_size(storage_bar, 200, 20);
lv_bar_set_range(storage_bar, 0, 100);
lv_bar_set_value(storage_bar, 65, LV_ANIM_ON);
lv_obj_align(storage_bar, LV_ALIGN_TOP_LEFT, 80, 175);
/* 确认按钮 */
lv_obj_t *confirm_btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(confirm_btn, 120, 40);
lv_obj_align(confirm_btn, LV_ALIGN_BOTTOM_MID, 0, -30);
lv_obj_t *btn_label = lv_label_create(confirm_btn);
lv_label_set_text(btn_label, "保存");
lv_obj_center(btn_label);
}
这个例子涵盖了本章所有控件。你把它跑起来,就能看到一个完整的设置界面。嗯,代码不长,但五脏俱全。